目录

Life in Flow

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

X

Reflection

Reflection

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

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

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

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

image.png

Base类

package com.soulboy.reflection;

public class Base {
    private String job;

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }
}

User类

package com.soulboy.reflection;

public class User extends Base {
    private int age;

    public String name;

    public User() {

    }

    public User(int age) {
        this.age = age;
    }

    private User(String name) {
        this.name = name;
    }

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

类类型(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只能获取到本类的方法
package com.soulboy.reflection;

import java.lang.reflect.Constructor;

public class ReflectUser {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {

        // 方式一:通过类名获取类类型对象
        Class<User> userClass1 = User.class;

        // 方式二:通过对象获取
        User user = new User();
        Class<? extends User> userClass2 = user.getClass();

        // 方式三:通过全限定名
        Class<?> userClass3 = Class.forName("com.soulboy.reflection.User");

        // 方式四:通过类加载器
        ClassLoader classLoader = ReflectUser.class.getClassLoader();
        Class<?> userClass4 = classLoader.loadClass("com.soulboy.reflection.User");

        System.out.println(userClass1);
        System.out.println(userClass2);
        System.out.println(userClass3);
        System.out.println(userClass4);

//get   // Name = com.soulboy.reflection.User
        System.out.println("getName = " + userClass1.getName());
        //getSimpleName = User
        System.out.println("getSimpleName = " + userClass1.getSimpleName());

        // 根据参数,获取构造器,修饰符不影响 com.soulboy.reflection.User , 1
        Constructor<User> declaredConstructor = userClass1.getDeclaredConstructor(String.class);
        System.out.println(declaredConstructor.getName() + " , " + declaredConstructor.getParameterCount());
        System.out.println("------------------------");

        // 获取全部声明的构造器,返回数组,修饰符不影响
        Constructor<?>[] declaredConstructors = userClass1.getDeclaredConstructors();
        for (Constructor<?> constructor : declaredConstructors) {
            System.out.println(constructor.getName() + " , " + constructor.getParameterCount());
//            com.soulboy.reflection.User , 2
//            com.soulboy.reflection.User , 1
//            com.soulboy.reflection.User , 1
//            com.soulboy.reflection.User , 0
        }
        System.out.println("------------------------");

        // 无法获取private的构造函数
        Constructor<?>[] constructors = userClass1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor.getName() + " , " + constructor.getParameterCount());
            // com.soulboy.reflection.User , 2
            // com.soulboy.reflection.User , 1
            // com.soulboy.reflection.User , 0
        }
    }

}

反射技术创建对象

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

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

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

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

反射技术获取方法

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

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

method对象的方法

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

修饰符权限说明

方法名数值
public1
private2
public final void17
public native int257
public final native void273

示例代码

public static void test4() throws Exception {
        // 1.根据全类名获取对应的 class 对象
        Class<User> userClass = User.class;
        // 2.获取当前类和父类中声明的方法,需要是public修饰符修饰的方法
        Method[] methods = userClass.getMethods();
        for (Method method : methods) {
            System.out.println("修饰符: " + method.getModifiers() + ",返回值类型: " + method.getReturnType() + ",方法名: " + method.getName());
            //        修饰符: 1,返回值类型: class java.lang.String,方法名: getName
            //        修饰符: 1,返回值类型: class java.lang.String,方法名: toString
            //        修饰符: 1,返回值类型: void,方法名: setName
            //        修饰符: 1,返回值类型: void,方法名: setAge
            //        修饰符: 1,返回值类型: int,方法名: getAge
            //        修饰符: 1,返回值类型: class java.lang.String,方法名: getJob
            //        修饰符: 1,返回值类型: void,方法名: setJob
            //        修饰符: 17,返回值类型: void,方法名: wait
            //        修饰符: 17,返回值类型: void,方法名: wait
            //        修饰符: 273,返回值类型: void,方法名: wait
            //        修饰符: 1,返回值类型: boolean,方法名: equals
            //        修饰符: 257,返回值类型: int,方法名: hashCode
            //        修饰符: 273,返回值类型: class java.lang.Class,方法名: getClass
            //        修饰符: 273,返回值类型: void,方法名: notify
            //        修饰符: 273,返回值类型: void,方法名: notifyAll
        }
        System.out.println("------------------------");


        // 3.获取当前类中声明的全部方法
        Method[] declaredMethods = userClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("修饰符: " + declaredMethod.getModifiers() + ",返回值类型: " + declaredMethod.getReturnType() +  ",方法名: " + declaredMethod.getName());
            //            修饰符: 1,返回值类型: void,方法名: setAge
            //            修饰符: 1,返回值类型: class java.lang.String,方法名: getName
            //            修饰符: 1,返回值类型: class java.lang.String,方法名: toString
            //            修饰符: 1,返回值类型: void,方法名: setName
            //            修饰符: 1,返回值类型: int,方法名: getAge
        }
    }

