目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Reflection

Reflection

java的反射(reflection)机制是指在程序的运行状态中

  • 可以构造任意一个类的对象
  • 可以了解任意一个类的成员变量和方法
  • 可以了解任意一个对象所属的类
  • 可以调用任意一个对象的属性和方法

这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制

反射核心得到编译以后得class文件对象,提供了一个Class类型,就是编译后的class类对象

image.png

Base 类

 1package com.soulboy.reflection;
 2
 3public class Base {
 4    private String job;
 5
 6    public String getJob() {
 7        return job;
 8    }
 9
10    public void setJob(String job) {
11        this.job = job;
12    }
13}

User 类

 1package com.soulboy.reflection;
 2
 3public class User extends Base {
 4    private int age;
 5
 6    public String name;
 7
 8    public User() {
 9
10    }
11
12    public User(int age) {
13        this.age = age;
14    }
15
16    private User(String name) {
17        this.name = name;
18    }
19
20    public User(int age, String name) {
21        this.age = age;
22        this.name = name;
23    }
24
25    public int getAge() {
26        return age;
27    }
28
29    public void setAge(int age) {
30        this.age = age;
31    }
32
33    public String getName() {
34        return name;
35    }
36
37    public void setName(String name) {
38        this.name = name;
39    }
40
41    @Override
42    public String toString() {
43        return "User{" +
44                "age=" + age +
45                ", name='" + name + '\'' +
46                '}';
47    }
48}

类类型(xxx.class)

  • 类字节码 Class 本身也是一个类,是 Java 反射的源头
  • 通过类字节码 Class 可以获取到类的相关信息有:
通过类字节码获取类的相关信息 解释
Constructor 构造方法
Field 成员变量
Method 方法

反射的常规开发流程

获取类类型对象的方式

  1. 方式一:通过类名获取类类型对象
    image.png
  2. 方式二:通过对象获取
    image.png
  3. 方式三:通过类的全限定名
    image.png
  4. 方式四:通过类加载器
    image.png

Class 类对象的常用 API

方法名 功能描述
getName( ) 获取 包名 + 类名
getSimpleName( ) 获取类名
getDeclaredConstructor(Class<?>... parameterTypes) 根据参数,获取构造器,修饰符不影响
getDeclaredConstructors 获取全部声明的构造器,返回数组,修饰符不影响

反射 API 比较多,宏观分类

  • get+ 要获取的东西,例如:获取属性为 getField()、获取方法为 getMethod()
    • 注意:只能获取公有的东西,getMethod可以获取到本类及其父类的方法
  • get+Declared+ 要获取的东西,例如:获取属性为 getDeclaredField()、获取方法为 geDeclaredtMethod()
    • 注意:可以获取全部的东西,getDeclaredMethod只能获取到本类的方法
 1package com.soulboy.reflection;
 2
 3import java.lang.reflect.Constructor;
 4
 5public class ReflectUser {
 6
 7    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
 8
 9        // 方式一:通过类名获取类类型对象
10        Class<User> userClass1 = User.class;
11
12        // 方式二:通过对象获取
13        User user = new User();
14        Class<? extends User> userClass2 = user.getClass();
15
16        // 方式三:通过全限定名
17        Class<?> userClass3 = Class.forName("com.soulboy.reflection.User");
18
19        // 方式四:通过类加载器
20        ClassLoader classLoader = ReflectUser.class.getClassLoader();
21        Class<?> userClass4 = classLoader.loadClass("com.soulboy.reflection.User");
22
23        System.out.println(userClass1);
24        System.out.println(userClass2);
25        System.out.println(userClass3);
26        System.out.println(userClass4);
27
28//get   // Name = com.soulboy.reflection.User
29        System.out.println("getName = " + userClass1.getName());
30        //getSimpleName = User
31        System.out.println("getSimpleName = " + userClass1.getSimpleName());
32
33        // 根据参数,获取构造器,修饰符不影响 com.soulboy.reflection.User , 1
34        Constructor<User> declaredConstructor = userClass1.getDeclaredConstructor(String.class);
35        System.out.println(declaredConstructor.getName() + " , " + declaredConstructor.getParameterCount());
36        System.out.println("------------------------");
37
38        // 获取全部声明的构造器,返回数组,修饰符不影响
39        Constructor<?>[] declaredConstructors = userClass1.getDeclaredConstructors();
40        for (Constructor<?> constructor : declaredConstructors) {
41            System.out.println(constructor.getName() + " , " + constructor.getParameterCount());
42//            com.soulboy.reflection.User , 2
43//            com.soulboy.reflection.User , 1
44//            com.soulboy.reflection.User , 1
45//            com.soulboy.reflection.User , 0
46        }
47        System.out.println("------------------------");
48
49        // 无法获取private的构造函数
50        Constructor<?>[] constructors = userClass1.getConstructors();
51        for (Constructor<?> constructor : constructors) {
52            System.out.println(constructor.getName() + " , " + constructor.getParameterCount());
53            // com.soulboy.reflection.User , 2
54            // com.soulboy.reflection.User , 1
55            // com.soulboy.reflection.User , 0
56        }
57    }
58
59}

