我们在上面的几篇文章中已经了解了如何使用spring进行aop的开发,本篇文章我们来介绍一下动态代理的底层实现。
一. AOP编程概念
AOP: Aspect oriented programming: 面向切面编程 = spring动态代理开发。以切面为基本单位的程序开发,通过切面间的批次协同,相互调用,完成程序构建。 切面 = 切入点 + 额外功能 OOP: Object Oriented Programming: 面向对象编程,以对象为基本单位的程序开发,通过过程间的批次协同,相互调用,完成程序构建 POP: Procedure Oriented Programming: 面向过程(函数,方法)编程,以过程为基本单位的程序开发,通过过程间彼此协同,相互调用,完成程序构建
AOP编程的本质就是Spring动态代理开发,通过代理类为原始类增加额外功能。好处就是利于原始类的维护 注意: aop编程不可能取代oop功能,是一种有力的补充。
回顾AOP编程的开发步骤:
- 原始对象
- 额外功能(MethodBeforeAdvice, MethodInterceptor)
- 切入点(切入点表达式)
- 组装
切面 = 切入点 + 额外功能
核心问题:
1. AOP如何创建代理类: 通过动态字节码技术 2. Spring工厂如何加工创建代理对象 通过原始对象的id, 获得的是代理对象
二. 动态代理实现
spring底层的动态代理有两种实现方式,一是JDK的动态代理技术,而是Cglib开源框架提供的动态代理技术。
2.1 JDK动态代理
jdk的动态代理,必须是基于接口进行代理,也就是我们的目标类必须实现一个接口,才能进行代理。我们给出案例,这里省略了UserService, 和 UserServiceImpl.
创建代理三个要素: 1. 原始对象 2. 额外功能 3. 代理对象和原始对象实现相同的接口 JDK为我们了提供了Proxy.newInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) 方法类实现动态代理技术。 参数介绍 @param: ClassLoader var0: 创建代理对象所需的类加载器 @param: interfaces: 和原始对象实现的接口数组 @param: InvocationHandler: 额外功能 这里InvocatioHandler也是一个接口,所以我们需要传如一个实现。该接口有一个方法需要实现: Object invoke(Object proxy, Method method, Object[] args); @param Object proxy:代表代理对象,忽略,不要使用 @param Methdod method: 代表额外功能增加给的原始方法 @param Object[] args: 原始方法的参数 @return: Object: 原始方法的返回值 这种写法和我们之前的MethodInterceptor很像,毕竟底层就是这么实现的。我们直接程序应该怎么写
public class JDKProxyTest{ public static void main(String[] args){ // 1. 创建原始对象 Userservice userService = new UserServiceImpl(); // 2. 创建动态代理类 UserService proxy = (UserService)Proxy.newInstance(userService.getClass.getClassLoader() ,userService.getClass.getInterfaces() , new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[]args){ // 前置额外功能 Object obj = method.invoke(userService, args); // 后置额外功能 return obj; } }); proxy.login("abc", "123456"); } }
我们在InvocationHandler接口中的实现方法里加入了我们想要的额外功能,并通过调用method.invoke() 完成原始方法的调用。同时将原始方法的返回值返回,此时我们需要使用接口来接收代理对象(因为代理对象和原始对象实现了同一个接口),要注意的是这个代理对象是动态生成的,所以我们可能找不到他的具体源码,当我们使用他调用方法的时候,额外功能就可以执行了。
注意事项:
类加载器的作用: 1. 通过类加载器吧对应的类字节码文件加载到JVM中 2. 通过类加载器创建类的Class对象进而创建这个类的对象 如何获得类加载器: 虚拟机为每一个类的.class文件自动分配与之对应的ClassLoader,动态代理类没有源文件,它是通过动态字节码技术生成,把字节码直接写入jvm. 此时在动态代创建的过程中,需要ClassLoader创建代理类的Class对象,可是因为动态代理类没有.classs文件,JVM也就不会为他分配ClassLoader,但是又需要,就只能借用一个。所以找一个我们自己写的类获取Class对象在调用getClassLoader()即可,这里注意不要使用JDK的类获取,因为不是一个类加载器。 复制代码
2.2 CgLib动态代理
上面说了JDK的动态代理技术的实现。但是JDK的动态代理技术有一个弊端,就是原始类必须要实现一个接口,如果原始类没有实现任何接口,此时想要给他创建动态代理类,JDK的动态代理就实现不了了。而Cglib可以实现。
cglib动态代理原理: cglib所创建的代理类是通过继承的方式实现的,他会继承原始类。原始类作为父类,代理类作为子类,这样就可以保证二者方法相同。而不需要实现接口。
代码实现:
publicclassUserService{ publicbooleanlogin(Stringname, Stringpassword){ System.out.println("login..."); } publicvoidregister(Useruser){ System.out.println("register..."); } } // 测试类classTest{ /**Enhancer.setClassLoader();*/publicstaticvoidmain(String[] args){ UserServiceuserService=newUserService(); Enhancerenhancer=newEnhancer(); enhander.setClassLoader(Test.class.getClassLoader()); enhander.setSuperClass(userService.class); MethodInteceptorinterceptor=newMethodInterceptor(){ publicObject(Objecto, Methodmethod, Object[] args, MethodProxymethodProxy) throwsThrowable{ System.out.println("----log-----"); Objectobj=method.invoke(userService, args); returnobj; } }; enhancer.setCallBack(interceptor); UserServiceproxy= (UserService)Enhancer.create(); proxy.login("abc", "123"); proxy.register(newUser()); } } 复制代码
总结:
- JDK动态代理,Proxy.newInstance : 通过接口创建代理的实现类
- CGlib: 动态代理, Enhancer, 通过父子类继承
三. spring工厂如何创建代理对象
上面我们讲述了spring中两种动态代理的实现。通过动态代理技术,就可以创建出代理对象。那么在spring中,为什么我们通过原始bean的id就可以得到代理对象呢。我们来浅析一下他的原理。其实他主要还是通过BeanPostProcessor这个接口实现的。这个接口我们在前面介绍过。
相当于我们在加工的过程中,创建了他的动态代理对象,进行了返回。这样我们获取的对象就是动态代理对象了。我们来写个案例。
publicinterfaceUserService{ voidregister(Useruser); booleanlogin(Stringname, Stringpassword) } publicclassUserServiceImplimplementsUserService{ publicvoidregister(Useruser){ System.out.println("registe----") } publicbooleanlogin(Stringname, Stringpassword){ System.out.println("login-----") } } publicclassProxyBeanPostProcessorimplementsBeanPostProcessor{ publicObjectpostProcessBeforeInitialization(Objectbean, StringbeanName) throwsBeansException{ returnbean; } publicObjectpostProcessAfterInitialization(Objectbean, StringbeanName) throwsBeansException{ returnProxy.newInstance(this.getClass.getClassLoader(), bean.getClass.getInterfaces,newInvocationHandler(){ publicObjectinvoke(Objectproxy, Methodmethod, Object[] args){ System.out.println("---log advice ---"); Objectret=method.invoke(bean, args); returnret; } }); } }
<beanid="userService"class="com.xxx.UserServiceImpl"/><beanid="proxy"class="com.xxx.ProxyBeanPostProcessor"/>
而在spring的源码中,是通过一个叫做AbstractAutoProxyCreator,这个类就是专门用来创建代理对象的,而他的本质就是实现了BeanPostProcessor接口,在方法中创建了代理对象。
好了关于动态代理的一些概念和底层实现我们就先介绍到这里.