我们在上面的几篇文章中已经了解了如何使用spring进行aop的开发,本篇文章我们来介绍一下动态代理的底层实现。
一. AOP编程概念
代码语言:javascript复制AOP: Aspect oriented programming: 面向切面编程 = spring动态代理开发。以切面为基本单位的程序开发,通过切面间的批次协同,相互调用,完成程序构建。 切面 = 切入点 额外功能
OOP: Object Oriented Programming: 面向对象编程,以对象为基本单位的程序开发,通过过程间的批次协同,相互调用,完成程序构建
POP: Procedure Oriented Programming: 面向过程(函数,方法)编程,以过程为基本单位的程序开发,通过过程间彼此协同,相互调用,完成程序构建
复制代码
代码语言:javascript复制AOP编程的本质就是Spring动态代理开发,通过代理类为原始类增加额外功能。好处就是利于原始类的维护
注意: aop编程不可能取代oop功能,是一种有力的补充。
复制代码
回顾AOP编程的开发步骤:
- 原始对象
- 额外功能(MethodBeforeAdvice, MethodInterceptor)
- 切入点(切入点表达式)
- 组装
切面 = 切入点 额外功能
复制代码
核心问题:
代码语言:javascript复制1. AOP如何创建代理类:
通过动态字节码技术
2. Spring工厂如何加工创建代理对象
通过原始对象的id, 获得的是代理对象
复制代码
二. 动态代理实现
spring底层的动态代理有两种实现方式,一是JDK的动态代理技术,而是Cglib开源框架提供的动态代理技术。
2.1 JDK动态代理
jdk的动态代理,必须是基于接口进行代理,也就是我们的目标类必须实现一个接口,才能进行代理。我们给出案例,这里省略了UserService, 和 UserServiceImpl.
代码语言:javascript复制创建代理三个要素:
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很像,毕竟底层就是这么实现的。我们直接程序应该怎么写
复制代码
代码语言:javascript复制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() 完成原始方法的调用。同时将原始方法的返回值返回,此时我们需要使用接口来接收代理对象(因为代理对象和原始对象实现了同一个接口),要注意的是这个代理对象是动态生成的,所以我们可能找不到他的具体源码,当我们使用他调用方法的时候,额外功能就可以执行了。
注意事项:
代码语言:javascript复制类加载器的作用:
1. 通过类加载器吧对应的类字节码文件加载到JVM中
2. 通过类加载器创建类的Class对象进而创建这个类的对象
如何获得类加载器:
虚拟机为每一个类的.class文件自动分配与之对应的ClassLoader,动态代理类没有源文件,它是通过动态字节码技术生成,把字节码直接写入jvm.
此时在动态代创建的过程中,需要ClassLoader创建代理类的Class对象,可是因为动态代理类没有.classs文件,JVM也就不会为他分配ClassLoader,但是又需要,就只能借用一个。所以找一个我们自己写的类获取Class对象在调用getClassLoader()即可,这里注意不要使用JDK的类获取,因为不是一个类加载器。
复制代码
2.2 CgLib动态代理
上面说了JDK的动态代理技术的实现。但是JDK的动态代理技术有一个弊端,就是原始类必须要实现一个接口,如果原始类没有实现任何接口,此时想要给他创建动态代理类,JDK的动态代理就实现不了了。而Cglib可以实现。
代码语言:javascript复制cglib动态代理原理:
cglib所创建的代理类是通过继承的方式实现的,他会继承原始类。原始类作为父类,代理类作为子类,这样就可以保证二者方法相同。而不需要实现接口。
复制代码
代码实现:
代码语言:javascript复制public class UserService{
public boolean login(String name, String password){
System.out.println("login...");
}
public void register(User user){
System.out.println("register...");
}
}
// 测试类
class Test{
/**
Enhancer.setClassLoader();
*/
public static void main(String[] args){
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhander.setClassLoader(Test.class.getClassLoader());
enhander.setSuperClass(userService.class);
MethodInteceptor interceptor = new MethodInterceptor(){
@Override
public Object(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable{
System.out.println("----log-----");
Object obj = method.invoke(userService, args);
return obj;
}
};
enhancer.setCallBack(interceptor);
UserService proxy = (UserService)Enhancer.create();
proxy.login("abc", "123");
proxy.register(new User());
}
}
复制代码
总结:
- JDK动态代理,Proxy.newInstance : 通过接口创建代理的实现类
- CGlib: 动态代理, Enhancer, 通过父子类继承
三. spring工厂如何创建代理对象
上面我们讲述了spring中两种动态代理的实现。通过动态代理技术,就可以创建出代理对象。那么在spring中,为什么我们通过原始bean的id就可以得到代理对象呢。我们来浅析一下他的原理。其实他主要还是通过BeanPostProcessor这个接口实现的。这个接口我们在前面介绍过。
相当于我们在加工的过程中,创建了他的动态代理对象,进行了返回。这样我们获取的对象就是动态代理对象了。我们来写个案例。
代码语言:javascript复制public interface UserService{
void register(User user);
boolean login(String name, String password)
}
public class UserServiceImpl implements UserService{
@Override
public void register(User user){
System.out.println("registe----")
}
@Override
public boolean login(String name, String password){
System.out.println("login-----")
}
}
public class ProxyBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{
return Proxy.newInstance(this.getClass.getClassLoader(), bean.getClass.getInterfaces,new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args){
System.out.println("---log advice ---");
Object ret = method.invoke(bean, args);
return ret;
}
});
}
}
复制代码
代码语言:javascript复制<bean id="userService" class="com.xxx.UserServiceImpl" />
<bean id="proxy" class="com.xxx.ProxyBeanPostProcessor" />
复制代码
而在spring的源码中,是通过一个叫做AbstractAutoProxyCreator,这个类就是专门用来创建代理对象的,而他的本质就是实现了BeanPostProcessor接口,在方法中创建了代理对象。
好了关于动态代理的一些概念和底层实现我们就先介绍到这里.