美文网首页Java
关于Java反射

关于Java反射

作者: 大黑跟小白的日常 | 来源:发表于2020-04-07 22:56 被阅读0次

    关于Class

    类加载的几种方式

    new Xxx()

    public class OrderService {
        public static List<String> names;
    }
    
    public class UserService {
        static {
            System.out.println("UserService 静态代码块被执行");
            OrderService.names = new ArrayList<>();
            OrderService.names.add("GY");
            OrderService.names.add("TLX");
        }
    }
    
    public class TestClient {
        public static void main(String[] args) {
            // 采用new 创建对象的方式 加载 UserService类
            UserService userService = new UserService();
            // 静态代码块被执行,OrderService的静态属性names被初始化
            System.out.println(OrderService.names);
        }
    }
    
    image-20200406214926926.png

    classLoader.loadClass(String className)

    public class TestClient {
        public static void main(String[] args) throws ClassNotFoundException {
            ClassLoader classLoader = TestClient.class.getClassLoader();
            // 这里不会去解析(链接)这个类,只是加载Class到JVM,也就是说不会去初始化类的静态资源(静态代码块)
            classLoader.loadClass("cls.loadclasstest.UserService");
            // 看具体打印结果,UserService类的 static {} 并没有执行 
            System.out.println(OrderService.names);
        }
    }
    
    image-20200406215757573.png
    image-20200406215937524.png

    Class.forName(String className)

    public class TestClient {
        public static void main(String[] args) throws ClassNotFoundException {
            // 默认会去初始化类,初始化类的静态资源,执行类的 static {}
            Class<?> cls1 = Class.forName("cls.loadclasstest.UserService");
            // 使用如下方式,则不会初始化类的静态资源
    //        Class.forName("cls.loadclasstest.UserService", false, TestClient.class.getClassLoader());
            System.out.println(OrderService.names);
        }
    }
    
    image-20200406220612748.png
    image-20200406220533146.png

    具体使用

    Spring框架中的IOC,根据XML中配置的目标类地址,加载得到对应的Class;

    根据包扫描路径,扫描到class文件,加载各个Class到程序。最终通过Class生成对应实例Bean交给IOC管理;

    得到Class cls对象之后

    public interface IUserService {}
    public class AbstractUserService implements IUserService {}
    public class UserService extends AbstractUserService {}
    

    判断Class是不是接口

    isInterface

    public class ClientTest {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> c1 = Class.forName("cls.clstest.IUserService");
            Class<?> c2 = Class.forName("cls.clstest.AbstractUserService");
            Class<?> c3 = Class.forName("cls.clstest.UserService");
            System.out.println(c1.isInterface());// true
            System.out.println(c2.isInterface());// false
            System.out.println(c3.isInterface());// false
        }
    }
    

    获取Class实现的接口Class[]

    getInterfaces

    Class<?>[] interfaces1 = c2.getInterfaces();
    // 只可以获取到该类直接实现的接口,无法获取到父类实现的接口
    Class<?>[] interfaces2 = c3.getInterfaces();
    
    image-20200406225756049.png
    image-20200406225838176.png

    判断Class是不是被某个注解修饰了

    isAnnotationPresent

    // 定义一个注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Controller {
        String name();
        String[] values();
    }
    
    @Controller(name="userService", values = {"abc","edf"})
    public class UserService {}
    
    public class ClientTest {
        public static void main(String[] args) throws ClassNotFoundException {
            Class<?> c3 = Class.forName("cls.clstest.UserService");
            // 判断某个Class对象上是否加了某个注解
            boolean annotationPresent = c3.isAnnotationPresent(Controller.class);
            System.out.println(annotationPresent); // true
            Class<?> c2 = Class.forName("cls.clstest.AbstractUserService");
            annotationPresent = c2.isAnnotationPresent(Controller.class);
            System.out.println(annotationPresent); // false
        }
    }
    

    获取被修饰注解中的属性

    getAnnotation

            Class<?> c3 = Class.forName("cls.clstest.UserService");
            // 获取 UserService类上的Controller注解对象
            Controller annotation = c3.getAnnotation(Controller.class);
            // 从注解对象中获取属性
            String name = annotation.name();// userService
            String[] values = annotation.values();// ["abc","edf"]
            System.out.println(name);
            for (String value : values) {
                System.out.println(value);
            }
    
    image-20200407103109178.png

    具体应用

    基于指定基础包扫描路径、注解模式实现的Spring IOC,底层实则就用到了根据Class对象,获取其上修饰的注解(对象),判断该类的实例是否是需要IOC进行管理的。并且可以根据Class之上的注解(对象),获取注解中的属性值,以用于实现如何构建当前Class的Bean(比如指定其在IOC中的Bean的名称、是否为单例、或用于Bean对象的一些成员属性的初始化赋值以用于后续业务操作)。

    获取构造

    Constructor也是一种类型,具体构造函数也是对象

    public class UserService {
        private String name;
        private Integer age;
        public UserService(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        public UserService(String name) {
            this.name = name;
        }
        public UserService() {
        }
    }
    
    public class ClientTest {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
            Class<?> c3 = Class.forName("cls.clstest.UserService");
            // 根据参数类型获取构造函数
            Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
            // 使用构造函数创建对象
            UserService userService = (UserService) constructor1.newInstance("hello", 18);
    
            // 获取所有构造函数
            Constructor<?>[] constructors = c3.getConstructors();
            // 遍历构造函数
            for (Constructor<?> constructor : constructors) {
                // 获取该构造函数的 参数类型集
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                System.out.println("------------");
                for (Class<?> parameterType : parameterTypes) {
                    // 打印参数类型
                    System.out.println(parameterType.getName());
                }
            }
        }
    }
    
    image-20200407113939445.png

    具体应用体现

    Spring IOC初始化过程中通过构造方法注入属性,生成Bean

    <bean id="******" class="***.***.***">
        <constructor-arg type="java.lang.String" value="***" />
    </bean>
    <!--或者-->
    <bean id="******" class="***.***.***">
        <!--这里实则是根据Class中匹配构造函数的参数的名称获取对应构造方法-->
        <!-- 具体参见 : https://blog.csdn.net/qq_31803503/article/details/80990821 -->
        <constructor-arg name="**" value="***" />
        <constructor-arg name="***" value="****"/>
    </bean>
    

    附加内容如下:

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 根据参数类型获取构造函数
    Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
    // 获取构造函数参数对象Parameter
    Parameter[] parameters = constructor1.getParameters();
    for (Parameter parameter : parameters) {
        // 打印参数名称
        System.out.println(parameter.getName()); // 当前这种获取参数的方式,打印的参数名称跟实际定义的名称不服
    }
    
    image-20200407122505454.png

    如上这中方式获取的构造函数参数的名称,没有具体意义,并不是具体的name、age, Spring使用ASM字节码操作框架来获取方法参数的名称 。

    具体参见:https://blog.csdn.net/qq_31803503/article/details/80990821

    创建对象

    用Constructor来创建对象

    // 比如通过获取无参构造来构建对象,要求目标Class得有可访问的无参构造
    Class<?> c3 = Class.forName("cls.clstest.UserService");
    Constructor<?> constructor2 = c3.getConstructor();
    UserService o = (UserService) constructor2.newInstance();
    

    如果构造不可访问,如:

    public class UserService {
        // 私有无参构造外部不可访问
        private UserService() {}
        private String name;
        private Integer age;
        public UserService(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
    }
    

    则需要暴力靠近

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 获取声明过的构造(不管是否可访问)
    Constructor<?> constructor2 = c3.getDeclaredConstructor();
    // 暴力靠近
    constructor2.setAccessible(true);
    // 通过当前Constructor构建对象
    UserService o = (UserService) constructor2.newInstance();
    

    如果获取的是带参构造,则需按类型进行传参构建对象

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 获取可访问的构造函数对象
    Constructor<?> constructor1 = c3.getConstructor(String.class, Integer.class);
    // 按照参数类型对应传参构建实例
    UserService userService = (UserService) constructor1.newInstance("hello", 18);
    

    cls.newInstance构建

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 这里底层实则也是调用的无参数构造,要求目标类Class提供可访问的无参构造
    UserService o1 = (UserService) c3.newInstance();
    

    应用体现

    Spring IOC 中的各个Service、Controller、Component、Configuration...对象,都是通过反射执行构造生成的(除去代理对象);

    Method方法

    // 目标类
    public class UserService {
        // 私有方法
        private String f1(String s) {
            System.out.println("private f1");
            return s;
        }
        // 静态方法
        public static Integer f2(Integer i1, Integer i2) {
            System.out.println("static f2");
            return i1 + i2;
        }
        // 无参方法
        public String f3() {
            System.out.println("public f3 no args");
            return "f3";
        }
        // 带参方法
        public String f3(String name, Integer age) {
            System.out.println("public f3 has args");
            return "f3 " + name + " " + age;
        }
    }
    

    获取Class中Method对象

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 获取已声明的方法(包含私有不可访问的方法),按名称获取方法
    Method f3_1 = c3.getDeclaredMethod("f3");// 获取无参方法f3
    Method f3_2 = c3.getDeclaredMethod("f3", String.class, Integer.class);// 获取带参方法
    // 获取所有方法
    Method[] methods = c3.getDeclaredMethods();
    for (Method method : methods) {
        // 方法名称
        System.out.println(method.getName());
        // 方法所在类
        System.out.println(method.getDeclaringClass().getName());// 即 cls.clstest.UserService
        // 方法返回类型
        System.out.println(method.getReturnType().getName());
        // 方法参数类型集合
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            // 打印方法参数类型名称
            System.out.println(parameterType.getName());
        }
        System.out.println("----------------------");
    }
    
    image-20200407203739503.png

    执行Method

    执行私有普通方法

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 构建执行方法的对象
    UserService userService = new UserService();
    // 获取私有方法Method对象需要使用getDeclaredMethod
    Method f1 = c3.getDeclaredMethod("f1", String.class);
    // 执行私有方法前,需要暴力靠近
    f1.setAccessible(true);
    // 执行
    f1.invoke(userService,"haha");
    

    执行静态方法

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    Method f2 = c3.getMethod("f2", Integer.class, Integer.class);
    // 静态方法使用Class对象去执行
    Object invoke = f2.invoke(c3, 1, 1);
    System.out.println(invoke);
    
    image-20200407205401240.png

    执行普通无参方法

    Class<?> c3 = Class.forName("cls.clstest.UserService");
    // 如果需要获取带参方法,则需要将参数类型一并传入
    Method f3 = c3.getMethod("f3");
    UserService userService = new UserService();
    Object invoke = f3.invoke(userService);
    System.out.println(invoke);
    

    获取Method上面的注解

    // 自定义注解
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RequestMapping {
        String[] value() default {};
    }
    
    @Controller(name = "userController", values = {"aaa","bbb"})
    public class UserController {
        @Autowired
        private UserService userService;
        // 在方法上加上注解
        @RequestMapping("/test")
        public String f() {
            String s = userService.f3();
            return s;
        }
    }
    
    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    // 根据方法名称获取Method对象,这里该方法没有参数
    Method f = controllerClass.getMethod("f");
    // 判断Method上是否包含某个注解
    boolean annotationPresent = f.isAnnotationPresent(RequestMapping.class);
    System.out.println(annotationPresent);// true
    if(annotationPresent) {
        // 获取注解对象
        RequestMapping annotation = f.getAnnotation(RequestMapping.class);
        // 获取注解对象中的参数
        String[] value = annotation.value();
        for (String s : value) {
            System.out.println(s); // /test
        }
    }
    
    image-20200407213447967.png

    应用体现

    • 在RPC框架中,服务提供者在接收到请求数据后,从协议数据中获取方法名称、参数类型、以及客户端请求参数、及注册中心的具体服务Bean的信息,从服务提供端容器中获取最终执该方法的对象,然后用反射进行执行,获取执行结果,响应服务调用者;
    • 在Spring MVC中通过获取具体Method对象上的注解,解析注解对象中的属性值,最终生成映射关系,在具体客户端请求时,通过url及一些信息最终定位到具体用于处理该请求业务的Method方法,然后从IOC中获取到该Controller对象,然后利用反射进行执行,获取业务请求结果,响应客户端;

    Field属性

    获取Field对象

    public class UserController {
        @Autowired
        private UserService userService;
    }
    
    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    // 根据属性名称,获取已声明属性(可以获取私有属性)
    // controllerClass.getField("userService"); // 这样则无法获取私有属性
    Field userServiceField = controllerClass.getDeclaredField("userService");
    // 获取所有属性--数组
    Field[] declaredFields = controllerClass.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        // 属性名称
        System.out.println(declaredField.getName());
        // 属性类型
        System.out.println(declaredField.getType());
    }
    
    image-20200407215600842.png

    执行Field的对应Get、Set方法

    执行属性Get方法

    类中不需要提供该属性对应的get、set方法

    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    Field userServiceF = controllerClass.getDeclaredField("userService");
    // 暴力靠近,让属性的get、set方法可以执行
    userServiceF.setAccessible(true);
    // 构建目标对象
    UserController userController = new UserController();
    // 反射执行Field的get方法
    UserService userService1 = (UserService) userServiceF.get(userController);
    // 执行目标对象的userService这个成员属性的get方法
    System.out.println(userService1);// 这里因为没有设置对象的属性,所以为null
    
    image.png

    执行属性Set方法

    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    UserController userController = new UserController();
    // 按属性名称获取属性Field对象
    Field userServiceF = controllerClass.getDeclaredField("userService");
    userServiceF.setAccessible(true);
    // 反射执行Filed的set方法
    userServiceF.set(userController,new UserService());
    // 反射执行Field的get方法
    UserService userService1 = (UserService) userServiceF.get(userController);
    System.out.println(userService1);// 打印对象中的属性
    
    image.png

    获取Field上面的注解

    // 自定义注解
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    }
    
    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    Field userServiceF = controllerClass.getDeclaredField("userService");
    // 判断Field上是否被某个注解修饰
    boolean b = userServiceF.isAnnotationPresent(Autowired.class);
    System.out.println(b);// true
    if(b) {
        Autowired annotation = userServiceF.getAnnotation(Autowired.class);
    }
    

    拓展内容:

    Class<?> controllerClass = Class.forName("cls.clstest.UserController");
    Field userServiceF = controllerClass.getDeclaredField("userService");
    // 判断Field上是否有@Autowired
    boolean b = userServiceF.isAnnotationPresent(Autowired.class);
    System.out.println(b);
    if(b) {
        // 如果当前字段被@Autowired修饰,Spring IOC 初始化——动态注入环节,则会考虑针对当前字段做自动注入操作
        // 获取字段的类型
        Class<?> type = userServiceF.getType();
        // 从IOC已经构建的Bean容器中根据类型type获取Bean
        // 这里我自己new一个,用于测试
        UserService userService = new UserService();
        // 同样需要从IOC容器中根据当前Class——controllerClass,获取到当前Controller对象,我同样new一个用于测试
        UserController userController = new UserController();
        // 获取到Bean之后,接下来执行Field的set方法,实现自动注入
        userServiceF.setAccessible(true);
        // 动态注入
        userServiceF.set(userController, userService);
    }
    

    应用体现

    Spring IOC加载过程中,会用到反射操作Field,实现Bean之间的动态注入等功能;

    相关文章

      网友评论

        本文标题:关于Java反射

        本文链接:https://www.haomeiwen.com/subject/licaphtx.html