Java | 动态代理使用详解

欢迎参观我的博客,一个Vue 与 SpringBoot结合的产物:https://poetize.cn

原文链接:https://poetize.cn/article?id=46

动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件

主要用处

Spring AOP、日志、用户鉴权、全局性异常处理、性能监控等

代理模式角色

  • Subject(抽象主题角色):定义代理类和真实主题的公共对外方法,也是代理类代理真实主题的方法
  • RealSubject(真实主题角色):真正实现业务逻辑的类
  • Proxy(代理主题角色):用来代理和封装真实主题

JDK动态代理代码示例

JDK动态代理主要涉及两个类:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

代理类和所有方法都被public final修饰,所以代理类只可被使用,不可以再被继承。

/**
 * 任务接口
 */
public interface BaseTask extends Serializable {

    /**
     * 任务执行前置处理
     */
    default void before(String[] args) throws Exception {
    }

    /**
     * 执行任务
     */
    void run(String[] args) throws Exception;

    /**
     * 任务执行后置处理
     */
    default void after(String[] args) throws Exception {
    }

    /**
     * 任务执行失败后回滚处理
     */
    default void rollback(String[] args) throws Exception {
    }
}

/**
 * 代理类
 */
@Slf4j
public class TaskProxy implements InvocationHandler {

    private BaseTask target;

    public TaskProxy(Class<? extends BaseTask> clazz) throws Exception {
        this.target = clazz.getConstructor().newInstance();
    }

    /**
     * 任务执行前置处理
     */
    private void before(String[] args) throws Exception {
        //加载环境参数配置
        EnvContext.loadEnvArgs();
        target.before(args);
    }

    /**
     * 任务执行失败后回滚处理
     */
    private void rollback(String[] args) throws Exception {
        try {
            target.rollback(args);
        } catch (Exception e) {
            log.error("任务执行失败后回滚异常", e);
        }
    }

    /**
     * 任务执行后置处理
     */
    private void after(String[] args) throws Exception {
        try {
            target.after(args);
        } catch (Exception ignored) {
        }
        try {
            JdbcContext.closeConnections();
        } catch (Exception ignored) {
        }
    }

    /**
     * @param proxy  代理
     * @param method 方法
     * @param args   运行参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String[] stringArgs = (String[]) args;
        try {
            before(stringArgs);
            return method.invoke(target, args);
        } catch (Exception e) {
            log.error("任务处理发生异常:{}", e.getMessage());
            rollback(stringArgs);
            throw e;
        } finally {
            after(stringArgs);
        }
    }

    /**
     * 获取被代理对象
     */
    public BaseTask getProxyInstance() {
        return (BaseTask) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
}

/**
 * 使用
 */
public class TaskExecutor {

    private TaskExecutor() {
    }
    
    public static void run(String[] args, Class<? extends BaseTask> clz) throws Exception {
        new TaskProxy(clz).getProxyInstance().run(args);
    }
}

JDK动态代理特点

  • JDK的代理是利用反射生成Proxyxx.class代理类字节码,并生成对象
  • JDK动态代理是基于接口设计实现的,如果没有接口,会抛异常
  • JDK动态代理之所以只能代理接口是因为代理类本身已经extends Proxy,而Java是不允许多重继承的,但是允许实现多个接口

CGLIB代理特点

  • CGLib采用了字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,织入横切逻辑,来完成动态代理的实现。
  • 由于CGLib是采用动态创建子类的方法,对于final方法,无法进行代理。
  • CGLib在创建代理对象时所花费的时间比JDK多得多,所以对于频繁创建对象,使用JDK方式要更为合适一些。

Spring AOP

AOP的技术,主要分为两大类:

  • 一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行。
  • 二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。

默认的策略是如果目标类是接口,则使用JDK动态代理技术;如果目标对象没有实现接口,则默认会采用CGLIB代理。

Java中创建对象的5种方式

Person person = new Person();


//Class类的newInstance使用的是类的public的无参构造器
Person person = Person.class.newInstance();


//有参数(不再必须是无参)的和私有的构造函数(不再必须是public)
Constructor<?>[] declaredConstructors = Person.class.getDeclaredConstructors();    //包括public的和非public的,当然也包括private的

Constructor<?>[] constructors = Person.class.getConstructors();    //只返回public的(返回结果是上面的子集)

Constructor<?> noArgsConstructor = declaredConstructors[0];
Constructor<?> haveArgsConstructor = declaredConstructors[1];

noArgsConstructor.setAccessible(true);    //非public的构造必须设置true才能用于创建实例
Object person1 = noArgsConstructor.newInstance();
Object person2 = declaredConstructors[1].newInstance("小明", 18);


//Clone
public class Person implements Cloneable


//反序列化
deserialize
posted @ 2023-09-27 10:16  LittleDonkey  阅读(111)  评论(0编辑  收藏  举报