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

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

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

CGLIB 代理 : 基于父类的动态代理技术:动态生成一个要代理的子类,子类重写要代理的类的所有不是
final的方法。在子类中采用方法拦截技术拦截所有的父类方法的调用,顺势织入横切逻辑,对方法进行
增强
JDK动态代理和CGLIB动态代理介绍_第2张图片
如上图,目标对象(目标类)就是被代理的对象,

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

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

  • Spring在选择用JDK还是CGLib的依据:
    当Bean实现接口时,Spring就会用JDK的动态代理
    当Bean没有实现接口时,Spring使用CGLib来实现
    可以强制使用CGLib(在Spring配置中加入

性能比较:
在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;
    }
}

JDK动态代理和CGLIB动态代理介绍_第3张图片
这里, 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);

    }
}

你可能感兴趣的:(JavaWeb,Java,框架,java,代理模式,spring)