小村村长

导航

Spring 的动态代理

  • 方法增强: 在不改变源码的基础上对方法增强
  1. 继承
  2. 装饰者模式:(弊端:繁琐,假如有1000个方法,必须重写所有的方法)
  • 1.装饰者类和被装饰者类必须实现同一个接口或继承同一个类
  • 2.在装饰者类中必须要有被装饰者类的引用
  • 3.在装饰者类中对需要增强的方法进行增强
  • 4.在装饰者类中对不需要增强的方法调用原来的逻辑

  • 动态代理
  1. 基于接口: JDK     要求: 被代理类必须实现了接口
  2. 基于子类: cglib     生成被代理类的子类

设计模式:
代码编写的固定格式

1.1 基于接口的动态代理(JDK):

作用: 方法增强

要求: 被代理类至少实现一个接口

Proxy: 动态代理

------------------ 接口
public interface AccountService {
    void save();
    void update();
    void delete();
    void select();
}
------------------ 实现类
/**
 * 此类中的方法需要增强
 * 被代理类
 */
public class AccountServiceImpl implements AccountService {
    public void save() {
        System.out.println("save...");
    }
    public void update() {
        System.out.println("update...");
    }
    public void delete() {
        System.out.println("delete...");
    }
    public void select() {
        System.out.println("select...");
    }
}
----------------- 动态代理
public class JDKProxy {
    public static void main(String[] args) {
        // 前提: 在不改变源码的基础上实现以下功能
        //需求: 在service层的所有方法执行前打印 1111111111111
        //      方法执行后打印2222222222222

        /**
         * 解决方案: 使用JDK提供的动态代理
         *      JDK的动态代理是基于接口的
         *      动态代理其实就是固定格式的代码
         *      要求: 被代理的类必须实现了接口
         *      API:
         *          java.lang.reflect.Proxy
         *              static Object newProxyInstance( // 此方法的执行会动态的生成一个类对象
         *                  ClassLoader loader,  // 类加载器,将生成的类加载到内存中
         *                  Class<?>[] interfaces,  // 生成的类需要实现的接口
         *                  InvocationHandler h) // 调用处理
         *   代理类和被代理类必须实现相同的接口
         *   在代理类中对需要增强的方法进行增强
         *   在代理类中对不需要增强的方法调用原来的逻辑
         */
        // 被代理类
        final AccountServiceImpl accountService = new AccountServiceImpl();
        // 代理类: 此方法的执行会动态的生成一个类对象
        AccountService proxy = (AccountService) Proxy.newProxyInstance(
                JDKProxy.class.getClassLoader(), // 类加载器,将生成的类加载到内存中
                // new Class[]{AccountService.class},// 生成的类需要实现的接口
                accountService.getClass().getInterfaces(),
                new InvocationHandler() { // 调用处理的方法
                    // 这个invoke方法与我们反射学习的invoke没有一点关系
                    // 每次调用生成的代理类的方法时,此方法都会执行
                    // 调用代理类的哪个方法,invoke就代表执行的那个方法
                    /**
                     * @param proxy : 动态生成的代理类对象引用(慎用)
                     * @param method : 代表当前执行的方法的字节码对象
                     * @param args : 当前执行的方法传递的参数
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // java中方法的返回值有两种状态,一种是有返回值,另外一种是无返回值
                        // 当前执行的方法若是有返回值,直接返回即可,如果没有则返回null
                        // 获取执行的方法名称
                        String name = method.getName();
                        System.out.println("执行的方法名: "+name);

                        // 原方法执行前打印 111111111
                        System.out.println("111111111111");
                        // 反射执行原方法
                        Object result = method.invoke(accountService,args);
                        // 原方法执行后打印 222222222
                        System.out.println("222222222222");
                        return result;
                    }
                }
        );

        proxy.insert();
        proxy.update();
        proxy.delete();
        proxy.select();
    }
}

 

1.2 基于子类的动态代理(cglib第三方):

作用: 方法增强

要求: 导入cglib的jar包,但cglib已经被Spring整合了,所以导入Spring-context包即可

 

------------------ jar包坐标
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
</dependencies>
------------------- 被代理类对象
/**
 * 此类中的方法需要增强
 * 被代理类
 */
public class AccountServiceImpl1{
    public void save() {
        System.out.println("save...");
        //System.out.println(1/0);
    }
    public void update() {
        System.out.println("update...");
    }
    public void delete() {
        System.out.println("delete...");
    }
    public void select() {
        System.out.println("select...");
    }
}
--------------------- 代理类
public class Demo3 {
    public static void main(String[] args) {
        // 对没有实现接口的类中的方法进行增强
        // 思路: 生成被代理类的子类
        // cglib: 生成的代理类,为被代理类的子类
        // 被代理类对象
        final AccountServiceImpl1 serviceImpl1 = new AccountServiceImpl1();

        // 此方法的执行会生成被代理类的子类
        // 代理类
        AccountServiceImpl1 proxy = (AccountServiceImpl1)Enhancer.create(
                serviceImpl1.getClass(), // 1.被代理对象的字节码
                new MethodInterceptor() { // 2.在原方法执行前进行拦截
                    // 对方法进行拦截处理
                    /**
                     * @param proxy : 生成的代理类对象
                     * @param method : 当前执行的方法的字节码对象
                     * @param args : 当前执行的方法传递的参数数组
                     * @param methodProxy (不用管) : 当前执行的方法的代理对象
                     * @return
                     * @throws Throwable
                     */
                    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                        Object result = null;
                        try {
                            // 在方法执行前打印 11111111111111
                            System.out.println("111111111111");
                            // 原方法执行
                            result = method.invoke(serviceImpl1,args);
                            // 在方法执行后打印 22222222222222
                            System.out.println("222222222222");
                        } catch (Exception e) {
                            // 当发生异常时打印 : 33333333333333
                            System.out.println("3333333333333");
                            //e.printStackTrace();
                        } finally {
                            // 最终打印 444444444444444
                            System.out.println("44444444444444");
                        }
                        return result;
                    }
                }
        );
        // 测试
        proxy.save();
        //proxy.update();
        //proxy.delete();
        //proxy.select();
    }
}

 总结:

Proxy基于接口 : JDK
    被代理类对象至少实现一个接口
    代理类和被代理类为兄弟关系
cglib基于子类 : cglib
    导入jar包坐标
     Enhancer
     代理和被代理类为父子关系
      Spring会自动抉择

posted on 2022-02-08 19:52  小村村长  阅读(92)  评论(0编辑  收藏  举报