[Java]静态代理、动态代理(基于JDK1.8)

进步·于辰的Java技术分享栈 / 2024-02-03 / 原文

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/18002823
出自【进步*于辰的博客】

参考笔记一,P83;笔记二,P75.4。

目录
  • 1、静态代理
    • 1.1 概述
    • 1.2 静态代理的两种形式
      • 1.2.1 面向接口
      • 1.2.2 面向继承
  • 2、动态代理
    • 2.1 什么是动态代理?
    • 2.2 常见的两种形式
      • 2.2.1 JDK动态代理
      • 2.2.2 Cglib动态代理
    • 2.3 注意事项
  • 最后

1、静态代理

1.1 概述

什么是代理模式?
“代理模式”指通过为原有代码(目标对象)创建代理对象,以将附加功能(代码)注入代理方法,在不变动目标对象的情况下实现附加功能的设计模式,分为静态代理和动态代理。

什么是静态代理?
“静态代理”指需要为目标对象手动创建代理类来实现代理功能的代理。

1.2 静态代理的两种形式

1.2.1 面向接口

特点:目标对象与代理对象隶属于同一接口。

看下述代码:
1、公共接口:目标类和代理类的公共接口。

interface CommonService {
    // 修改成绩
    int changeScore(int score);
}

2、目标类。

class Target implements CommonService {
    @Override
    public int changeScore(int score) {
        System.out.println("成绩加" + score + "分");
        return 1;
    }
}

3、事务类:用于附加功能。

class Transaction {
    public void before() {
        System.out.println("打开事务");
    }

    public void after() {
        System.out.println("关闭事务");
    }
}

4、代理类。

class Proxy implements CommonService{
    private Target target;
    private Transaction transaction;

    public Proxy(Target target, Transaction transaction) {
        this.target = target;
        this.transaction = transaction;
    }

    @Override
    public int changeScore(int score) {
        transaction.before();
        int x = target.changeScore(score);
        transaction.after();
        return x;
    }
}

5、测试类。

class Test {
    public static void main(String[] args) {
        Target target = new Target();
        Transaction transaction = new Transaction();
        Proxy proxy = new Proxy(target, transaction);
        int x = proxy.changeScore(100);
        if (x > 0)
            System.out.println("成绩修改成功");
        else
            System.out.println("成绩修改出错");
    }
}

测试结果:
image

1.2.2 面向继承

特点:目标对象与代理对象是继承关系,代理对象继承于目标对象。

看下述代码:
1、目标类。

class Target {
    public int changeScore(int score) {
        System.out.println("成绩加" + score + "分");
        return 1;
    }
}

2、事务类:用于附加功能。

class Transaction {
    public void before() {
        System.out.println("打开事务");
    }

    public void after() {
        System.out.println("关闭事务");
    }
}

3、代理类。

class Proxy extends Target{
    private Transaction transaction;

    public Proxy(Transaction transaction) {
        this.transaction = transaction;
    }

    public int changeScore(int score) {
        transaction.before();
        int x = super.changeScore(score);
        transaction.after();
        return x;
    }
}

4、测试类。

class Test {
    public static void main(String[] args) {
        Transaction transaction = new Transaction();
        Proxy proxy = new Proxy(transaction);
        int x = proxy.changeScore(50);
        if (x > 0)
            System.out.println("成绩修改成功");
        else
            System.out.println("成绩修改出错");
    }
}

测试结果:
image

2、动态代理

2.1 什么是动态代理?

“动态代理”指在不变动源代码(目标对象)的情况下,无需手动创建代理类,而是通过反射创建代理对象来实现代理功能的代理。

2.2 常见的两种形式

2.2.1 JDK动态代理

特点:面向接口,隶属于Java API

看下述代码:
1、公共接口。

/**
 * 公共接口,目标对象和代理对象的公共接口,
 * 注:动态代理中代理对象是通过反射创建的(在JVN中,看不到),
 *     因为JDK动态代理面向接口,故目标对象和代理对象实现于同一接口
 */
interface StudentService {
    // 修改成绩
    int changeScore(int score);
}

2、目标类。

class PrimaryStudent implements StudentService {
    @Override
    public int changeScore(int score) {
        System.out.println("成绩加" + score + "分");
        return 1;
    }
}

3、事务类:用于附加功能。

class Transaction {
    public void before() {
        System.out.println("打开事务");
    }

    public void after() {
        System.out.println("关闭事务");
    }
}

4、代理类。

/**
 * JDK动态代理类,需实现接口 InvocationHandler 
 */
class DynamicProxy implements InvocationHandler {
    private Object target;// 目标对象,无需指定具体目标类类型
    private Transaction transaction;