反射技术创建对象

通过获取了构造器,最终都是为了创建对象,所以Constructor类主要用于创建对象
JDK9 后用构造器创建对象的方式是,class.getDeclaredConstructor().newInstance()

  • 日常开发定义的POJO类里面,开发规范都推荐显示的写出空构造函数
  • 一是方便通过反射创建对象,二是子类继承父类时,默认调用super()保证父类有空构造函数
方法名 功能说明
T newInstance( ) 根据类的空参的构造器创建对象,类必须提供空参的构造器和 public 权限
T newInstance(Object... init args ) 根据指定的构造方法创建对象

反射创建对象有多种方式,常用步骤如下

  • 根据全类名获取对应的 class 对象
  • 调用指定参数结构的构造器,生成 constructor 的实例
  • 通过 constructor 的实例创建对应类的对象,并初始化类属性
 1public static void test3() throws Exception {
 2        // 1.根据全类名获取对应的 class 对象
 3        Class<User> userClass = User.class;
 4        // 2.调用指定参数结构的构造器,生成constructor的实例
 5        Constructor<User> constructor = userClass.getConstructor(int.class, String.class);
 6        // 3.通过 constructor的实例创建对应类的对象,并初始化类属性
 7        User user = constructor.newInstance(16, "Alice");
 8        // User{age=16, name='Alice'}
 9        System.out.println(user);
10    }

反射技术获取方法

通过 class 对象获取方法 Method 对象

方法名 功能说明
getMethods 获取当前运行类和 父类中声明的方法需要是 public 访问权限的方法
getDeclaredMethods( ) 获取当前运行时类中声明的全部方法,不包含父类中声明的方法

method 对象的方法

方法名 功能说明
getReturnType( ) 获取全部的返回值
getParameterTypes( ) 获取全部的参数
getModifiers( ) 获取修饰符
getExceptionTypes( ) 获取异常信息
method.getName( ) 获取方法名

修饰符权限说明

方法名 数值
public 1
private 2
public final void 17
public native int 257
public final native void 273

