木心

毕竟几人真得鹿,不知终日梦为鱼

导航

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

本篇博客内容

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

---

posted on 2019-05-22 21:40  wenbin_ouyang  阅读(295)  评论(0编辑  收藏  举报