cglib 动态代理和 jkd 动态代理

Java 中的代理

Java 中的代理,整体上可以分为两种:

  1. 静态代理,23 种设计模式,里边有一个代理模式,那个就是静态代理。
  2. 动态代理:
    a. 编译时增强
    ⅰ. AspectJ
    b. 运行时增强
    ⅰ. JDK 动态代理
    ⅱ. CGLIB 动态代理

例子:买电脑
背景:在早年间,假如你要买台电脑你得去联系电脑生产厂商购买,随着业务规模的增加,生产的电脑越来越多,当然买的人也多,这时候对于厂家来说相关的运营成本(仓储成本、物流成本、售后服务…)也随之增加了,为了剥离这块不相关的业务使之能专注生产电脑,把卖电脑的业务及相关的后期维护打包卖给代理商,由代理商来卖电脑。
代理:基于此发展而来的中间代理商必须要取得生产厂家的授权,在代码中即要实现相同的接口,一套标准
spring 会根据目标对象是否实现接口来选择哪种动态代理

静态代理

上述例子使用静态代理怎么做呢?首先需要定义一个接口,

public interface IProduer {
    void saleProduct(float money);
}

接着生产厂家和代理厂商都实现这个接口

/**
 * 生产厂商实现
 */
public class Producer implements IProduer {
    @Override
    public void saleProduct(float money) {
        System.out.println("生产厂家得到的钱:" + money);
    }
}
/**
 * 中介作为中间代理,实现了接口,也代理了生产厂商
 */
public class ProducerAgent implements IProduer{
    private Producer producer;

    public ProducerAgent(Producer producer) {
        this.producer = producer;
    }

    @Override
    public void saleProduct(float money) {
        publicAd();
        producer.saleProduct(money);
        agentFee();
    }

    private void agentFee() {
        System.out.println("收取中介费...");
    }

    private void publicAd() {
        System.out.println("代理厂商发广告,找卖家..");
    }
}

来看看效果:

public class App {
    public static void main(String[] args) {
        Producer produer = new Producer();
        ProducerAgent producerAgent = new ProducerAgent(produer);
        producerAgent.saleProduct(1500);
    }
}

输出:

代理厂商发广告,找卖家..
生产厂家得到的钱:1500.0
收取中介费...

可以看到,静态代理需要写很多的代理类,实际开发过程中很少去用

AspectJ

首先,在 IDEA 中想要运行 AspectJ,需要先安装 AspectJ 插件,就是下面这个:
在这里插入图片描述
安装好之后,我们需要在 IDEA 中配置一下,使用 ajc 编译器代替 javac(这个是针对当前项目的设置,所以可以放心修改):
在这里插入图片描述
有如下几个需要修改的点: 

  1. 首先修改编译器为 ajc。
  2. 将使用的 Java 版本改为 8,这个一共有两个地方需要修改。
  3. 设置 aspectjtools.jar 的位置,这个 jar 包需要自己提前准备好,可以从 Maven 官网下载,然后在这里配置 jar 的路径,配置完成之后,点
    击 test 按钮进行测试,测试成功就会弹出来图中的弹框。
    对于第 3 步所需要的 jar,也可以在项目的 Maven 中添加如下依赖,自动下载,下载到本地仓库之后,再删除掉 pom.xml 中的配置即可:
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjtools</artifactId>
      <version>1.9.7.M3</version>
    </dependency>

这样,开发环境就准备好了。接下来,假设我有一个银行转帐的方法:

public class MoneyService {
 public void transferMoney() {
 System.out.println("转账操作");
 }
}

我想给这个方法添加事务,那么我就新建一个 Aspect,如下:

public aspect TxAspect {
 void around():call(void MoneyService.transferMoney()){
   System.out.println("开启事务");
    try {
        proceed();
        System.out.println("提交事务事务");
     } catch (Exception e) {
        System.out.println("回滚事务");
     }
  }
}

