Aop作为Spring的一个重要组成部分,有着举足轻重的位置,因此有必要去深入了解实现原理。
在现在的面试或者同事之间聊天的时候,我们都习惯去这样回答Aop:Aop就是面向切面编程,实现原理就是动态代理(jdk代理或者cglib代理),应用于Spring的事务和日志打印等场景。
不知道你发现问题没有,上述的回答很虚(评论性见解,并非工程学见解),谁都可以说出来这就几句话,失去了其独到的意义。作为爱折腾的我们,需要去探究、去实践,掌握根本才能决胜于千里。
下面我们开始进行Aop的源码分析:
为了方便进行源码分析,我们先使用一下Spring为我们提供的Aop功能,这样也为我们进行Debug调试提供入口。
AopMain .java
public class AopMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
annotationConfigApplicationContext.getBean(AopService.class).test();
}
}
AppConfig.java
@Configuration
@ComponentScan({"com.dflm"})
@EnableAspectJAutoProxy
public class AppConfig {
}
AopService.java
@Component
public class AopService implements Service{
public void test(){
System.out.println("AopService 的 test方法被执行");
}
}
解释:上述的AopMain就是入口类,AppConfig就是取代xml的配置类,AopService就是需要增强的类。
开始分析源码(下面的源码都是idea进行反编译的源码):
我们关注点是Aop,那么我们就需要关注AopService的test方法是如何实现增强的。那么对下属代码的getBean方法进行Debug.
annotationConfigApplicationContext.getBean(Service.class).test();
1、进入方法后的截图
2、继续进入到getBean方法
3、根据上述的结果可知,beanNames的长为1,那么直接进入到getBean方法。
4、进入到一个空壳方法
5、继续进入到具体的实现方法
6、由于装入的bean太多,上述设置了条件断点,因此再关闭应用,再调试进入到这个端点。观察取出的结果。
发现这个时候获取的aopService对象根本不存在。那么这个可以猜想:这一步是再spring初始化进行得操作,所以这个时候我们再最开始入口类得getBean下个断点;
居然这个时候进入到了入口类得断点处。
7、由于我们开始再getBean已经下过断点,此时只是需要下一步,进入到获取aopService得代码处。
8、这个时候能够从中取出aopService对象,而且是代理对象。那么这个时候需要先去getSingleTon中查看如何获取得aopService代理对象。
9、可以知道aopService是从一个ConcurrentHashMap中取出来的,而且取出来的时候,就已经是代理对象了。那么我们现在需要考虑两个问题:一是这个ConcurrentHashMap是什么时候放入的数据呢?二是我们的原生service去哪里了呢?那么带着这个问题,再进行分析,使用idea得全局查找this.singletonObjects.put,因为这是缓存保存的地方。
10、点击进入到上述查询的位置,并打上断点,此时去除其余的断点,重新启动应用。
11、查看左下侧的调用栈
12、逐个进行分析调用栈的方法,可以定位到如下代码,因为这个方法传入的参数是后面取出对象的关键。
13、由于ObjectFacory是一个接口类,因此我们关注他的实现方法createBean,进入实现代码中。
14、继续进入到doCreateBean方法中,发现我们的initializeBean方法是关键,居然把原生的bean转为了代理bean.
15、那么现在的重要的方法就是initializeBean方法,继续进入,发现方法里面主要的aop的代码就是applyBeanPostProcessorsAfterInitialization方法实现的。
16、继续进入到applyBeanPostProcessorsAfterInitialization方法实现.
17、再次断点进入到beanProcessor.postProcessAfterInitialization的方法实现。
断点完毕;
那么为了更好的去理解上述的bean后处理器,所以我们来写一个案例去辅助理解。
AopMain.java
public class AopMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
annotationConfigApplicationContext.getBean(Service.class).test();
}
}
AppConfig.java
@Configuration
@ComponentScan({"com.dflm"})
@EnableAspectJ
public class AppConfig {
}
AopService.java
@Component
public class AopService implements Service{
public void test(){
System.out.println("AopService 的 test方法被执行");
}
}
Service.java
public interface Service {
public void test();
}
EurekaAspectJ.java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(MyBenaPostprossor.class)
public @interface EurekaAspectJ {
}
MyBenaPostprossor.java
public class MyBenaPostprossor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("aopService")){
return Proxy.newProxyInstance(MyBenaPostprossor.class.getClassLoader(),new Class[]{Service.class}, new MyInvocationHandler(bean));
}
return bean;
}
}
MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler {
Object object;
public MyInvocationHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理方法进来了");
return method.invoke(object,args);
}
}