【源码解析】Spring AOP

举报
小明的混沌之路 发表于 2022/07/31 15:16:53 2022/07/31
【摘要】 抛出 AOP 概念,应用场景,动态代理

前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫 

🏆 Java领域优质创作者、阿里云专家博主、华为云专家🏆

🔥 如果此文还不错的话,还请👍关注点赞收藏三连支持👍一下博主哦

一、总的来聊  AOP 概念,应用场景,动态代理

【直接开始背诵并默写,抛出 AOP 概念,应用场景,动态代理】

【AOP概念、应用】AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方 式和运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 IOC 的一个扩展功能,先有的 IOC ,再有的AOP,只是在 IOC 的整个流程中新增的一个扩展点而已,在调用BeanPostProcessor 的后置处理方法实现。当我们需要为多个不具有继承关系的对象引入一个公共行为,例如日志、权限管理、事务等功能时,只能在在每个对象里引用公共行为。AOP便做这种不便于维护,而且有大量重复代码的事情。

编辑

【AOP实现-动态代理源码分析】一般我们会将切面类声明为一个bean,切点指定的方法所在的类也同样需由Spring 注入。Spring的@EnableAspectJAutoProxy 容器beanFactory 中注册一个AnnotationAwareAspectJAutoProxyCreator对象;AnnotationAwareAspectJAutoProxyCreator对目标对象进行代理对象的创建,对象内部,是封装JDK和CGlib 两个技术,实现动态代理对象创建的(创建代理对象过程中,会先创建一个代理工厂,获取到所有的增强器(通知方法),将这些增强器和目标类注入代理工厂,再用代理工厂创建对象);其中JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。Proxy 利用 InvocationHandler(定义横切逻辑) 接口动态创建 目标类的代理对象。代理对象执行目标方法,得到目标方法的拦截器链,利用拦截器的链式机制,依次进入每一个拦截器进行执行。最终通过 getProxy() 【后面详细说】方法来获取代理对象。

二、展开说AOP 实现过程

【展开说AOP(AOP Proxy)实现过程、AOP代理对象的生成、织入(Weaving)切面】

        【*实现过程*】AOP实现过程在IOC中,首先创建一个新的 ApplicationContext,加载bean并自动刷新上下文,在AbstractApplicationContext的 refresh() 方法中加载或刷新容器,通过AbstractApplicationContext 的finishBeanFactoryInitialization() 方法完成bean工厂的初始化和所有的单例bean;ConfigurableListableBeanFactory#preInstantiateSingletons() 方法 确保所有非lazyinit单例都被实例化;在AbstractBeanFactory 的 getBean(java.lang.String)方法,返回一个可以共享或独立于指定bean的实例,最后在AbstractBeanFactory#createBean() 方法创建bean。AbstractAutowireCapableBeanFactory#initializeBean() 方法,进行工厂回调、init方法和bean后处理器初始化给定的bean。

        下面进入AOP 代理对象的创建,在AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization() 方法,将 BeanPostProcessor 应用到给定的现有bean实例中,调用它们的代码后置处理初始化方法。返回的bean实例可以是围绕原始的包装器。在AbstractAutoProxyCreator 对目标对象进行代理对象的创建,对象内部,是封装JDK和CGlib两个技术的 postProcessAfterInitialization() 方法中,如果子类将bean标识为一个代理,则使用配置的侦听器创建一个代理;进入AbstractAutoProxyCreator的 wrapIfNecessary() 方法,包装给定的有资格代理的bean,在 AbstractAutoProxyCreator中的 createProxy() 会为给定的bean创建AOP代理,在 ProxyFactory工厂中getProxy(java.lang.ClassLoader)【后面详细说】,会根据此工厂中的设置创建新代理对象,最后DefaultAopProxyFactory#createAopProxy() 方法,创建给定的AOP代理工厂实例。

【实在解释不明白就直接画图,跳过上面的直接说,生成和织入】

 【*AOP代理对象的生成*】

        AOP代理对象的生成,这里面在 ProxyFactory工厂中getProxy(java.lang.ClassLoader),获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false),检查上面得到的接口中有没有定义 equals或者hashcode的接口,调用Proxy.newProxyInstance创建代理对象

【getProxy源码,了解即可,背诵上述文字描述】

    public Object getProxy(ClassLoader classLoader) { // 获取代理实例
       if (logger.isDebugEnabled()) {
           logger.debug("Creating JDK dynamic proxy: target source is " +this.advised.getTargetSource());
       }
       Class[] proxiedInterfaces =AopProxyUtils.completeProxiedInterfaces(this.advised);
       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }


【*织入AOP切面*】

        织入AOP切面在InvocationHandler,InvocationHandler 是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。invoke() 方法,获取可以应用到此方法上的通知链(Interceptor Chain),依次进入每一个拦截器进行执行,如果有,则应用通知,并执行joinpoint;如果没有,则直接反射执行joinpoint。最终通过 getProxy() 方法来获取代理对象。

【invoke源码,了解即可,背诵上述文字描述】

@Override @Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//从代理工厂中拿到TargetSource对象,该对象包装了被代理实例bean
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//被代理对象的equals方法和hashCode方法是不能被代理的,不会走切面
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
...
}
//这个target就是被代理实例
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
//从代理工厂中拿过滤器链 Object是一个MethodInterceptor类型的对象,其实就是一个advice对象
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
//如果该方法没有执行链,则说明这个方法不需要被拦截,则直接反射调用
if (chain.isEmpty()) { 
......



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。