这就是 AspectJ 的语法,跟 Java 有点像,但是不太一样。需要注意的是,这个 TxAspect 不是一个 Java 类,它的后缀是 .aj ,proceed 表示继续执行目标方法,
最后,我们去运行转账服务:

public class Demo01 {
 public static void main(String[] args) {
 MoneyService moneyService = new MoneyService();
 moneyService.transferMoney();
 }
}

运行结果:

开启事务
转账操作
提交事务

这就是一个静态代理,为什么这么说呢?我们通过 IDEA 来查看一下 TxAspect 编译之后的结果:

@Aspect
public class TxAspect {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }
    }

    public TxAspect() {
    }

    @Around(
            value = "call(void MoneyService.transferMoney())",
            argNames = "ajc$aroundClosure"
    )
    public void ajc$around$org_javaboy_demo_p2_TxAspect$1$3b99afea(AroundClosure ajc$aroundClosure) {
        System.out.println("开启事务");
        try {
            ajc$around$org_javaboy_demo_p2_TxAspect$1$3b99afeaproceed(ajc$aroundClosure);
            System.out.println("提交事务事务");
        } catch (Exception var2) {
            System.out.println("回滚事务");
        }
    }

    public static TxAspect aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("org_javaboy_demo_p2_TxAspect", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

再看一下编译之后的启动类:

public class Demo01 {
 public Demo01() {
 }
 public static void main(String[] args) {
 	MoneyService moneyService = new MoneyService();
 	transferMoney_aroundBody1$advice(moneyService, TxAspect.aspectOf(), (AroundClosure)null);
  }
}

可以看到,都是修改后的内容了。
所以说 AspectJ 的作用就有点类似于 Lombok,直接在编译时期将我们的代码改了,这就是编译时增强。

基于接口的动态代理(JDK自带)

在这里插入图片描述

  • 统一的接口规范

    public interface IProduer {
        void saleProduct(float money);
    }
    
  • 生产厂家的实现

    public class Producer implements IProduer {
        @Override
        public void saleProduct(float money) {
            System.out.println("生产厂家得到的钱:"+money);
        }
    }
    
  • 代理售卖电脑

    public class Client {
        public static void main(String[] args) {
            Producer producer = new Producer();
            IProduer proxyProducer = (IProduer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() {
                /**
                 * 作用:执行被代理对象的任何接口方法都会经过该方法
                 * @param proxy 代理对象的引用
                 * @param method 当前执行的方法
                 * @param args 当前执行方法的参数
                 * @return 和被代理对象有相同的返回值
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //提供增强的业务代码
                    Object res = null;
                    float money = (float) args[0];
                    if ("saleProduct".equals(method.getName())){
                        res = method.invoke(producer,money*0.85f);
                    }
                    return res;
                }
            });
            proxyProducer.saleProduct(10000);
        }
    }
    

缺点:如果没有实现任何接口 Proxy 这种方式就无法使用了

基于子类的动态代理(cgLib)

在这里插入图片描述

  • 引入第三方 jar 包

        <dependencies>
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>2.1_3</version>
            </dependency>
        </dependencies>
    
  • 生产厂家

    public class Producer{
        public void saleProduct(float money) {
            System.out.println("生产厂家得到的钱:"+money);
        }
    }
    
  • 经销商

    public class Client {
        public static void main(String[] args) {
            Producer producer = new Producer();
            Producer producerProxy = (Producer) Enhancer.create(producer.getClass(), new MethodInterceptor() {
                /**
                 * 作用:执行被代理对象的任何接口方法都会经过该方法
                 * @param proxy 代理对象的引用
                 * @param method 当前执行的方法
                 * @param args 当前执行方法的参数
                 * @param methodProxy 当前执行方法的代理对象
                 * @return 和被代理对象有相同的返回值
                 * @throws Throwable
                 */
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    //提供增强的业务代码
                    Object res = null;
                    float money = (float) args[0];
                    if ("saleProduct".equals(method.getName())){
                        res = method.invoke(producer,money*0.8f);
                    }
                    return res;
                }
            });
    	    producerProxy.saleProduct(20000);
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值