Java19:反射

一:反射的理解

Reflection(反射)是指被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息,并直接操作任意对象的内部属性及方法。

框架=反射+注解+设计模式

二:体会反射机制的“动态性”

// 创建一个指定类的对象,classpath:指定类的全类名
public  Object getInstance(String classpath) throws Exception {

    Class clazz = Class.forName(classpath);

    return clazz.newInstance();
}

@Test
public void test2()  {
    Random random = new Random();
    String classpath="";
    for (int i = 0; i < 100; i++) {
        int number = random.nextInt(3);
        switch (number) {
            case 0:
                classpath = "java.util.Date";
                break;

            case 1:
                classpath = "java.lang.Object";
                break;
            case 2:

                classpath = "com.cn.test.Person";
                break;

        }

        try {
      Object obj=getInstance(classpath);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

三:反射机制能提供的功能

》在运行时判断任意一个对象所属的类

》在运行时构造任意一个类的对象

》在运行时判断任意一个类所具有的成员变量和方法

》在运行时获取泛型信息

》在运行时调用任意一个对象的成员变量和方法

》在运行时处理注解

》生成动态代理

四:相关API

java.lang.Class  :反射的源头

java.lang.reflect.Method

java.lang.reflect.Field

java.lang.reflect.Constructor

五:关于Class类的理解

1.类的加载过程:

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。

接着我们使用java.exe 命令 对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中,在此过程就称为类的加载。加载到内存中的类,我们称为运行时类,此运行时类,就作为Class 的一个实例。

2.换句话说,Class的实例 对应着 一个运行时类。

3.加载到内存中的运行时类会缓存一定的时间,在此时间之内,我们可以通过不同方式来获取此运行时类。

4.获取Class实例的几种方式:

@Test
public void test3() throws ClassNotFoundException {
    //1.调用类.class 属性,获取Class实例
    Class class1= Person.class;
    System.out.println(class1);
    Person p =new Person();
    //2.调用getClass(),获取Class实例
    Class class2= p.getClass();
    System.out.println(class2);
    //3.Class.forName,获取Class实例
    Class class3 = Class.forName("com.cn.test.Person");
    System.out.println(class3);
    //4.调用类的加载器getClassLoader(),获取Class实例
    ClassLoader classLoader = Reflection1Test.class.getClassLoader();
    Class class4= classLoader.loadClass("com.cn.test.Person");
    System.out.println(class4);
    //System.out.println(class1==class2);

5. 总结:创建类的对象的方式

方式一: new + 构造器

方式二:要创建Xxx类的对象,可以考虑:Xxx/Xxxs 类是否有静态方法存在,可以调用其静态方法,创建Xxx对象。

方式三:通过反射

6.Class实例可以是哪些结构的说明

1)class:外部类,成员(成员内部类,静态内部类)局部内部类,匿名内部类

2)interface:接口

3) []:数组

4)enum:枚举

5)annotation:注解@interface

6)primitive type:基本数据类型

7)void

7.类的加载器,加载配置文件

@Test
public void test3() throws IOException {
    //方式一
    Properties pro =new Properties();
    FileInputStream fis =new FileInputStream("jdbc.propertities");
    pro.load(fis);
    String name = pro.getProperty("name");
    String age = pro.getProperty("age");
    System.out.println(name+":"+age);


}
@Test
public void test4() throws IOException {
  //方式二,该方式文件放在src目录下
    ClassLoader classLoader = Reflection2Test.class.getClassLoader();
    InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc2.propertities");
    Properties pro =new Properties();
    pro.load(resourceAsStream);
    String name = pro.getProperty("name");
    String age = pro.getProperty("age");
    System.out.println(name+":"+age);

}

六:反射的实例

1.获取所有的属性,方法,构造器

@Test
public void test2() {

    Class clazz = Person.class;
    //获取运行时类及其所有父类的public方法
    Method[] methods = clazz.getMethods();
    for (Method m : methods) {
        System.out.print(m + "\t");
        System.out.println(m.getName());
    }

    System.out.println();
    //获取运行时类所有方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method m : declaredMethods) {
        System.out.print(m + "\t");
        System.out.println(m.getName());
    }


}

@Test
public void test3() {

    Class clazz = Person.class;
    //获取运行时类所有public的构造器
    Constructor[] constructors = clazz.getConstructors();
    for(Constructor c:constructors){
        System.out.println(c);
    }
    System.out.println();
    //获取运行时类所有构造器
    Constructor[] constructors2 = clazz.getDeclaredConstructors();
    for(Constructor c:constructors2){
        System.out.println(c);
    }

}
@Test
public void test4() throws Exception {
    //获取当前运行时类父类的对象
    Class superclass = Person.class.getSuperclass();
    System.out.println(superclass);

}