    public DynamicProxy(Object target, Transaction transaction) {
        this.target = target;
        this.transaction = transaction;
    }

    /**
     * 代理时执行方法
     *
     * @param proxy 代理对象,暂未用到
     * @param method 目标对象的 Method 的 class对象
     * @param args 目标对象的 Method 的参数数组
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        transaction.before();
        /**
         * 解释:先看下面测试类内的 proxy.changeScore(15), 由于代理对象proxy实现于公共接口 CommonService,
         *       故proxy也有 changeScore()。
         *       当 proxy.changeScore(15) 时,就会执行当前invoke()。
         */
        Object result = method.invoke(target, args);// 这就是反射中Method对象执行时的方法,即通过反射调用目标对象被代理的方法
        transaction.after();
        return result;
    }
}

5、测试类。

class Test {
    public static void main(String[] args) {
        PrimaryStudent target = new PrimaryStudent();
        Transaction transaction = new Transaction();
        /**
         * 创建代理对象(通过反射)
         * 注:第一个参数是目标对象的类加载器,指定为哪个目标对象创建代理对象;
         *     第二个参数是目标对象实现的接口,指定目标对象和代理对象的公共接口、
         *     第三个参数是拦截器对象,指定用哪个拦截器来创建代理对象
         * 注:由于代理对象proxy是通过反射创建于JVM,并无类存在,故要上转为公共接口 CommonService
         */
        StudentService proxy = (StudentService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new DynamicProxy(target, transaction));
        int x = proxy.changeScore(15);
        if (x > 0)
            System.out.println("成绩修改成功");
        else
            System.out.println("成绩修改出错");
    }
}

测试结果:
image
newProxyInstance()用于创建代理对象,调用相应方法指调用重写的invoke(),调用目标方法的本质是反射

2.2.2 Cglib动态代理

特点:面向继承,隶属于Spring API

看下述代码:
1、目标类。

class PrimaryStudent {
    public int changeScore(int score) {
        System.out.println("成绩加" + score + "分");
        return 1;
    }
}

2、事务类:用于附加功能。

class Transaction {
    public void before() {
        System.out.println("打开事务");
    }

    public void after() {
        System.out.println("关闭事务");
    }
}

3、代理类。

/**
 * Cglib动态代理类,需实现接口 MethodInterceptor
 */
class DynamicProxy implements MethodInterceptor {
    private Object target;// 目标对象,无需指定具体目标类类型
    private Transaction transaction;

    public DynamicProxy(Object target, Transaction transaction) {
        this.target = target;
        this.transaction = transaction;
    }

    public Object createProxy() {
        Enhancer proxy = new Enhancer();// 创建代理对象
        proxy.setCallback(this);// 设置拦截器,指定回滚对象为自身
        proxy.setSuperclass(target.getClass());// 设置父类,指定为哪个目标对象创建代理对象
        return proxy.create();
    }

    /**
     * 代理时执行方法
     *
     * @param o 未知参数
     * @param method 目标对象的 Method 的 class对象
     * @param args 目标对象的 Method 的参数数组
     * @param methodProxy 未知参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        transaction.before();
        Object result = method.invoke(target, args);
        transaction.after();
        return result;
    }
}

4、测试类。

class Test {
    public static void main(String[] args) {
        PrimaryStudent target = new PrimaryStudent();
        Transaction transaction = new Transaction();
        /**
         * 创建代理对象(通过反射)
         * 注:由于代理对象proxy是通过反射创建于JVM,并无类存在。代理对象继承于目标对象,故将proxy上转为 PrimaryStudent
         */
        PrimaryStudent proxy = (PrimaryStudent)new DynamicProxy(target, transaction).createProxy();
        int x = proxy.changeScore(30);
        if (x > 0)
            System.out.println("成绩修改成功");
        else
            System.out.println("成绩修改出错");
    }
}

测试结果:
image
createProxy()返回创建的代理对象,Enhancer 类似 Proxy(代理类),由proxy.create()创建代理对象。intercept()与JDK动态代理的invoke()类似。

2.3 注意事项

  1. JDK动态代理和Cglib动态代理皆可拦截所有方法,包括:toString()hashcode()。不能拦截由 $final$ 修饰方法,如:getClass()
  2. JDK动态代理隶属于 Java API,Cglib动态代理隶属于 Spring API,大家导包的时候别选错了。

最后

本文中的例子是为了阐述静态代理和动态代理的实现思路、方便大家理解而简单举例的,不一定有实用性。

如果大家能理解代理的底层实现思路,那么,这对大家日后学习Spring事务管理、面向切面等知识和运用一定有所裨益。

本文完结。