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 ApplicationContext2 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 ... 25more45 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
Error
更换了Jar 包 从 1.5 --> 换到了 1.8.10 就好了 注意 aspectj 和asectjweaver 要版本对应
JavaConfig
类的结构 如 下图 所示
① Java Config
代码如下: 其中要注意@EnableAspectJAutoProxy(proxyTargetClass=true) 默认值为 false 只有开启proxyTargetClass 才会实现AspectJ 的切片功能
1 packagecom.myth.spring.aop.config;2
3 importorg.springframework.context.annotation.ComponentScan;4 importorg.springframework.context.annotation.EnableAspectJAutoProxy;5 importorg.springframework.stereotype.Component;6
7 @Component8 @EnableAspectJAutoProxy(proxyTargetClass=true)9 @ComponentScan(basePackages="com.myth.spring.aop")10 public classArithmeticCaculatorConfig {11
12 }
ArithmeticCaculatorConfig
② Service 类下面就是最基本的 实现类 代码如下:
注意每个类前 要加上@Component的标签
1 packagecom.myth.spring.aop.service;2
3 public interfaceIArithmeticCaculator {4 public int add(int a ,intb);5 public int sub(int a ,intb);6 public int mul(int a ,intb);7 public int div(int a ,intb);8 }
IArithmeticCaculator
1 packagecom.myth.spring.aop.service.impl;2
3 importorg.springframework.stereotype.Component;4
5 importcom.myth.spring.aop.service.IArithmeticCaculator;6 importcom.myth.springAOP.exception.NotZeroException;7 @Component8 public class ArithmeticCaculatorImpl implementsIArithmeticCaculator{9
10 @Override11 public int add(int a, intb) {12 System.out.println(a +b);13 return a +b;14 }15
16 @Override17 public int sub(int a, intb) {18 return a -b;19 }20
21 @Override22 public int mul(int a, intb) {23 return a *b;24 }25
26 @Override27 public int div(int a, intb) {28 if (b == 0) {29 throw new NotZeroException("除数不能为空");30 }31 return a /b;32 }33 }
ArithmeticCaculatorImpl
③ Aspect 类
这个类中 需要注意的点就比较多了
我在其中只写了 @Before , @After 和 @AfterReturning
1.定义为@Aspect
2.定义@Pointcut 根据这个切点来描写方法
3.写其中的@Before 或者其他Aspect 方法
当我们调用JoinPoint 时 要 joinpoint 要选择org.aspectj.lang.JoinPoint
1 packagecom.myth.spring.aop.service.aspect;2
3 importjava.util.Arrays;4
5 importorg.aspectj.lang.JoinPoint;6 importorg.aspectj.lang.annotation.AfterReturning;7 importorg.aspectj.lang.annotation.Aspect;8 importorg.aspectj.lang.annotation.Before;9 importorg.aspectj.lang.annotation.Pointcut;10 importorg.springframework.stereotype.Component;11
12 @Component13 @Aspect14 /**
15 * 要在Config中配置@EnableAspectJAutoProxy(proxyTargetClass=true) 默认为false16 * joinpoint 要选择org.aspectj.lang.JoinPoint17 *18 */
19 public classLoggingAspect {20 @Pointcut("execution(* com.myth.spring.aop.service.impl.ArithmeticCaculatorImpl.*(int , int ))")21 public voidArithmeticPointCut() {}22
23 @Before("ArithmeticPointCut()")24 public voidbefore(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 voidafterReturn(JoinPoint joinPoint ,Object result) {37 String method =joinPoint.getSignature().getName();38 System.out.println("The method "+method+" ends with " +result);39 }40 }
LoggingAspect
④ Junit 类
关于Junit 类 可以关注 我的这篇帖子
http://www.cnblogs.com/mythdoraemon/p/7533553.html
代码如下:
1 packagecom.myth.spring.aop.test;2
3 import static org.junit.Assert.*;4
5 importorg.junit.Test;6 importorg.junit.runner.RunWith;7 importorg.springframework.beans.factory.annotation.Autowired;8 importorg.springframework.test.context.ContextConfiguration;9 importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;10
11 importcom.myth.spring.aop.config.ArithmeticCaculatorConfig;12 importcom.myth.spring.aop.service.IArithmeticCaculator;13 @RunWith(SpringJUnit4ClassRunner.class)14 @ContextConfiguration(classes=ArithmeticCaculatorConfig.class)15 public classTestAspect {16
17 @Autowired18 privateIArithmeticCaculator iArithmeticCaculator;19
20 @Test21 public voidtest() {22 iArithmeticCaculator.add(1, 2);23 }24
25 }
TestAspect
注解注入
注解注入大部分与JavaConfig 很像 , 其实就是把Java Config 的内容去不都写到了XML 中 罢了
结构如下:
我们跟上面的结构对应起来
① Java Config 这里应该是容器 applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?>
2
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.xsd7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
9
10
11
12
13
14
15
16
applicationContext.xml
② Service 类 与上面的代码一样 就不重复写了
③ Aspect 类 也与 前面的一样 这回我写的是Around 方法
Around 其实包括了其他4种切面方式
1 /**
2 * 对于环绕通知来说, 连接点的参数类型必须是 ProceedingJoinPoint . 它是 JoinPoint 的子接口, 允许控制何时执行,3 * 是否执行连接点.4 * 环绕通知必须有返回值,返回值为proceedingJoinPoint.proceed() 的结果5 *@paramproceedingJoinPoint6 */
7 @Around(value = "ArithmeticPointCut()")8 publicObject 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 returnresult;29 }
LoggingAspect#Around
④ Test 类
这回Test 类 跟上面的会有些不同
@ContextConfiguration(locations= {"classpath:applicationContext.xml"})
这样子加载ApplicationContext 资源
1 packagecom.myth.spring.aop.annotion.test;2
3 importorg.junit.Test;4 importorg.junit.runner.RunWith;5 importorg.springframework.beans.factory.annotation.Autowired;6 importorg.springframework.test.context.ContextConfiguration;7 importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;8
9 importcom.myth.spring.aop.annotion.service.IArithmeticCaculator;10
11 @RunWith(SpringJUnit4ClassRunner.class)12 @ContextConfiguration(locations= {"classpath:applicationContext.xml"})13 public classTestAspect {14 @Autowired15 privateIArithmeticCaculator iArithmeticCaculator;16
17 @Test18 public voidtestAdd() {19 iArithmeticCaculator.add(1, 2);20 }21
22 @Test23 public voidtestDiv() {24 iArithmeticCaculator.div(1, 0);25 }26 }
TestAspect
XML配置
XML 配置其实就是把所有的切面方法写到applicationContext中
这里就只放上xml 配置文件
1 <?xml version="1.0" encoding="UTF-8"?>
2
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.xsd7 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
9
10
11
12
13
14
15
16
17
18
19
20 id="pointcut"/>
21
22
23
24
25
26
27
28
29
30
31
32
applicationContext.xml
总结: 我是建议用第一种方法
首先界面干净清晰 不用太依赖于xml文件
其次 因为我这个案例比较小, 如果切面有很多 其他的写法会有很多切面在XML 中存在 对了 我们可以设置Order 属性来确定切面的顺序, 在 XML 中 我有表现出这种形式
当然了 每种写法都可以, 大家择其喜欢的吧