2.获取指定的属性,方法,构造器

@Test
public  void filedTest() throws Exception {
    Class clazz=Person.class;
    Object obj = clazz.newInstance();
     Person per= (Person) obj;
     //获取指定属性
    Field name = clazz.getDeclaredField("name");
    //给指定对象属性复制
    name.set(per,"tom");
    System.out.println(name.get(per));
    //给静态属性赋值,并获取
    Field hobbies = clazz.getDeclaredField("hobbies");
    hobbies.set(clazz,"baskball");
    System.out.println(hobbies.get(clazz));
}


@Test
public  void methodTest() throws Exception {

    Class clazz=Person.class;
    Object obj = clazz.newInstance();
    Person per= (Person) obj;
    //获取public 无参方法 并调用
    Method info = clazz.getDeclaredMethod("info");
    info.invoke(per);
    //获取public 有参方法 并调用
    Method getNation = clazz.getDeclaredMethod("getNation",String.class);
    getNation.invoke(per,"china");
    //获取静态方法并调用
    Method study = clazz.getDeclaredMethod("study");
    study.invoke(clazz);
    //获取私有方法并调用
    Method getAge = clazz.getDeclaredMethod("getAge", int.class);
    //设置为可访问
    getAge.setAccessible(true);
    System.out.println(getAge.invoke(per, 20));


}

@Test
public  void ConstructTest() throws Exception {
   Class clazz= Person.class;
   //该方法为常用用法,具有通用性
    Object o = clazz.newInstance();
    System.out.println(o);
    //获取带有参数的构造器,不常用
    Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class);
    constructor.setAccessible(true);
    Object jerry = constructor.newInstance(15, "jerry");
    System.out.println(jerry);


}



七:反射的应用:动态代理

1.静态代理:

interface ColthFactory{
   void  produceColth();
}
//代理类
class ProxyColthFactory implements ColthFactory{
    private ColthFactory factory;

    public ProxyColthFactory(ColthFactory factory){
        this.factory=factory;
    }

    @Override
    public void produceColth() {
        System.out.println("生产衣服之前的准备工作");
        factory.produceColth();
        System.out.println("生产衣服之后的收尾工作");

    }
}
//被代理类
class NIkeColthFactory implements  ColthFactory{

    @Override
    public void produceColth() {
        System.out.println("NIKE生产衣服");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类对象
        NIkeColthFactory nike =new NIkeColthFactory();
        //创建代理类对象
        ProxyColthFactory proxy =new ProxyColthFactory(nike);
        //调用方法
        proxy.produceColth();
    }
}

2.动态代理:

class ProxyFactory{

    public static Object getInstance(Object obj){
        MyInvocationHandler hander= new MyInvocationHandler();
        hander.blind(obj);
        return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), hander);

    }
}

