SpringAOP之代理模式之动态代理之CGLIB&JDK

一、概念

代理模式是常用的Java设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。按照代理类的创建时期,代理类可分为两种。

静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类:在程序运行时,运用反射机制动态创建而成。

静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。

还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。

二、Spring AOP使用了两种动态代理:

一、原理区别:

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>

JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final

面试题:同一个类中,A方法调用B方法,B方法事务无法生效或者B方法没有被增强,也就是同一个类中嵌套事务或者嵌套方法无法生效的问题?

首先,来创建一个需要代理的接口

package demo.interf;  
  
public interface ICustomerService {  
    public void doSomething1();  
    public void doSomething2();  
}  

然后就是具体服务类:

package demo.interf.impl;  
  
import demo.interf.ICustomerService;  
  
public class CustomerServiceImpl implements ICustomerService {  
  
    public void doSomething1() {  
  
        System.out.println("Inside CustomerServiceImpl.doSomething1()");  
  
        doSomething2();  
  
    }  
  
    public void doSomething2() {  
  
        System.out.println("Inside CustomerServiceImpl.doSomething2()");  
  
    }  
  
}  

下面我们就来模拟动态生成代理类的过程,若使用 JDK Dynamic Proxy,这一过程是在运行时进行的。

CustomerServiceImpl 类对象的代理类:

package demo.interf.impl;  
  
import demo.interf.ICustomerService;  
  
public class CustomerServiceProxy implements ICustomerService {  
  
    private ICustomerService customerService;  
  
    public void setCustomerService(ICustomerService customerService) {  
        this.customerService = customerService;  
    }  
  
    public void doSomething1() {  
        doBefore();  
        customerService.doSomething1();  
        doAfter();  
    }  
  
    public void doSomething2() {  
        doBefore();  
        customerService.doSomething2();  
        doAfter();  
    }  
  
    private void doBefore() {  
        // 例如,可以在此处开启事务  
        System.out.println("do some important things before...");  
    }  
  
    private void doAfter() {  
        // 例如,可以在此处提交或回滚事务、释放资源等等  
        System.out.println("do some important things after...");  
    }  
  
}  

使用代理对象调用业务逻辑操作的客户端程序:

package test;  
  
import demo.interf.ICustomerService;  
import demo.interf.impl.CustomerServiceImpl;  
import demo.interf.impl.CustomerServiceProxy;  
  
public class TestProxy {  
      
    public static void main(String[] args) {  
        // 创建代理目标对象。对于Spring来说,这一工作  
        // 是由Spring DI容器完成的。  
        ICustomerService serviceProxyTarget = new CustomerServiceImpl();  
  
        // 创建代理对象。对于Spring来说,这一工作  
        // 也是由Spring DI容器完成的。  
        CustomerServiceProxy serviceProxy = new CustomerServiceProxy();  
        serviceProxy.setCustomerService(serviceProxyTarget);  
        ICustomerService serviceBean = (ICustomerService) serviceProxy;  
  
        // 调用业务逻辑操作  
        serviceBean.doSomething1();  
    }  
}  

好了,完成了。现在以调试方式运行这个应用,你会发现在 doSomething1() 中调用 doSomething2() 方法的时候并未去执行CustomerServiceProxy 类的 doBefore()、doAfter() 方法。再来看看这句关键代码:doSomething2(); 把它隐含的意思也表达出来吧:this.doSomething2(); 哦,我明白了,在 CustomerServiceImpl 类中 this 关键字表示的是当前这个CustomerServiceImpl类的实例。那程序当然就会去执行 CustomerServiceImpl 类中的 doSomething2() 方法了,而不会去执行 CustomerServiceProxy 类中的 doSomething2() 方法!!

在使用 Spring AOP 的时候,我们从 IOC 容器中获取的 Service Bean 对象其实都是代理对象,而不是那些 Service Bean 对象本身,也就是说获取的并不是被代理对象或代理目标。当我在自己的 Service 类中使用 this 关键字嵌套调用同类中的其他方法时,由于 this 关键字引用的并不是该 Service Bean 对象的代理对象,而是其本身,故 Spring AOP 是不能拦截到这些被嵌套调用的方法的。

解决方案:强制使用cglib代理即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值