1、导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
2、创建被拦截的bean
public class TestBean {
private String testStr = "testStr";
public String getTestStr(){
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
public void test(){
System.out.println(testStr);
}
}
3、创建Advisor
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.test(..))")
public void test(){
}
@Before("test()")
public void beforeTest(){
System.out.println("beforeTest");
}
@After("test()")
public void afterTest(){
System.out.println("afterTest");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint p){
System.out.println("before1");
Object o = null;
try {
o = p.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("after1");
return o;
}
}
4、创建配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
"
>
<aop:aspectj-autoproxy/>
<bean id="test" class="com.ipluto.demo.aspect.TestBean"/>
<bean class="com.ipluto.demo.aspect.AspectJTest"/>
</beans>
5、测试
public static void main(String[] args) {
ApplicationContext bf = new ClassPathXmlApplicationContext("beans.xml");
TestBean bean = (TestBean)bf.getBean("test");
bean.test();
}
6、运行结果
可以从测试的结果看到,目前test方法执行的时候被增强了,使得辅助功能可以独立于核心业务之外,方便与程序的扩展和解耦.
AOP自定义标签
我们都知道Spring是否支持注解的AOP是通过配置文件中的一个标签:
<aop:aspectj-autoproxy/>
,当在配置文件中声明了这句配置的时候,Spring就会支持注解的AOP.这里就和我们之前自己写自定义标签是一样的,声明一个自定义标签,并且把自定义的解析器注册进去,那么在解析标签的时候,就能够找到对应的解析器进行解析了.上面的那个自定义标签其实也是这样的原理.
所以我们不妨使用全局搜索aspectj-autoproxy
,找到下面这个类
public class AopNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// In 2.0 XSD as well as in 2.5+ XSDs
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
// 注册了aspectj-autoproxy对应的解析器
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace in 2.5+
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
之前讲到自定义标签的解析器,其实开始都是从parse方法开始的,所以我们直接来看一下AspectJAutoProxyBeanDefinitionParser的parse方法.
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 1、注册或者升级AnnotationAwareAutoProxyCreator定义beanName为internalAutoProxyCreator的Bean Definition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2、对于proxy-target-class以及expose-proxy属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3、注册组件并通知,便于监听器做进一步处理
registerComponentIfNecessary(beanDefinition, parserContext);
}
下面主要分析一下前两个步骤:
- 注册或者升级AnnotationAutoProxyCreator
对于AOP的实现,基本上都是靠AnnotationAwareAutoProxyCreator完成,他可以根据@Point注解定义的切点自动代理相匹配的bean.但是为了配置方便,Spring使用了自定义配置来帮助我们自动注册AnnotationAwareAutoProxyCreator.
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经存在了自动代理创建器且存在的自动代理创建器与现在的不一致
// 根据优先级来判断到底需要使用哪一个
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
// 比较优先级
if (currentPriority < requiredPriority) {
// 改变bean
apcDefinition.setBeanClassName(cls.getName());
}
}
// 如果已存在自动代理创建器和将要创建的一致,则无须再次创建
return null;
}
// 如果不存在自动代理创建器,则进行创建并且注册
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
- proxy-target-class以及expose-proxy属性的处理
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 对于proxy-target-class属性的处理
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
// 强制使用
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 对于expose-proxy属性的处理
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
// 强制使用的过程其实也是一个属性设置的过程
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
proxy-target-class: Spring AOP部分只用JDK动态代理或者CGLIB来为目标对象创建代理(建议尽量使用JDK的动态代理).如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理.所有该目标类型实现的接口都将被代理.若该目标对象没有实现任何接口,则创建一个CGLIB代理.如果你想强制使用CGLIB代理,只需要将< aop:config/>的proxy-target-class属性设置为true:
< aop:config proxy-target-class=“true”/>.
当使用CGLIB代理和@AspectJ自动代理支持的时候,也可以配置
< aop:aspectj-autoproxy/>的proxy-target-class属性设置为true:
< aop:aspectj-autoproxy proxy-target-class=“true”/>
不知道大家有没有遇到过这样的情况,一个类中有两个方法a和b.在a方法内部直接调用了b方法,并且事务的注解a和b方法上都有,但是依然会导致事务失效.
@Transactional(propagation=Propagation.REQUIRED)
public void a(){
this.b();
}
@Transactional(propagation=Propagation.REQUIRES_NEW)
private void b() {
}
因为在a方法调用b方法的时候使用了this,而this指向目标对象,所以this.b()不会执行b事务的切面.
解决办法:
1、修改配置:<aop:aspectj-autoproxy expose-proxy=“true”/>
2、把this.b(),修改为((TransactionTestService)AopContext.currentProxy()).b();