反射技术获取属性

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

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

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

方法名功能说明
getModifiers( )整数形式返回此Field的修饰符,整数对应在 java.lang.reflect.Modifier 里面
getType( )返回 Field的属性类型
getName( )返回 Field的名称
public static void test5() throws Exception {
        // 根据全类名获取对应的 class 对象
        Class<User> userClass = User.class;
        // getFields():获取当前类和父类中声明的属性,需要是public修饰符修饰的方法
        Field[] fields = userClass.getFields();
        for (Field field : fields) {
            System.out.println("修饰符: " + field.getModifiers() + ",属性类型: " + field.getType() +  ",属性名: " + field.getName());
            // 修饰符: 1,属性类型: class java.lang.String,属性名: name
        }
        System.out.println("------------------------");

        // 获取当前运行时类中声明的全部属性,不包含父类中声明的属性
        Field[] declaredFields = userClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("修饰符: " + declaredField.getModifiers() + ",属性类型: " + declaredField.getType() +  ",属性名: " + declaredField.getName());
            // 修饰符: 2,属性类型: int,属性名: age
            // 修饰符: 1,属性类型: class java.lang.String,属性名: name
        }
    }

暴力反射属性值操作

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

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

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

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

public static void test6() throws Exception {
        // User 的空构造函数的修饰符改为 private User() {)
        // 获取类对象
        Class<User> userClass = User.class;
        // 拿到空构造函数(private)
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
        // 暴力反射
        declaredConstructor.setAccessible(true);
        // 报错: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"
        User user = declaredConstructor.newInstance();
        user.setName("kobe");
        user.setAge(39);
        // User{age=39, name='kobe'}
        System.out.println(user);
    }

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

package com.soulboy.reflection;

public class Product {
    private int price;
    private String title;

    private Product() {

    }

    @Override
    public String toString() {
        return "Product{" +
                "price=" + price +
                ", title='" + title + '\'' +
                '}';
    }
}

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

方法功能说明
get(object obj)获取取指定对象 obi上此Field的属性内容
set(object obj,0bject value)设置指定对象 obj上此Field的属性内容
public static void test7() throws Exception {
        Class<Product> productClass = Product.class;
        // 暴力破解构造函数
        Constructor<Product> declaredConstructor = productClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Product product = declaredConstructor.newInstance();

        // 获取属性Field,暴力反射
        Field title = productClass.getDeclaredField("title");
        title.setAccessible(true);
        title.set(product,"Java Reflection");
        Field price = productClass.getDeclaredField("price");
        price.setAccessible(true);
        price.set(product,888);
        
        // Product{price=888, title='Java Reflection'}
        System.out.println(product);
    }

反射技术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中添加如下代码

private String say(String name) {
        System.out.println("我是:" + name);
        return name;
    }

    private int say(int age) {
        System.out.println("我的年龄:" + age);
        return age;
    }

    private static void sleep(String name){
        System.out.println("我是:" + name + ",准备睡觉了!");
    }

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

public static void test8() throws Exception {
        Class<User> userClass = User.class;
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        User user = declaredConstructor.newInstance();
        user.setName("Jame");
        user.setAge(39);

        // 获取指定的方法并调用 private User(String name){ };
        Method say = userClass.getDeclaredMethod("say", String.class);
        // 开启访问
        say.setAccessible(true);
        // 第一个参数是对象,第二个参数是入参
        Object returnValue  = say.invoke(user, "秃鹰");
        // 我是:秃鹰
    }

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

public static void test8() throws Exception {
        Class<User> userClass = User.class;
        Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        User user = declaredConstructor.newInstance();
        user.setName("Jame");
        user.setAge(39);

        // 获取指定的方法并调用  private static void sleep(String name){ };
        Method sleep = userClass.getDeclaredMethod("sleep", String.class);
        // 开启访问
        sleep.setAccessible(true);
        // 果原方法为静态方法,此时形参 obj 可为null,第二个参数是入参
        sleep.invoke(null,"秃鹰");
        // 我是:秃鹰,准备睡觉了!
    }

代理

image.png

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

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