class MyInvocationHandler implements InvocationHandler{
    private Object obj; //赋值时必须传被代理类实例
    public void blind(Object obj){
        this.obj=obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = method.invoke(obj, args);// method为代理类调用的方法,此方法也作为被代理类对象要调用的方法
        return invoke;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        NIkeColthFactory nike = new NIkeColthFactory();
        ColthFactory  colthFactory= (ColthFactory) ProxyFactory.getInstance(nike);
        colthFactory.produceColth();

    }
}
<think>嗯,用户想了解Java Spring框架中反射的使用方法和常见问题。首先,我需要回忆一下Spring框架中哪些地方用到了反射机制。比如,依赖注入、动态代理、AOP这些功能都可能依赖反射。用户提供的引用里提到Spring通过反射自动扫描和装配Bean,还有Field和Method类的使用,这些应该是关键点。 接下来,我应该分步骤解释反射在Spring中的应用。比如,Bean的实例化过程,Spring如何通过反射调用构造方法创建对象。然后,依赖注入时如何通过反射设置私有字段或调用setter方法。动态代理部分,可能需要提到JDK动态代理利用反射生成代理类。AOP的切面逻辑也是通过反射来调用目标方法的。 关于使用方法,需要结合具体的例子,比如XML配置和注解配置下Spring如何处理。比如引用3中的XML配置,Spring会解析XML文件,通过反射创建Bean实例。注解方式如@Autowired,Spring会查找对应的Bean并注入。 常见问题部分,用户可能遇到性能问题,因为反射比直接调用慢。权限问题,比如访问私有成员时需要setAccessible(true),但可能存在安全限制。还有兼容性问题,比如反射Java版本升级后的变化,或者混淆代码导致的反射失败。此外,循环依赖的问题,Spring虽然有解决机制,但在某些情况下仍可能导致异常。 需要确保提到的每个点都有引用支持,比如引用2提到反射的通用性和框架中的应用,引用4提到Field和Method类的使用。同时,注意用户提供的引用中有关于XML配置和依赖注入的内容,需要整合进去。 可能还需要考虑用户的实际场景,比如开发者在使用Spring时遇到反射相关的问题,如何排查和解决。比如性能优化部分,可以建议减少反射调用,使用缓存。安全方面,提醒正确处理Accessible权限。 最后,生成的相关问题要围绕Spring和反射的深入应用,比如原理、优化、安全问题、异常处理等,帮助用户进一步学习。</think>### Java Spring框架中反射的使用方法与常见问题 #### 一、反射在Spring框架中的核心作用 1. **Bean的实例化与依赖注入** Spring通过反射动态加载类并调用构造方法创建对象。例如,XML配置中定义的`<bean class="com.example.UserService">`会被解析为`Class.forName("com.example.UserService").newInstance()`[^3]。依赖注入时,Spring通过反射访问私有字段或调用setter方法,例如`Field.set(obj, value)`或`Method.invoke()`[^4]。 2. **动态代理与AOP** Spring AOP使用JDK动态代理或CGLIB生成代理类,其中JDK代理基于`Proxy.newProxyInstance()`和反射机制调用目标方法[^2]。 3. **注解解析与组件扫描** 如`@Autowired`、`@Component`等注解的处理依赖反射。Spring通过`Class.getAnnotations()`扫描类路径,识别并注册Bean[^3]。 #### 二、具体使用方法 1. **基于XML配置的反射调用** ```xml <!-- 配置Bean --> <bean id="userDao" class="com.example.UserDaoImpl"/> <bean id="userService" class="com.example.UserService"> <property name="userDao" ref="userDao"/> </bean> ``` Spring解析XML时,通过反射调用`UserService`的`setUserDao`方法完成依赖注入[^3]。 2. **注解驱动的反射应用** ```java @Service public class OrderService { @Autowired private PaymentService paymentService; // 反射注入私有字段 } ``` Spring容器启动时,通过反射遍历字段并注入实例。 3. **方法级反射调用示例** ```java Method method = MyService.class.getDeclaredMethod("calculate", int.class, int.class); Object result = method.invoke(beanInstance, 10, 20); // 调用带参数的方法 ``` 这种机制常用于框架内部的事件处理或动态路由。 #### 三、常见问题与解决方案 1. **性能问题** - **现象**:频繁反射调用导致启动速度慢或运行时延迟。 - **优化**: - 使用缓存(如Spring的`ReflectionUtils`) - 优先采用CGLIB字节码增强代替JDK动态代理 2. **权限限制** - **案例**:注入私有字段时报`IllegalAccessException` - **解决**: ```java Field field = target.getClass().getDeclaredField("secretKey"); field.setAccessible(true); // 突破私有权限限制 field.set(target, value); ``` 3. **兼容性问题** - **场景**:Java模块化系统(JPMS)限制反射访问 - **处理**:在`module-info.java`中添加`opens`语句开放包访问权限。 4. **循环依赖陷阱** - **表现**:Bean A依赖Bean B,Bean B又依赖Bean A,导致`BeanCurrentlyInCreationException` - **方案**: - 使用`@Lazy`延迟加载 - 重构代码解耦依赖 #### 四、最佳实践 1. **优先使用Spring工具类** 如`BeanUtils.copyProperties()`替代手动反射操作: ```java BeanUtils.copyProperties(source, target); // 安全高效的属性拷贝 ``` 2. **谨慎使用`MethodHandle`** 对于高频调用的方法,可考虑Java 7+的`MethodHandle`提升性能: ```java MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(MyClass.class, "method", MethodType.methodType(void.class)); handle.invokeExact(instance); ``` 3. **结合泛型类型检查** 反射获取泛型类型信息: ```java Type genericType = field.getGenericType(); // 获取带泛型的类型 if (genericType instanceof ParameterizedType) { Type actualType = ((ParameterizedType)genericType).getActualTypeArguments()[0]; } ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值