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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值