java动态代理jdk动态代理和CGLib动态代理

1.代理的概念

什么是代理呢,显而易见,就如同生活中的代理一般,给代理人 做某件事的权限或者能力,然后其他人需要你去做这件事的时候,就不直接让你去做了,而让代理去做。这种模式有什么好处呢?你想想,代理相当于可以做你能做的事了,然后这个代理还能做到你不能做的事。比如你卖一个东西,你只管发货,代理可以帮你宣传,帮你售后,这个能力是不是就增强了。有人可能就有疑问,那我直接把所有能力绑在一起不就行了。额,我只能说很多的业务其实不可能一下全部考虑好,更好的应该是按自己的需求来。

正文

2.CGLIB动态代理

使用CGlib动态代理是实现AOP的一种方式。Spring生成对象时,如果当前类A被切面切到,比如A类的test方法加上了@Transactional注解,就会有一个事务的切面,那么除了生成一个单例外,还会生成代理对象CGLIB$A$XX 这种代理对象,这个 代理对象会继承类A然后实现MethodInterceptor接口。然后我们通过调用注入的类A的test方法,就会发现,其实我们是调用CGLIB$A$XX代理对象的 intercept方法,这个方法就是一个切面逻辑。代码实现可以自己尝试,如下。使用到了enhancer字节码增强器。下面主要讲解intercept方法的参数。主要是MethodProxy。

 

 @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // CGLib动态代理 类
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"./code");
        UserService bean = applicationContext.getBean(UserService.class);
        // 字节码增强器
//        Enhancer enhancer=new Enhancer();
//        // 设置代理类
//        enhancer.setSuperclass(UserService.class);
//        // 设置切面逻辑
//        enhancer.setCallback(new UserServiceCGLIBProxy());
//        UserInterface userService= (UserInterface)enhancer.create();
//        userService.getJDKuser();

        // CGlib代理接口
        Enhancer en=new Enhancer();
        // 设置代理类
        en.setSuperclass(UserService.class);
        // 设置切面逻辑
        UserService testuser=new UserService();

        en.setCallback(new MethodInterceptor() {
            /**
             *
             * @param o 代理对象
             * @param method 当前执行的方法
             * @param objects 方法的参数
             * @param methodProxy methodProxy会代理两个方法,一个是增强后的intercept方法,一个是没有增强的方法,o是代理对象
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                // invoke执行的是普通的CGlibinvokeMethod方法
                methodProxy.invoke(testuser,objects); // 相当于被代理对象直接调用CGlibinvokeMethod方法
                // invokeSuper执行的是 CGLIB$CGlibinvokeMethod方法
                Object result = methodProxy.invokeSuper(o, objects);// 相当于代理对象调用CGLIB$CGlibinvokeMethod方法
                System.out.println("接口切面逻辑");
                return result;
            }
        });
        UserService proxy= (UserService)en.create();
        proxy.CGlibinvokeMethod();

    }

2.1MethodProxy

MethodProxy中主要有两个方法,一个invoke,一个invokeSuper。

invoke执行的是被代理对象的当前方法。invokeSuper执行的时候代理对象的切面逻辑。

因此,invoke的参数必须是被代理对象,不能是XxCGLIB$XX这种代理对象。如果invoke调用代理对象的,代理对象执行方法时会进入intercept,然后又会调用invoke代理对象,这样就会形成无线 递归,最后栈溢出StackOverflowError。

invokeSuper的参数必须是代理对象,因为它会执行CGLIB$test方法,如果参数为被代理对象,那么是找不到这个方法的,也会报错。如果对方法有疑问,可以把代理对象的class字节码文件反编译后看看。代理对象会继承被代理对象然后会为每个方法创建一个CGLIB$XX方法。由于是继承,所以被代理对象的属性最好不要是final修饰

invoke源码,1代表的是被代理对象,2代表代理对象,底层的源码是使用了FASTCLASS,通过Switch (方法下标),入参是 方法的索引下标,从而快速定位执行方法,而不是通过反射来执行方法。因此这时候CGLIB性能优于jdk反射

这个大概说一下,有兴趣可以自己了解

public Object invoke(Object obj, Object[] args) throws Throwable {
		try {
			init();
			FastClassInfo fci = fastClassInfo;
			return fci.f1.invoke(fci.i1, obj, args);
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
		catch (IllegalArgumentException ex) {
			if (fastClassInfo.i1 < 0)
				throw new IllegalArgumentException("Protected method: " + sig1);
			throw ex;
		}
	}
	public Object invokeSuper(Object obj, Object[] args) throws Throwable {
		try {
			init();
			FastClassInfo fci = fastClassInfo;
			return fci.f2.invoke(fci.i2, obj, args);
		}
		catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
	}

3.JDK动态代理

通过反射的方式来实现增强。代理类实现InvocationHandler接口,但是不需要创建代理类对象

/**
 * 作者: 四叶
 * 创建时间: 2022/11/4
 * 内容:
 */
package com.cglibstudy.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserServiceJDKProxy implements InvocationHandler {
    private Object targetObj;
    public UserServiceJDKProxy(Object target){
        this.targetObj=target;

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // JDK动态代理
        System.out.println("JDK动态代理方法开始执行");
        Object ressult = method.invoke(targetObj, args);
        System.out.println("JDK动态代理方法执行完毕");
        return ressult;

    }
}
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
        // JDK动态代理
        UserService userService1=new UserService();
        Class<? extends UserService> aClass = userService1.getClass();
        UserServiceJDKProxy jdkProxy=new UserServiceJDKProxy(userService1);
        UserInterface service= (UserInterface)Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), jdkProxy);
        service.getJDKuser();
    }

总结与比较:

1,CGLIB需要依赖于三方CGLIB库生成代理类,JDK不需要,直接通过反射即可。

2,CGLIB会创建一个继承于被代理对象的代理类,然后实现MethodInterceptor,重写intercept方法,因此被代理类不可用final修饰; JDK实现InvocationHandler 接口。

3,JDK采用委托机制,只能代理实现接口的类。而CGLIB由于是继承被代理对象,可以对普通的类来实现代理。

4.如果当前类是实现了接口,Spring默认使用JDK实现AOP,如果没有实现,会使用CGLIB实现AOP。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值