Java框架-代理模式详细介绍、Spring的AOP

1. 代理模式详介

1.1 分类和作用

  • 分类:静态代理、jdk动态代理(接口代理)、cglib动态代理(子类代理)技术
  • 使用代理的原因:实际开发中通常都会调用别人编写的代码/框架来完成业务需求。很多情况是需要对这些代码/框架进行微调或扩展,而如果修改原代码很容易出现错误。这时候就需要使用代理。
  • 作用:通过代理访问目标对象。在目标对象实现的基础上,增强额外的功能操作,即在不修改原代码的基础上扩展目标对象的功能。

1.2 静态代理(不推荐)

  • 原理:
    1. 代理类实现与目标对象相同的接口。通过构造器或set方法给代理对象注入目标对象;
    2. 实现代理对象接口方法时,内部调用目标对象真正实现方法,并且可添加额外的业务控制。
  • 实现要求:
    1. 代理对象需要实现与目标对象一样的接口;
    2. 代理对象需要维护一个目标对象(需要目标对象调用目标对象自身方法);
    3. 代理对象一定会调用目标对象的方法。
  • 优点:在不修改原有代码的基础上,对指定的目标对象进行扩展
  • 缺点:
    1. 对于代理对象而言,接口的所有方法都要重写,即便需要增强的方法仅是其中的少数几个;
    2. 目标对象的接口修改后,目标对象和代理对象都得相应修改,不便于维护
    3. 每个目标对象至少有一个代理对象,导致代理类过多;
1.2.1 代码实现概述

代理类的代码实现:

  1. 代理类实现目标对象的接口;
  2. 代理类中创建目标对象;
  3. 重写接口方法,调用目标对象的方法,同时添加额外的业务逻辑。

1.3 jdk动态代理(接口代理)

  • 概念:代理类在程序运行时动态创建

  • 特点

    1. 目标对象必须实现接口;
    2. 程序运行期间,利用jdk的proxy相关api,在内存中动态构建字节码对象,从而生成代理对象,同时让代理对象实现目标对象的接口。
    3. jdk动态代理对象在内存的名称是$Proxy加上数字,如 P r o x y 3 、 Proxy3、 Proxy3Proxy4等
  • 生成动态代理的API:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)

    • 参数1 loader:当前使用哪个类加载器生成代理对象
    • 参数2 interfaces:目标对象实现的接口类型数组
    • 参数3 h:事件处理程序(当执行代理方法时候会触发),需要传入一个InvocationHandler接口实现类对象。(详见案例代码)
1.3.1 jdk动态代理的入门案例
1.3.1.1 目标对象接口
public interface IStar {
   
    /*明星的功能*/
    void singing(double money);
    void act(double money);
}
1.3.1.2 目标对象类,实现接口
public class Star implements IStar {
   
    @Override
    public void singing(double money) {
   
        System.out.println("[开演唱会!收费标准:]" + money);
    }

    @Override
    public void act(double money) {
   
        System.out.println("[拍戏!收费标准:]" + money);
    }
}
1.3.1.3 测试类中实现动态代理
public class Test_jdk {
   
    public static void main(String[] args) {
   
        //1.定义目标对象
        IStar target = new Star();
        //2.对目标对象动态生成代理对象
        /**
         * jdk动态代理
         *
         * 1.运行时期,利用jdk的api,在内存中动态构建字节码对象,从而生成代理对象。
         * 2.要求:目标对象一定要实现接口
         * 3.生成代理的Api
         * |-->Proxy
         *   |--> static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)
         *      参数1:loader       当前使用哪个类加载器生成代理对象
         *      参数2:interfaces   目标对象实现的接口类型数组
         *      参数3:h            事件处理程序(当执行代理方法时候会触发),需要传入一个InvocationHandler接口实现类对象
         *          InvocationHandler接口只有一个方法Object invoke(Object proxy, Method method, Object[] args)
         *              其中:proxy指当前的代理对象
         *                   method指代理对象调用的方法,使用反射的invoke()来执行方法代码
         *                   args指传入的参数数组
         * 4.原理
         *     生成代理对象:class $Proxy3 implements IStar
         *     通过类加载器生成动态代理对象,通过接口类型数组确定代理对象实现的接口,通过处理程序代码确定代理所要实现的功能
         * 5.API的写法套路如下,可以另行定义事件处理程序方法,再把方法传入
         */
        IStar proxy = (IStar) Proxy.newProxyInstance(
                Test_jdk.class.getClassLoader(),
                new Class[]{
   IStar.class},
                new InvocationHandler() {
   
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
                        // 获取方法参数
                        Double money = (Double) args[0];
                        // 添加额外的业务判断
                        if (money > 100000) {
   
                            // 访问目标对象的方法
                            return method.invoke(target,args);
                        } else {
   
                            System.out.println("档期忙,没空!");
                        }
                        return null;
                    }
                }
        );
        //实行代理方法
        proxy.singing(100001);
        proxy.act(1);
    }
}
1.3.1.4 代理工厂类优化代理类
/**
 * 代理工厂
 * 1. 对所有的目标对象生成代理对象,使用泛型
 * 2. 要求:目标对象一定要实现接口。因为用的是jdk接口代理
 */
public class ProxyFactory<T> {
   
    //创建泛型目标对象
    private T target;
    //代理工厂构造函数,工厂对象创建时会传入目标对象
    public ProxyFactory(T target) {
   
        this.target = target;
    }

    // 针对传入的目标对象生成代理对象并返回
    public T createProxy() {
   
        return (T)Proxy.newProxyInstance(
                this.getClass().getClassLoader(),   //根据工厂类获取类加载器
                target.getClass().getInterfaces(),  //根据目标对象获取所实现的所有接口的数组
                new InvocationHandler() {
   
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
                        // 获取方法参数
                        Double money = (Double) args[0];
                        // 判断
                        if (money > 100000) {
   
                            // 调用目标对象的方法
                            // 使用method对象的invoke()执行方法代码
                            return method.invoke(target, args);
                        } else {
   
                            System.out.println("没有调用目标对象方法!不满足条件");
                            return null;
                        }
                    }
                });
    }
}

1.4 cglib动态代理(子类代理)

  • 使用场景:如果目标对象没有实现任何接口,无法使用接口代理。此时就要用到CGLIB子类代理。
  • 原理:在内存中动态构建一个子类对象,从而实现对目标对象功能的扩展
  • 使用要求:需要导入CGLIB的jar包,或者直接引入Spring-Core核心包。
  • 注意事项:
    1. 代理的类不能为final,否则报错;
    2. **目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法!!
  • 特点:
    1. cglib动态代理对象在内存的名称是class com.azure.proxy3_cglib.Star$$EnhancerByCGLIB$$6e7ec13a,格式是:类全名$$EnhancerByCGLIB$$内存地址
1.4.1 cglib动态代理的入门案例
1.4.1.1 引入依赖

添加spring-core依赖

1.4.1.2 目标类
public class Star {
   
    public void singing(double money) {
   
        System.out.println("[开演唱会!收费标准:]" + money);
    }

    public void act(double money) {
   
        System.out.println("[拍戏!收费标准:]"
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值