浅析Spring Aop源码

本文通过分析Spring AOP的源码,探讨AopService的test方法如何实现增强,详细介绍了从getBean方法开始的调试过程,揭示了bean在Spring初始化阶段如何转化为代理对象。在分析过程中,发现了关键步骤如applyBeanPostProcessorsAfterInitialization方法在实现AOP增强中的作用。
摘要由CSDN通过智能技术生成

        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);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值