• 反射(一)类加载器、反射、动态代理


    本篇博客内容

    1、类加载器
    1.1、类的加载
    1.2、类初始化时机
    1.3、类加载器
    1.4、类加载器的作用

    2、反射
    2.1、反射机制
    2.2、获取Class对象(字节码文件对象)的三种方法
    2.3、通过反射获取构造方法并使用
    2.4、通过反射获取成员变量并使用
    2.5、通过反射获取成员方法并使用
    2.6、配置文件+反射
    2.7、通过反射越过泛型检查
    2.8、通过反射写一个通用方法:设置某个对象的某个属性为指定的值

    3、动态代理
    3.1、简单使用
    3.2、测试 jdk 动态代理--使用InvocationHandler 匿名内部类
    3.3、测试 jdk 动态代理--创建类实现 InvocationHandler 接口
    3.4、代理工厂实现 jdk 动态代理

    1、类加载器  <=返回目录

    1.1、类的加载   <=返回目录

      当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。

      加载:就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。

      连接:

        验证:是否有正确的内部结构,并和其他类协调一致。

        准备:负责为类的静态成员分配内存,并设置默认初始化值。

        解析:将类的二进制数据中的符号引用替换为直接引用。

      初始化:类的初始化。

    1.2、类初始化时机   <=返回目录

      (1)创建类的实例 Person p = new Person();

      (2)访问类的静态变量,或为类的静态变量赋值 Person.name = "zhangsan";

      (3)调用类的静态方法

      (4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

      (5)初始化某个类的子类

      (6)直接使用java.exe命令来运行某个主类(包含main方法的类)

    1.3、类加载器   <=返回目录

      负责将 xxx.class 文件加载到内存中,并为之生成对应的Class对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。

      类加载器的组成(分类):

        BootStrap ClassLoader 根类加载器

        Extension ClassLoader 扩展类加载器

        System ClassLoader 系统类加载器

      

    1.4、类加载器的作用   <=返回目录

      BootStrap ClassLoader 根类加载器:也被称为引导类加载器,负责java核心类的加载,比如System、String等,再jre的lib目录下rt.jar文件中。

      Extension ClassLoader 扩展类加载器:负责jre的扩展目录ext中jar包的加载。

      System ClassLoader 系统类加载器:负责再JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

      类加载器将 xxx.class 文件加载到内存中,并为之生成对应的Class对象。那么,Class对象有什么用呢?如何用呢?这就是反射要研究的内容。

    2、反射  <=返回目录

    2.1、反射机制   <=返回目录

      java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

      要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。

      

    2.2、获取Class对象(字节码文件对象)的三种方法   <=返回目录

    // 第一种 对象.getClass()
    Person p = new Person();
    Class c = p.getClass();
    
    // 第二种 类名.class
    Class c2 = Person.class;
    
    // 第三种 将类名作为字符串传递给Class类中的静态方法forName
    Class c3 = Class.forName("com.oy.Person");
    // 任意数据类型都具备一个class静态属性
    Class intType = int.class;
    System.out.println("int类型的名字:" + intType.getName()); // int类型的名字:int
    Class integerType = Integer.class;
    System.out.println("Integer类型的名字:" + integerType.getName()); // Integer类型的名字:java.lang.Integer

    2.3、通过反射获取构造方法并使用   <=返回目录

      Person类

    public class Person {
        public Integer id;
        private String name;
        private Integer age;
        
        private void show(String name) {
            System.out.println("name: " + name);
        }
        
        public void show(Integer age) {
            System.out.println("age: " + age);
        }
        
        public Person() {}
        public Person(String name) {
            this.name = name;
        }
        private Person(Integer age) {
            this.age = age;
        }
        public Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        
        // getter和setter方法省略
       
        @Override
        public String toString() {
            return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
        }
    }

      测试类

    package com.oy.test;
    import java.lang.reflect.Constructor;
    import com.oy.model.Person;
    
    public class Demo {
        
        public static void main(String[] args) throws Exception {
            Class clazz = Class.forName("com.oy.model.Person");
            
            // public Constructor[] getConstructors():获取所有公共构造方法
            // public Constructor[] getDeclaredConstructors():获取所有构造方法
            Constructor[] Constructors = clazz.getConstructors();
            for (Constructor con : Constructors) {
                System.out.println(con);
            }
            
            // 获取单个构造方法
            // public Constructor<T> getConstructor(Class<?>... parameterTypes)
            // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
            Constructor con1 = clazz.getConstructor();
            System.out.println(con1); // public com.oy.model.Person()
            Person p1 = (Person)con1.newInstance();
            //java.lang.IllegalArgumentException: wrong number of arguments
            // Person p1 = (Person)clazz.getConstructor(String.class);
            System.out.println(p1); // Person [name=null, age=null]
            
            Constructor con2 = clazz.getConstructor(String.class);
            System.out.println(con2); // public com.oy.model.Person(java.lang.String)
            Person p2 = (Person)con2.newInstance("张三");
            System.out.println(p2); // Person [name=张三, age=null]
            
            Constructor con3 = clazz.getConstructor(String.class, Integer.class);
            System.out.println(con3); // public com.oy.model.Person(java.lang.String,java.lang.Integer)
            Person p3 = (Person)con3.newInstance("张三", 10);
            System.out.println(p3); // Person [name=张三, age=10]
        }
    }

      通过反射获取私有构造方法并使用

    Class clazz = Class.forName("com.oy.model.Person");
    Constructor con = clazz.getDeclaredConstructor(Integer.class);
    // 没有下面这句,报错java.lang.IllegalAccessException
    con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查
    Person p = (Person) con.newInstance(20);
    System.out.println(p); // Person [name=null, age=20]

    2.4、通过反射获取成员变量并使用   <=返回目录

      为了测试,给Person类添加一个public字段

    public Integer id;

      通过反射获取成员变量并使用

    Class clazz = Class.forName("com.oy.model.Person");
    
    // Field[] fields = clazz.getFields(); // 获取所有公共的成员变量
    Field[] fields = clazz.getDeclaredFields(); // 获取所有的成员变量
    for (Field field : fields) {
        System.out.println(field);
    }
    
    // 通过无参构造方法创建对象
    Constructor con = clazz.getConstructor();
    Object obj = con.newInstance();
    
    // 获取单个的成员变量
    // 获取public字段: id, 并对其赋值
    Field idField = clazz.getField("id");
    idField.set(obj, 1);
    System.out.println(obj); // Person [id=1, name=null, age=null]
    
    // 获取private字段: name, 并对其赋值
    Field nameField = clazz.getDeclaredField("name");
    nameField.setAccessible(true);
    nameField.set(obj, "张三");
    System.out.println(obj); // Person [id=1, name=张三, age=null]

    2.5、通过反射获取成员方法并使用   <=返回目录

    Class clazz = Class.forName("com.oy.model.Person");
    
    // Method[] methods = clazz.getMethods(); // 获取自己的包括父亲的公共方法
    Method[] methods = clazz.getDeclaredMethods(); // 获取自己的所有的方法
    for (Method method : methods) {
        System.out.println(method);
    }
    
    // 通过无参构造方法创建对象
    Constructor con = clazz.getConstructor();
    Object obj = con.newInstance();
    
    // 获取单个方法并使用
    Method method1 = clazz.getMethod("show", Integer.class);
    method1.invoke(obj, 100);
    
    // 获取私有方法并使用
    Method method2 = clazz.getDeclaredMethod("show", String.class);
    method2.setAccessible(true);
    method2.invoke(obj, "张三");
    
    // 通过setXxx()方法给字段赋值
    String fieldName = "age";
    fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    Method method3 = clazz.getMethod("set" + fieldName, Integer.class);
    method3.invoke(obj, 200);
    System.out.println(obj); // Person [id=null, name=null, age=200]

    2.6、配置文件+反射   <=返回目录

    package com.oy.test;
    import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class Demo { public static void main(String[] args) throws Exception { // 加载键值对数据 Properties prop = new Properties(); FileReader fr = new FileReader("config.properties"); // 文件位于项目路径下 prop.load(fr); fr.close(); // 获取数据 String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); System.out.println("className: " + className + ", methodName: " + methodName); // 获取字节码文件对象 Class clazz = Class.forName(className); // 通过无参构造方法创建对象 // Constructor con = clazz.getConstructor(); // Object obj = con.newInstance(); Object obj = clazz.newInstance(); Method method = clazz.getMethod(methodName, Integer.class); method.invoke(obj, 300); } }

      

      配置文件 config.properties

    className=com.oy.model.Person
    methodName=show

    2.7、通过反射越过泛型检查   <=返回目录

    /*
     * 给你一个ArrayList<Integer>对象,在这个集合中添加一个字符串数据,如何实现呢?
     */
    public class ArrayListDemo {
        public static void main(String[] args) throws Exception {
            // 创建集合对象
            ArrayList<Integer> array = new ArrayList<Integer>();
    
            Class c = array.getClass(); // 集合ArrayList的class文件对象
            Method m = c.getMethod("add", Object.class);
    
            m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello
            m.invoke(array, "world");
            m.invoke(array, "java");
    
            System.out.println(array);
        }
    }

    2.8、通过反射写一个通用方法:设置某个对象的某个属性为指定的值   <=返回目录

    package com.oy.test;
    import java.lang.reflect.Field;
    
    public class Utils {
        // 给obj对象的propertyName属性赋值为value
        public static void setProperty(Object obj, String propertyName, Object value) throws Exception {
            // 根据对象获取字节码文件对象
            Class<?> clazz = obj.getClass();
            // 获取该对象的propertyName成员变量
            Field field = clazz.getDeclaredField(propertyName);
            // 取消访问检查
            field.setAccessible(true);
            // 给对象的成员变量赋值为指定的值
            field.set(obj, value);
        }
    }

      测试类:

    package com.oy.test;
    import com.oy.model.Person;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            Person p = new Person();
            Utils.setProperty(p, "id", 1);
            System.out.println(p); // Person [id=1, name=null, age=null]
    
            Utils.setProperty(p, "name", "张三");
            System.out.println(p); // Person [id=1, name=张三, age=null]
    
            Utils.setProperty(p, "age", 10);
            System.out.println(p); // Person [id=1, name=张三, age=10]
        }
    }

    3、动态代理   <=返回目录

    3.1、简单使用   <=返回目录

      接口

    public interface UserDao {
        void getUserById(Integer id);
    }

      实现类

    public class UserDaoImpl implements UserDao {
    
        @Override
        public void getUserById(Integer id) {
            System.out.println("查询user...");
        }
    
    }

      测试jdk动态代理

    package com.oy.test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class Test {
    
        public static void main(String[] args) {
            // 创建被代理对象
            final UserDao userDao = new UserDaoImpl();
    
            UserDao proxy = (UserDao) Proxy.newProxyInstance(
                    userDao.getClass().getClassLoader(), 
                    userDao.getClass().getInterfaces(), 
                    new InvocationHandler() {
    
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("before...");
                            Object result = method.invoke(userDao, args);
                            System.out.println("after...");
                            return result;
                        }
    
                    }
            );
    
            // 代理对象调用方法
            proxy.getUserById(1);
        }
    }

      打印结果

    before...
    查询user...
    after...

    3.2、测试 jdk 动态代理--使用InvocationHandler 匿名内部类   <=返回目录

      InvocationHandler 匿名内部类:

      UserDao:

    public interface UserDao {
        void add();
        void delete();
        void update();
        void find();
    }

      UserDaoImpl:

    public class UserDaoImpl implements UserDao {
    
        @Override
        public void add() {
            System.out.println("添加。。。");
        }
    
        @Override
        public void delete() {
            System.out.println("删除。。。");
        }
    
        @Override
        public void update() {
            System.out.println("修改。。。");
        }
    
        @Override
        public void find() {
            System.out.println("查询。。。");
        }
    
    }

      TestProxy:

    package com.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 测试 jdk 动态代理
     */
    public class TestProxy {
    
        public static void main(String[] args) {
            final UserDao userDao = new UserDaoImpl();
    
            UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(
                    userDao.getClass().getClassLoader(),
                    userDao.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // System.out.println(proxy.getClass().getName()); // com.sun.proxy.$Proxy0
                            System.out.println("权限校验");
                            Object result = method.invoke(userDao, args);
                            System.out.println("日志记录");
                            return result;
                        }
                    });
    
            // 动态代理对象的类型:com.sun.proxy.$Proxy0
            // System.out.println("动态代理对象的类型:" + proxyUserDao.getClass().getName());
            proxyUserDao.add();
            proxyUserDao.find();
        }
    
    }

    3.3、测试 jdk 动态代理--创建类实现 InvocationHandler 接口   <=返回目录

      创建类实现 InvocationHandler 接口

       UserDaoProxyHandler:

    package com.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class UserDaoProxyHandler implements InvocationHandler {
    
        private UserDao userDao;
    
        public UserDaoProxyHandler(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // System.out.println(proxy.getClass().getName()); // com.sun.proxy.$Proxy0
            System.out.println("权限校验");
            Object result = method.invoke(userDao, args);
            System.out.println("日志记录");
            return result;
        }
    
    }

      TestProxy:

    package com.proxy;
    
    import java.lang.reflect.Proxy;
    
    /**
     * 测试 jdk 动态代理
     */
    public class TestProxy {
    
        public static void main(String[] args) {
            UserDao userDao = new UserDaoImpl();
    
            UserDao proxyUserDao = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
                    userDao.getClass().getInterfaces(), new UserDaoProxyHandler(userDao));
    
            // 动态代理对象的类型:com.sun.proxy.$Proxy0
            // System.out.println("动态代理对象的类型:" + proxyUserDao.getClass().getName());
            proxyUserDao.add();
            proxyUserDao.find();
        }
    
    }

    3.4、代理工厂实现 jdk 动态代理   <=返回目录

        UserDao

    public interface UserDao {
        void getUserById(Integer id);
    }
    View Code

      UserDaoImpl

    public class UserDaoImpl implements UserDao {
        @Override
        public void getUserById(Integer id) {
            System.out.println("查询user...");
        }
    }
    View Code

      BeforeAdvice

    /**
     * 前置通知
     * @author oy
     * @version 1.0
     * @date 2020年4月7日
     * @time 上午11:36:59
     */
    public interface BeforeAdvice {
        void before();
    }
    View Code

      AfterAdvice

    /**
     * 后置通知
     * @author oy
     * @version 1.0
     * @date 2020年4月7日
     * @time 上午11:36:45
     */
    public interface AfterAdvice {
        void after();
    }
    View Code

      ProxyFactory

    package com.oy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyFactory<T> {
        private T target;
        private BeforeAdvice beforeAdvice;
        private AfterAdvice afterAdvice;
    
        @SuppressWarnings("unchecked")
        public T createProxy() {
            if (target == null) return null;
    
            T proxy = (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                    new InvocationHandler() {
    
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // 执行前置增强
                            if (beforeAdvice != null) beforeAdvice.before();
    
                            // 执行目标对象的目标方法
                            Object result = method.invoke(target, args);
    
                            // 执行后置增强
                            if (afterAdvice != null) afterAdvice.after();
    
                            return result;
                        }
    
                    });
    
            return proxy;
        }
    
        public void setTarget(T target) {
            this.target = target;
        }
        public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
            this.beforeAdvice = beforeAdvice;
        }
        public void setAfterAdvice(AfterAdvice afterAdvice) {
            this.afterAdvice = afterAdvice;
        }
    }
    View Code

      TestProxy

    package com.oy;
    
    public class TestProxy {
    
        public static void main(String[] args) {
    
            // 创建被代理对象
            UserDao target = new UserDaoImpl();
    
            // 创建代理工厂
            ProxyFactory<UserDao> factory = new ProxyFactory<>();
            factory.setTarget(target);
    
            factory.setBeforeAdvice(new BeforeAdvice() {
                @Override
                public void before() {
                    System.out.println("ProxyFactory before...");
                }
            });
    
            factory.setAfterAdvice(new AfterAdvice() {
                @Override
                public void after() {
                    System.out.println("ProxyFactory after...");
                }
            });
    
            // 创建代理对象
            UserDao userDapProxy = factory.createProxy();
            userDapProxy.getUserById(1);
    
        }
    }
    View Code

    ---

  • 相关阅读:
    [原创]在使用SDK 23(6.0)版本后org.apache.http相关的类找不到的解决办法
    [原创] Gradle DSL method not found: 'android()' 和 buildToolsVersion is not specified 的解决办法。
    [原创]Android Lollipop (5.0) 原生代码 Settings 首页加载逻辑分析
    访问https接口报错 基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系
    js for循环中延迟 setTimeout
    ABP框架扩展AbpSession
    web在线查看PDF文件
    web在线预览office文件
    sql server游标读取excel文件数据,更新到指定表中
    echarts图形销毁重新绘制
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/10908708.html
Copyright © 2020-2023  润新知