示例代码

 1public static void test4() throws Exception {
 2        // 1.根据全类名获取对应的 class 对象
 3        Class<User> userClass = User.class;
 4        // 2.获取当前类和父类中声明的方法,需要是public修饰符修饰的方法
 5        Method[] methods = userClass.getMethods();
 6        for (Method method : methods) {
 7            System.out.println("修饰符: " + method.getModifiers() + ",返回值类型: " + method.getReturnType() + ",方法名: " + method.getName());
 8            //        修饰符: 1,返回值类型: class java.lang.String,方法名: getName
 9            //        修饰符: 1,返回值类型: class java.lang.String,方法名: toString
10            //        修饰符: 1,返回值类型: void,方法名: setName
11            //        修饰符: 1,返回值类型: void,方法名: setAge
12            //        修饰符: 1,返回值类型: int,方法名: getAge
13            //        修饰符: 1,返回值类型: class java.lang.String,方法名: getJob
14            //        修饰符: 1,返回值类型: void,方法名: setJob
15            //        修饰符: 17,返回值类型: void,方法名: wait
16            //        修饰符: 17,返回值类型: void,方法名: wait
17            //        修饰符: 273,返回值类型: void,方法名: wait
18            //        修饰符: 1,返回值类型: boolean,方法名: equals
19            //        修饰符: 257,返回值类型: int,方法名: hashCode
20            //        修饰符: 273,返回值类型: class java.lang.Class,方法名: getClass
21            //        修饰符: 273,返回值类型: void,方法名: notify
22            //        修饰符: 273,返回值类型: void,方法名: notifyAll
23        }
24        System.out.println("------------------------");
25
26
27        // 3.获取当前类中声明的全部方法
28        Method[] declaredMethods = userClass.getDeclaredMethods();
29        for (Method declaredMethod : declaredMethods) {
30            System.out.println("修饰符: " + declaredMethod.getModifiers() + ",返回值类型: " + declaredMethod.getReturnType() +  ",方法名: " + declaredMethod.getName());
31            //            修饰符: 1,返回值类型: void,方法名: setAge
32            //            修饰符: 1,返回值类型: class java.lang.String,方法名: getName
33            //            修饰符: 1,返回值类型: class java.lang.String,方法名: toString
34            //            修饰符: 1,返回值类型: void,方法名: setName
35            //            修饰符: 1,返回值类型: int,方法名: getAge
36        }
37    }

反射技术获取属性

通过 class 对象获取属性 Field 对象

方法名 功能说明
getrields( ) 获取当前运行类和 父类中声明的属性,需要是 public 访问权限的属性
getDeclaredFields( ) 获取当前运行时类中声明的全部属性,不包含父类中声明的属性

通过属性对象 Field 获取属性信息

方法名 功能说明
getModifiers( ) 整数形式返回此 Field 的修饰符,整数对应在 java.lang.reflect.Modifier 里面
getType( ) 返回 Field 的属性类型
getName( ) 返回 Field 的名称
 1public static void test5() throws Exception {
 2        // 根据全类名获取对应的 class 对象
 3        Class<User> userClass = User.class;
 4        // getFields():获取当前类和父类中声明的属性,需要是public修饰符修饰的方法
 5        Field[] fields = userClass.getFields();
 6        for (Field field : fields) {
 7            System.out.println("修饰符: " + field.getModifiers() + ",属性类型: " + field.getType() +  ",属性名: " + field.getName());
 8            // 修饰符: 1,属性类型: class java.lang.String,属性名: name
 9        }
10        System.out.println("------------------------");
11
12        // 获取当前运行时类中声明的全部属性,不包含父类中声明的属性
13        Field[] declaredFields = userClass.getDeclaredFields();
14        for (Field declaredField : declaredFields) {
15            System.out.println("修饰符: " + declaredField.getModifiers() + ",属性类型: " + declaredField.getType() +  ",属性名: " + declaredField.getName());
16            // 修饰符: 2,属性类型: int,属性名: age
17            // 修饰符: 1,属性类型: class java.lang.String,属性名: name
18        }
19    }

暴力反射属性值操作

对反射进行相关操作,但如果构造器、方法、属性 没权限怎么操作?

可以通过 setAccessible(true),修改访问权限,Method和Field Constructor对象都有setAccessible()方法

模拟没有权限User 的空构造函数的修饰符改为 private User() {)

