JDK动态代理和CGLIB动态代理介绍

Posted 格子衫111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK动态代理和CGLIB动态代理介绍相关的知识,希望对你有一定的参考价值。

我们先看一下下面这段代码:

可以看到,这里为了增加事务,对业务代码的侵入性较强,不利于后期的维护,那么有没有办法使业务代码和事务代码分开呢?答案肯定是有的,在Spring中可以使用AOP进行解耦,但是其底层其实是使用的动态代理实现的,那么我们在这里就介绍一下两种常见的动态代理模式:JDK动态代理CGLIB动态代理

常用的动态代理技术
JDK 代理 : 基于接口的动态代理技术·:利用拦截器(必须实现InvocationHandler)加上反射机制生成
一个代理接口的匿名类,再调用具体方法前调用InvokeHandler来处理,从而实现方法增强

CGLIB 代理 : 基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是
final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行
增强

如上图,目标对象(目标类)就是被代理的对象,

  • JDK动态代理:代理类是去实现目标类的接口(目标类必须实现接口),和目标类同级

  • CGLIB动态代理:代理类是采用ASM生成目标类的子类,和目标类是继承关系

  • Spring在选择用JDK还是CGLib的依据:
    当Bean实现接口时,Spring就会用JDK的动态代理
    当Bean没有实现接口时,Spring使用CGLib来实现
    可以强制使用CGLib(在Spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)

性能比较:
在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点

实现方式:

JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:
 1. 实现InvocationHandler接口,重写invoke()
 2. 使用 Proxy.newProxyInstance() 产生代理对象
 3. 被代理的对象必须要实现接口

CGLib 必须依赖于CGLib的类库,需要满足以下要求:
 1. 实现MethodInterceptor接口,重写intercept()
 2. 使用 Enhancer对象.create() 产生代理对象
 3. 被代理的对象不能使用final修饰

一、JDK动态代理方式

Jdk工厂类

/*
    JDK动态代理工厂类
 */
@Component
public class JDKProxyFactory 

    @Autowired
    private AccountService accountService;

    @Autowired
    private TransactionManager transactionManager;

    /*
        采用JDK动态代理技术来生成目标类的代理对象
         ClassLoader loader, : 类加载器:借助被代理对象获取到类加载器
         Class<?>[] interfaces, : 被代理类所需要实现的全部接口
         InvocationHandler h : 当代理对象调用接口中的任意方法时,那么都会执行InvocationHandler中invoke方法
     */
    public AccountService createAccountSericeJDKProxy()

        AccountService accountServiceProxy= (AccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() 

            @Override      
            //   proxy: 当前的代理对象引用   method:被调用的目标方法的引用   
            //   args:被调用的目标方法所用到的参数
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
                try 
                        if(method.getName().equals("transfer")) 
                            System.out.println("进行了前置增强");
                            // 手动开启事务:调用事务管理器类中的开启事务方法
                            transactionManager.beginTransaction();

                            // 让被代理对象的原方法执行
                            method.invoke(accountService, args);
                            System.out.println("进行了后置增强");

                            // 手动提交事务
                            transactionManager.commit();
                        else 

                            method.invoke(accountService, args);
                        
                 catch (Exception e) 
                    e.printStackTrace();
                    // 手动回滚事务
                    transactionManager.rollback();
                 finally 
                    // 手动释放资源
                    transactionManager.release();
                
                
                return null;
            
        );

        return  accountServiceProxy;
    


这里, method.invoke(accountService, args) 进行业务方法的调用,然后在前后增加了事务的处理

测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceImplTest 

	@Autowired
    private JDKProxyFactory proxyFactory;
	    /*
        测试JDK动态代理优化转账案例
     */
    @Test
    public void testTransferProxyJDK()

        // 当前返回的实际上是AccountService的代理对象proxy
        AccountService accountSericeJDKProxy = proxyFactory.createAccountSericeJDKProxy();
        // 代理对象proxy调用接口中的任意方法时,都会执行底层的invoke方法
        accountSericeJDKProxy.transfer("tom","jerry",100d);
        accountSericeJDKProxy.save();

    

二、CGLIB动态代理方式

Cglib工厂类

/*
   该类就是采用cglib动态代理来对目标类(AccountServiceImpl)进行方法(transfer)的动态增强(添加上事务控制)
 */
@Component
public class CglibProxyFactory 

    @Autowired
    private AccountService accountService;

    @Autowired
    private TransactionManager transactionManager;

    public AccountService createAccountServiceCglibProxy()
        // 编写cglib对应的API来生成代理对象进行返回
        // 参数1 : 目标类的字节码对象
        // 参数2:  动作类,当代理对象调用目标对象中原方法时,那么会执行intercept方法
        AccountService accountServiceproxy = (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() 

            // o : 代表生成的代理对象   method:调用目标方法的引用  
            // objects:方法入参    methodProxy:代理方法
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable 

                try 
                    // 手动开启事务:调用事务管理器类中的开启事务方法
                    transactionManager.beginTransaction();

                    method.invoke(accountService, objects);

                    transactionManager.commit();
                 catch (Exception e) 
                    e.printStackTrace();
                    // 手动回滚事务
                    transactionManager.rollback();
                 finally 
                    // 手动释放资源
                    transactionManager.release();
                

                return null;
            
        );
        return accountServiceproxy;

    

这里, method.invoke(accountService, objects) 进行业务方法的调用,然后在前后增加了事务的处理

测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceImplTest 

	@Autowired 
	private CglibProxyFactory cglibProxyFactory;
	
	/*
       测试Cglib动态代理优化转账案例
    */
    @Test
    public void testTransferProxyCglib()

        // accountServiceCglibProxy: proxy
        AccountService accountServiceCglibProxy = cglibProxyFactory.createAccountServiceCglibProxy();
        accountServiceCglibProxy.transfer("tom","jerry",100d);

    

以上是关于JDK动态代理和CGLIB动态代理介绍的主要内容,如果未能解决你的问题,请参考以下文章

spring的AOP动态代理--JDK代理和CGLIB代理

动态代理之 CGLIB 动态代理

写cglib动态代理需要哪些jar包

Java入门提高篇Day12 Java代理——Cglib动态代理

jdk动态代理和cglib动态代理的区别

Spring 静态代理+JDK动态代理和CGLIB动态代理