优点

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

缺点

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

静态代理

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

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

优点

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

缺点

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

示例代码:静态代理

PayService(接口)

package com.soulboy.proxy;

public interface PayService {
    String callBack(String outTradeNo);

    int save(int userId, int productId);
}

PayServiceImpl (被代理的类)

package com.soulboy.proxy;

public class PayServiceImpl implements PayService{
    @Override
    public String callBack(String outTradeNo) {
        System.out.println("回调方法运行,更新订单状态!");
        return outTradeNo;
    }

    @Override
    public int save(int userId, int productId) {
        System.out.println("新增订单成功!");
        return productId;
    }
}

StaticProxyPayServiceImpl (静态代理类)

package com.soulboy.proxy;

public class StaticProxyPayServiceImpl implements PayService{

    //目标代理对象
    public PayService payService;

    public StaticProxyPayServiceImpl(PayService payService) {
        this.payService = payService;
    }

    /**
     * 支付回调
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public String callBack(String outTradeNo) {
        // 开启事务
        openTransaction();
        String value = payService.callBack(outTradeNo);
        //提交事务
        commitTransaction();
        return value;
    }

    /**
     * 下单新增
     * @param userId
     * @param productId
     * @return
     */
    @Override
    public int save(int userId, int productId) {
        // 开启事务
        openTransaction();
        int value = payService.save(userId, productId);
        //提交事务
        commitTransaction();
        return value;
    }

    public static void openTransaction() {
        System.out.println("开启事务");

    }

    public static void commitTransaction() {
        System.out.println("提交事务");
    }
}

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

package com.soulboy.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        PayService payService = new PayServiceImpl();
        PayService staticProxyPayService = new StaticProxyPayServiceImpl(payService);
        staticProxyPayService.save(12, 232);
        //开启事务
        //新增订单成功!
        //提交事务
        staticProxyPayService.callBack("11021");
        //开启事务
        //回调方法运行,更新订单状态!
        //提交事务

//        //手动开启事务(比较麻烦)
//        openTransaction();
//        payService.save(12, 232);
//        //手工提交事务(比较麻烦)
//        commitTransaction();
    }
    
    public static void openTransaction() {
        System.out.println("开启事务");

    }

    public static void commitTransaction() {
        System.out.println("提交事务");
    }
}

动态代理

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

常见实现方式

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

示例代码:JDK动态代理

PayService(接口)

package com.soulboy.proxy;

public interface PayService {
    String callBack(String outTradeNo);

    int save(int userId, int productId);
}

PayServiceImpl (被代理的类)

package com.soulboy.proxy;

public class PayServiceImpl implements PayService{
    @Override
    public String callBack(String outTradeNo) {
        System.out.println("回调方法运行,更新订单状态!");
        return outTradeNo;
    }

    @Override
    public int save(int userId, int productId) {
        System.out.println("新增订单成功!");
        return productId;
    }
}

JdkProxy (JDK动态代理类)

package com.soulboy.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxy implements InvocationHandler {
    //定义目标类(被代理类)
    private Object targetObject;

    /**
     * 获取代理对象
     * @param targetObject
     * @return
     */
    public Object getProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        //通过反射创建代理对象Proxy
        //(目标类的类加载器, 代理类实现的接口, 当前对象)
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
    }

    /**
     * Object proxy:被代理的对象
     * Method method:要调用的方法
     * Object[ ] args:方法调用时所需要的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //开启事务
        openTransaction();
        //执行目标类的目标方法
        Object result = method.invoke(targetObject, args);
        //提交事务
        commitTransaction();
        return result;
    }

    /**
     * 开启事务
     */
    public static void openTransaction() {
        System.out.println("开启事务");

    }

    /**
     * 提交事务
     */
    public static void commitTransaction() {
        System.out.println("提交事务");
    }
}

ProxyTest (测试类)

package com.soulboy.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        PayService payService = new PayServiceImpl();
        JdkProxy jdkProxy = new JdkProxy();
        PayService payServiceProxyInstance = (PayService) jdkProxy.getProxyInstance(payService);

        payServiceProxyInstance.save(12, 232);
        //开启事务
        //新增订单成功!
        //提交事务

        payServiceProxyInstance.callBack("11021");
        //开启事务
        //回调方法运行,更新订单状态!
        //提交事务


    }

    public static void openTransaction() {
        System.out.println("开启事务");

    }

    public static void commitTransaction() {
        System.out.println("提交事务");
    }
}

类加载

image.png


作者:Soulboy