示例代码:暴力反射 private 的空构造函数

 1public static void test6() throws Exception {
 2        // User 的空构造函数的修饰符改为 private User() {)
 3        // 获取类对象
 4        Class<User> userClass = User.class;
 5        // 拿到空构造函数(private)
 6        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
 7        // 暴力反射
 8        declaredConstructor.setAccessible(true);
 9        // 报错:Exception in thread "main" java.lang.IllegalAccessException: class com.soulboy.reflection.ReflectUser cannot access a member of class com.soulboy.reflection.User with modifiers "private"
10        User user = declaredConstructor.newInstance();
11        user.setName("kobe");
12        user.setAge(39);
13        // User{age=39, name='kobe'}
14        System.out.println(user);
15    }

Product 类
成员变量和构造函数全部private

 1package com.soulboy.reflection;
 2
 3public class Product {
 4    private int price;
 5    private String title;
 6
 7    private Product() {
 8
 9    }
10
11    @Override
12    public String toString() {
13        return "Product{" +
14                "price=" + price +
15                ", title='" + title + '\'' +
16                '}';
17    }
18}

示例代码:暴力反射 Field,设置属性值

方法 功能说明
get(object obj) 获取取指定对象 obi 上此 Field 的属性内容
set(object obj,0bject value) 设置指定对象 obj 上此 Field 的属性内容
 1public static void test7() throws Exception {
 2        Class<Product> productClass = Product.class;
 3        // 暴力破解构造函数
 4        Constructor<Product> declaredConstructor = productClass.getDeclaredConstructor();
 5        declaredConstructor.setAccessible(true);
 6        Product product = declaredConstructor.newInstance();
 7
 8        // 获取属性Field,暴力反射
 9        Field title = productClass.getDeclaredField("title");
10        title.setAccessible(true);
11        title.set(product,"Java Reflection");
12        Field price = productClass.getDeclaredField("price");
13        price.setAccessible(true);
14        price.set(product,888);
15        
16        // Product{price=888, title='Java Reflection'}
17        System.out.println(product);
18    }

反射技术 invoke:运行类的指定方法

用来调用某个类中的方法但是它不是通过当前类直接去调用而是通过反射的机制去调用

invoke的中文意思是【调用、召唤】

运行类的指定方法的步骤

  1. 获取 class 对象
  2. 创建对象
  3. 获取方法
  4. invoke 调用

Object invoke(Object obj, Object .. args)

  • obj 是调用类的实例对象
  • args 是调用发方法的参数,是可变长度的
  • Object 对应原方法的返回值,若原方法无返回值,此时返回 null
  • 如果原方法为静态方法,此时形参 obj 可为 null
  • 如果原方法形参列表为空,则 args 为 null
  • 如果原方法声明为 private,则需要在调用此 invoke()方法前,调用对象的 setAccessible(true)方法

User 中添加如下代码

 1private String say(String name) {
 2        System.out.println("我是:" + name);
 3        return name;
 4    }
 5
 6    private int say(int age) {
 7        System.out.println("我的年龄:" + age);
 8        return age;
 9    }
10
11    private static void sleep(String name){
12        System.out.println("我是:" + name + ",准备睡觉了!");
13    }

示例代码 非 static:private String say(String name) { }

 1public static void test8() throws Exception {
 2        Class<User> userClass = User.class;
 3        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
 4        declaredConstructor.setAccessible(true);
 5        User user = declaredConstructor.newInstance();
 6        user.setName("Jame");
 7        user.setAge(39);
 8
 9        // 获取指定的方法并调用 private User(String name){ };
10        Method say = userClass.getDeclaredMethod("say", String.class);
11        // 开启访问
12        say.setAccessible(true);
13        // 第一个参数是对象,第二个参数是入参
14        Object returnValue  = say.invoke(user, "秃鹰");
15        // 我是:秃鹰
16    }

