Spring 使用AspectJ 的三种方式
一,使用JavaConfig
二,使用注解隐式配置
三,使用XML 配置
背景知识:
注意 使用AspectJ 的 时候 要导入相应的Jar 包
嗯 昨天还碰到了这样的问题:
Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut ArithmeticPointCut
1 java.lang.IllegalStateException: Failed to load ApplicationContext 2 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) 3 at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83) 4 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117) 5 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83) 6 at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230) 7 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228) 8 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287) 9 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 10 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289) 11 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247) 12 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) 13 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 14 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 15 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 16 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 17 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 18 at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 19 at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) 20 at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 21 at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) 22 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) 23 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 24 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 25 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) 26 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 27 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 28 Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut ArithmeticPointCut 29 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) 30 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483) 31 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) 32 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) 33 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) 34 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) 35 at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) 36 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) 37 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) 38 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128) 39 at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60) 40 at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:108) 41 at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:251) 42 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98) 43 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116) 44 ... 25 more 45 Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut ArithmeticPointCut 46 at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:317) 47 at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:217) 48 at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:190) 49 at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:169) 50 at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:220) 51 at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:279) 52 at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:311) 53 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:119) 54 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:89) 55 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:70) 56 at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:346) 57 at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:298) 58 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:423) 59 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1633) 60 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) 61 ... 39 more
更换了Jar 包 从 1.5 --> 换到了 1.8.10 就好了 注意 aspectj 和asectjweaver 要版本对应
JavaConfig
类的结构 如 下图 所示
① Java Config
代码如下: 其中要注意@EnableAspectJAutoProxy(proxyTargetClass=true) 默认值为 false 只有开启proxyTargetClass 才会实现AspectJ 的切片功能
1 package com.myth.spring.aop.config; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.EnableAspectJAutoProxy; 5 import org.springframework.stereotype.Component; 6 7 @Component 8 @EnableAspectJAutoProxy(proxyTargetClass=true) 9 @ComponentScan(basePackages="com.myth.spring.aop") 10 public class ArithmeticCaculatorConfig { 11 12 }
② Service 类下面就是最基本的 实现类 代码如下:
注意每个类前 要加上@Component的标签
1 package com.myth.spring.aop.service; 2 3 public interface IArithmeticCaculator { 4 public int add(int a ,int b); 5 public int sub(int a ,int b); 6 public int mul(int a ,int b); 7 public int div(int a ,int b); 8 }
1 package com.myth.spring.aop.service.impl; 2 3 import org.springframework.stereotype.Component; 4 5 import com.myth.spring.aop.service.IArithmeticCaculator; 6 import com.myth.springAOP.exception.NotZeroException; 7 @Component 8 public class ArithmeticCaculatorImpl implements IArithmeticCaculator{ 9 10 @Override 11 public int add(int a, int b) { 12 System.out.println(a + b); 13 return a + b; 14 } 15 16 @Override 17 public int sub(int a, int b) { 18 return a - b; 19 } 20 21 @Override 22 public int mul(int a, int b) { 23 return a * b; 24 } 25 26 @Override 27 public int div(int a, int b) { 28 if (b == 0) { 29 throw new NotZeroException("除数不能为空"); 30 } 31 return a / b; 32 } 33 }
③ Aspect 类
这个类中 需要注意的点就比较多了
我在其中只写了 @Before , @After 和 @AfterReturning
1.定义为@Aspect
2.定义@Pointcut 根据这个切点来描写方法
3.写其中的@Before 或者其他Aspect 方法
当我们调用JoinPoint 时 要 joinpoint 要选择org.aspectj.lang.JoinPoint
1 package com.myth.spring.aop.service.aspect; 2 3 import java.util.Arrays; 4 5 import org.aspectj.lang.JoinPoint; 6 import org.aspectj.lang.annotation.AfterReturning; 7 import org.aspectj.lang.annotation.Aspect; 8 import org.aspectj.lang.annotation.Before; 9 import org.aspectj.lang.annotation.Pointcut; 10 import org.springframework.stereotype.Component; 11 12 @Component 13 @Aspect 14 /** 15 * 要在Config中配置@EnableAspectJAutoProxy(proxyTargetClass=true) 默认为false 16 * joinpoint 要选择org.aspectj.lang.JoinPoint 17 * 18 */ 19 public class LoggingAspect { 20 @Pointcut("execution(* com.myth.spring.aop.service.impl.ArithmeticCaculatorImpl.*(int , int ))") 21 public void ArithmeticPointCut() {} 22 23 @Before("ArithmeticPointCut()") 24 public void before(JoinPoint joinpoint) { 25 String method = joinpoint.getSignature().getName(); 26 System.out.println("The method "+method+" begins with "+Arrays.asList(joinpoint.getArgs())); 27 } 28 29 /* @After(value= "ArithmeticPointCut()") 30 public void After(JoinPoint joinpoint) { 31 String method = joinpoint.getSignature().getName(); 32 System.out.println("The method "+method+" ends with "); 33 }*/ 34 35 @AfterReturning(value="ArithmeticPointCut()", returning = "result") 36 public void afterReturn(JoinPoint joinPoint ,Object result) { 37 String method = joinPoint.getSignature().getName(); 38 System.out.println("The method "+method+" ends with " + result); 39 } 40 }
④ Junit 类
关于Junit 类 可以关注 我的这篇帖子
使用Spring的隐式注解和装配以及使用SpringTest框架
http://www.cnblogs.com/mythdoraemon/p/7533553.html
代码如下:
1 package com.myth.spring.aop.test; 2 3 import static org.junit.Assert.*; 4 5 import org.junit.Test; 6 import org.junit.runner.RunWith; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.test.context.ContextConfiguration; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 11 import com.myth.spring.aop.config.ArithmeticCaculatorConfig; 12 import com.myth.spring.aop.service.IArithmeticCaculator; 13 @RunWith(SpringJUnit4ClassRunner.class) 14 @ContextConfiguration(classes=ArithmeticCaculatorConfig.class) 15 public class TestAspect { 16 17 @Autowired 18 private IArithmeticCaculator iArithmeticCaculator; 19 20 @Test 21 public void test() { 22 iArithmeticCaculator.add(1, 2); 23 } 24 25 }
注解注入
注解注入大部分与JavaConfig 很像 , 其实就是把Java Config 的内容去不都写到了XML 中 罢了
结构如下:
我们跟上面的结构对应起来
① Java Config 这里应该是容器 applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> 9 <!-- 添加自动扫描机制 --> 10 <context:component-scan base-package="com.myth.spring.aop.annotion"></context:component-scan> 11 12 13 <!-- 添加切面机制 --> 14 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 15 16 </beans>
② Service 类 与上面的代码一样 就不重复写了
③ Aspect 类 也与 前面的一样 这回我写的是Around 方法
Around 其实包括了其他4种切面方式
1 /** 2 * 对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行, 3 * 是否执行连接点. 4 * 环绕通知必须有返回值,返回值为proceedingJoinPoint.proceed() 的结果 5 * @param proceedingJoinPoint 6 */ 7 @Around(value = "ArithmeticPointCut()") 8 public Object Around(ProceedingJoinPoint proceedingJoinPoint) { 9 String method = proceedingJoinPoint.getSignature().getName(); 10 Object result = null; 11 try { 12 //相当于前置通知 13 System.out.println("The method " + method + " begins with " + Arrays.asList(proceedingJoinPoint.getArgs())); 14 result = proceedingJoinPoint.proceed(); 15 //相当于后置返回通知 16 System.out.println("The method " + method + " ends with " + result); 17 } catch (NotZeroException e) { 18 //相当于异常通知 19 System.out.println("The method " + method + " occur an exception: " + e); 20 throw new NotZeroException("除数不能为空"); 21 } catch (Throwable e) { 22 // TODO Auto-generated catch block 23 e.printStackTrace(); 24 } finally { 25 //相当于后置通知 26 System.out.println("The method " + method + " end"); 27 } 28 return result; 29 }
④ Test 类
这回Test 类 跟上面的会有些不同
@ContextConfiguration(locations= {"classpath:applicationContext.xml"})
这样子加载ApplicationContext 资源
1 package com.myth.spring.aop.annotion.test; 2 3 import org.junit.Test; 4 import org.junit.runner.RunWith; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 import com.myth.spring.aop.annotion.service.IArithmeticCaculator; 10 11 @RunWith(SpringJUnit4ClassRunner.class) 12 @ContextConfiguration(locations= {"classpath:applicationContext.xml"}) 13 public class TestAspect { 14 @Autowired 15 private IArithmeticCaculator iArithmeticCaculator; 16 17 @Test 18 public void testAdd() { 19 iArithmeticCaculator.add(1, 2); 20 } 21 22 @Test 23 public void testDiv() { 24 iArithmeticCaculator.div(1, 0); 25 } 26 }
XML配置
XML 配置其实就是把所有的切面方法写到applicationContext中
这里就只放上xml 配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> 9 10 <bean id="arithmeticCalculator" class="com.myth.spring.AOP.xml.ArithmeticCalculatorImpl"> 11 </bean> 12 13 <bean id="loggingAspect" class="com.myth.spring.AOP.xml.LoggingAspect"></bean> 14 15 <bean id="validation" class="com.myth.spring.AOP.xml.Validation"></bean> 16 17 <!-- 配置AOP --> 18 <aop:config> 19 <aop:pointcut expression="execution(* com.myth.spring.AOP.xml.ArithmeticCalculator.*(.. ))" 20 id="pointcut"/> 21 <aop:aspect order="2" ref="loggingAspect" > 22 <aop:before method="beforeMethod" pointcut-ref="pointcut"/> 23 <!-- <aop:after method="afterMethod" pointcut-ref="pointcut"/> --> 24 <aop:after-returning method="afterReturningMethod" pointcut-ref="pointcut" returning="result"/> 25 <aop:after-throwing method="afterThrowingMethod" pointcut-ref="pointcut" throwing="ex"/> 26 </aop:aspect> 27 <aop:aspect order="1" ref="validation" > 28 <aop:before method="validate" pointcut-ref="pointcut"/> 29 </aop:aspect> 30 </aop:config> 31 32 </beans>
总结: 我是建议用第一种方法
首先界面干净清晰 不用太依赖于xml文件
其次 因为我这个案例比较小, 如果切面有很多 其他的写法会有很多切面在XML 中存在 对了 我们可以设置Order 属性来确定切面的顺序, 在 XML 中 我有表现出这种形式
当然了 每种写法都可以, 大家择其喜欢的吧