【String注解驱动开发】困扰了我很久的AOP嵌套调用终于解决了!

简介: 最近在分析Spring源码时,在同一个类中写了嵌套的AOP方法,测试时出现:Spring AOP在同一个类里自身方法相互调用时无法拦截。哎,怎么办?还能怎么办呢?继续分析Spring源码,解决问题呗。于是乎,有了本文。项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

问题阐述

Spring AOP在同一个类里自身方法相互调用时无法拦截。比如下面的代码:

public class SomeServiceImpl implements SomeService  {  
    public void someMethod()  {  
        someInnerMethod();  
    }  
    public void someInnerMethod(){  
    }  
}

两个方法经过AOP代理,执行时都实现系统日志记录。单独使用someInnerMethod时,没有任何问题。但someMethod就有问题了。someMethod里调用的someInnerMethod方法是原始的,未经过AOP增强的。我们期望调用一次someMethod会记录下两条系统日志,分别是someInnerMethod和someMethod的,但实际上只能记录下someMethod的日志,也就是只有一条。在配置事务时也可能会出现问题,比如someMethod方法是REQUIRED,someInnerMethod方法是REQUIRES_NEW,someInnerMethod的配置将不起作用,与someMethod方法会使用同一个事务,不会按照所配置的打开新事务。

问题分析

由于java这个静态类型语言限制,最后想到个曲线救国的办法,出现这种特殊情况时,不要直接调用自身方法,而通过AOP代理后的对象。在实现里保留一个AOP代理对象的引用,调用时通过这个代理即可。例如下面的代码。

//从beanFactory取得AOP代理后的对象  
SomeService someServiceProxy = (SomeService)beanFactory.getBean("someService");   
//把AOP代理后的对象设置进去  
someServiceProxy.setSelf(someServiceProxy);   
//在someMethod里面调用self的someInnerMethod,这样就正确了  
someServiceProxy.someMethod();

但这个代理对象还要我们手动set进来。有没有更好的方式解决呢?

问题解决

幸好SpringBeanFactory有BeanPostProcessor扩展,在bean初始化前后会统一传递给BeanPostProcess处理,繁琐的事情就可以交给程序了,代码如下,首先定义一个BeanSelfAware接口,实现了此接口的程序表明需要注入代理后的对象到自身。

public class SomeServiceImpl implements SomeService,BeanSelfAware{  
    //AOP增强后的代理对象  
    private SomeService self;
    //实现BeanSelfAware接口  
    public void setSelf(Object proxyBean){  
        this.self = (SomeService)proxyBean  
    }  
    public void someMethod(){  
        //注意这句,通过self这个对象,而不是直接调用的  
        someInnerMethod();
    }  
    public void someInnerMethod(){  
    }  
}

再定义一个BeanPostProcessor,beanFactory中的每个Bean初始化完毕后,调用所有BeanSelfAware的setSelf方法,把自身的代理对象注入自身。

public class InjectBeanSelfProcessor implements BeanPostProcessor  {     
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException{ 
        if(bean instanceof BeanSelfAware){  
            System.out.println("inject proxy:" + bean.getClass());  
            BeanSelfAware myBean = (BeanSelfAware)bean;  
            myBean.setSelf(bean);  
            return myBean;  
        }  
        return bean;  
    }  
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException{  
        return bean;  
    }  
}

最后,在BeanFactory配置中组合起来,只需要把BeanPostProcesser加进去就可以了,比平常多一行配置而已。

<!-- 注入代理后的bean到bean自身的BeanPostProcessor... -->  
<bean class=" org.mypackage.InjectBeanSelfProcessor"></bean>  
<bean id="someServiceTarget" class="org.mypackage.SomeServiceImpl" />   
<bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">  
    <property name="target">  
        <ref local="someServiceTarget" />  
    </property>  
    <property name="interceptorNames">  
        <list>  
            <value>someAdvisor</value>  
        </list>  
    </property>  
</bean>  
<!-- 调用spring的DebugInterceptor记录日志,以确定方法是否被AOP增强 -->  
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor" />  
<bean id="someAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">  
    <property name="advice">  
        <ref local="debugInterceptor" />  
    </property>  
    <property name="patterns">  
        <list>  
            <value>.*someMethod</value>  
            <value>.*someInnerMethod</value>  
        </list>  
    </property>  
</bean>

这里的someService#someInnerMethod就表现出预期的行为了,无论怎样,它都是经过AOP代理的,执行时都会输出日志信息。

相关文章
|
1月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
331 0
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
428 1
|
7月前
|
安全 API
鸿蒙开发:实现AOP代码插桩能力
正确的运用AOP,可以提升代码的模块化、复用性、可维护性和灵活性,同时降低了耦合度,使系统更易于扩展和维护。
148 13
鸿蒙开发:实现AOP代码插桩能力
|
7月前
|
数据处理
鸿蒙开发:ArkTs字符串string
字符串类型是开发中非常重要的一个数据类型,除了上述的方法概述之外,还有String对象,正则等其他的用处,我们放到以后得篇章中讲述。
393 19
|
XML Java 数据格式
使用完全注解的方式进行AOP功能实现(@Aspect+@Configuration+@EnableAspectJAutoProxy+@ComponentScan)
本文介绍了如何使用Spring框架的注解方式实现AOP(面向切面编程)。当目标对象没有实现接口时,Spring会自动采用CGLIB库进行动态代理。文中详细解释了常用的AOP注解,如`@Aspect`、`@Pointcut`、`@Before`等,并提供了完整的示例代码,包括业务逻辑类`User`、配置类`SpringConfiguration`、切面类`LoggingAspect`以及测试类`TestAnnotationConfig`。通过这些示例,展示了如何在方法执行前后添加日志记录等切面逻辑。
1249 2
使用完全注解的方式进行AOP功能实现(@Aspect+@Configuration+@EnableAspectJAutoProxy+@ComponentScan)
|
11月前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
239 1
|
11月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
298 0
|
24天前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
172 5
|
4月前
|
存储 编译器 C语言
关于string的‘\0‘与string,vector构造特点,反迭代器与迭代器类等的讨论
你真的了解string的'\0'么?你知道创建一个string a("abcddddddddddddddddddddddddd", 16);这样的string对象要创建多少个对象么?你知道string与vector进行扩容时进行了怎么的操作么?你知道怎么求Vector 最大 最小值 索引 位置么?
101 0