示例代码 static:private static void sleep(String name){ }

 1public static void test8() throws Exception {
 2        Class<User> userClass = User.class;
 3        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
 4        declaredConstructor.setAccessible(true);
 5        User user = declaredConstructor.newInstance();
 6        user.setName("Jame");
 7        user.setAge(39);
 8
 9        // 获取指定的方法并调用  private static void sleep(String name){ };
10        Method sleep = userClass.getDeclaredMethod("sleep", String.class);
11        // 开启访问
12        sleep.setAccessible(true);
13        // 果原方法为静态方法,此时形参 obj 可为null,第二个参数是入参
14        sleep.invoke(null,"秃鹰");
15        // 我是:秃鹰,准备睡觉了!
16    }

代理

image.png

属于代理设计模式(Proxy Pattern)

  • 为其他对象提供一种代理以控制对这个对象的访问,属于结构型模式。
  • 客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象

优点

  • 可以在访问一个类时做一些控制,或增加功能
  • 操作代理类无须修改原本的源代码,符合开闭原则,系统具有较好的灵活性和可扩展性

缺点

  • 增加系统复杂性和调用链路

静态代理

由程序创建或特定工具自动生成源代码,在程序运行前,代理类的.class文件就已经存在
通过将目标类与代理类实现同一个接口,让代理类持有真实类对象,然后在代理类方法中调用真实类方法,在调用真实类方法的前后添加我们所需要的功能扩展代码来达到增强的目的

A->B(Proxy 增加逻辑)->C

优点

  • 代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可
  • 方便增加功能,拓展业务逻辑

缺点

  • 代理类中出现大量冗余的代码非常不利于扩展维护
  • 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度

示例代码:静态代理

PayService(接口)

1package com.soulboy.proxy;
2
3public interface PayService {
4    String callBack(String outTradeNo);
5
6    int save(int userId, int productId);
7}

PayServiceImpl (被代理的类)

 1package com.soulboy.proxy;
 2
 3public class PayServiceImpl implements PayService{
 4    @Override
 5    public String callBack(String outTradeNo) {
 6        System.out.println("回调方法运行,更新订单状态!");
 7        return outTradeNo;
 8    }
 9
10    @Override
11    public int save(int userId, int productId) {
12        System.out.println("新增订单成功!");
13        return productId;
14    }
15}

StaticProxyPayServiceImpl (静态代理类)

 1package com.soulboy.proxy;
 2
 3public class StaticProxyPayServiceImpl implements PayService{
 4
 5    //目标代理对象
 6    public PayService payService;
 7
 8    public StaticProxyPayServiceImpl(PayService payService) {
 9        this.payService = payService;
10    }
11
12    /**
13     * 支付回调
14     *
15     * @param outTradeNo
16     * @return
17     */
18    @Override
19    public String callBack(String outTradeNo) {
20        // 开启事务
21        openTransaction();
22        String value = payService.callBack(outTradeNo);
23        //提交事务
24        commitTransaction();
25        return value;
26    }
27
28    /**
29     * 下单新增
30     * @param userId
31     * @param productId
32     * @return
33     */
34    @Override
35    public int save(int userId, int productId) {
36        // 开启事务
37        openTransaction();
38        int value = payService.save(userId, productId);
39        //提交事务
40        commitTransaction();
41        return value;
42    }
43
44    public static void openTransaction() {
45        System.out.println("开启事务");
46
47    }
48
49    public static void commitTransaction() {
50        System.out.println("提交事务");
51    }
52}

ProxyTest (测试类)
在采用静态代理之后,本类中的static方法已经不需要,只是之前为了手动开启事务而写

 1package com.soulboy.proxy;
 2
 3public class ProxyTest {
 4    public static void main(String[] args) {
 5        PayService payService = new PayServiceImpl();
 6        PayService staticProxyPayService = new StaticProxyPayServiceImpl(payService);
 7        staticProxyPayService.save(12, 232);
 8        //开启事务
 9        //新增订单成功!
10        //提交事务
11        staticProxyPayService.callBack("11021");
12        //开启事务
13        //回调方法运行,更新订单状态!
14        //提交事务
15
16//        //手动开启事务(比较麻烦)
17//        openTransaction();
18//        payService.save(12, 232);
19//        //手工提交事务(比较麻烦)
20//        commitTransaction();
21    }
22    
23    public static void openTransaction() {
24        System.out.println("开启事务");
25
26    }
27
28    public static void commitTransaction() {
29        System.out.println("提交事务");
30    }
31}

动态代理

在程序运行时,运用反射机制动态创建而成,无需手动编写代码

常见实现方式

  • JDK 动态代理:JDK 动态代理与静态代理一样,目标类需要实现一个代理接口,再通过代理对象调用目标方法
    核心方法就是反射机制:定义了一个 java.lang.reflect.InvocationHandler接口的实现类,重写invoke方法
    1//Object proxy:被代理的对象
    2//Method method:要调用的方法
    3//Object[ ] args:方法调用时所需要的参数
    4public interface InvocationHandler {
    5	public Object invoke(Object proxy, Method method, Object[ ] args)throw Throwable;
    6}
    
  • CGLIB 动态代理: 不要求实现接口。

示例代码:JDK 动态代理

PayService(接口)

1package com.soulboy.proxy;
2
3public interface PayService {
4    String callBack(String outTradeNo);
5
6    int save(int userId, int productId);
7}

PayServiceImpl (被代理的类)

 1package com.soulboy.proxy;
 2
 3public class PayServiceImpl implements PayService{
 4    @Override
 5    public String callBack(String outTradeNo) {
 6        System.out.println("回调方法运行,更新订单状态!");
 7        return outTradeNo;
 8    }
 9
10    @Override
11    public int save(int userId, int productId) {
12        System.out.println("新增订单成功!");
13        return productId;
14    }
15}

JdkProxy (JDK 动态代理类)

 1package com.soulboy.proxy;
 2
 3import java.lang.reflect.InvocationHandler;
 4import java.lang.reflect.Method;
 5import java.lang.reflect.Proxy;
 6
 7public class JdkProxy implements InvocationHandler {
 8    //定义目标类(被代理类)
 9    private Object targetObject;
10
11    /**
12     * 获取代理对象
13     * @param targetObject
14     * @return
15     */
16    public Object getProxyInstance(Object targetObject) {
17        this.targetObject = targetObject;
18        //通过反射创建代理对象Proxy
19        //(目标类的类加载器, 代理类实现的接口, 当前对象)
20        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
21    }
22
23    /**
24     * Object proxy:被代理的对象
25     * Method method:要调用的方法
26     * Object[ ] args:方法调用时所需要的参数
27     */
28    @Override
29    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
30        //开启事务
31        openTransaction();
32        //执行目标类的目标方法
33        Object result = method.invoke(targetObject, args);
34        //提交事务
35        commitTransaction();
36        return result;
37    }
38
39    /**
40     * 开启事务
41     */
42    public static void openTransaction() {
43        System.out.println("开启事务");
44
45    }
46
47    /**
48     * 提交事务
49     */
50    public static void commitTransaction() {
51        System.out.println("提交事务");
52    }
53}

ProxyTest (测试类)

 1package com.soulboy.proxy;
 2
 3public class ProxyTest {
 4    public static void main(String[] args) {
 5        PayService payService = new PayServiceImpl();
 6        JdkProxy jdkProxy = new JdkProxy();
 7        PayService payServiceProxyInstance = (PayService) jdkProxy.getProxyInstance(payService);
 8
 9        payServiceProxyInstance.save(12, 232);
10        //开启事务
11        //新增订单成功!
12        //提交事务
13
14        payServiceProxyInstance.callBack("11021");
15        //开启事务
16        //回调方法运行,更新订单状态!
17        //提交事务
18
19
20    }
21
22    public static void openTransaction() {
23        System.out.println("开启事务");
24
25    }
26
27    public static void commitTransaction() {
28        System.out.println("提交事务");
29    }
30}

类加载

image.png


作者:Soulboy