Spring 使用AspectJ的三种方式

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
Error

更换了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 }
ArithmeticCaculatorConfig

② 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 }
IArithmeticCaculator
 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 }
ArithmeticCaculatorImpl

③ 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 }
LoggingAspect

 ④ 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 }
TestAspect 

注解注入

 注解注入大部分与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>
applicationContext.xml

② 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     }
LoggingAspect#Around

④ 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 }
TestAspect 

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>
applicationContext.xml

总结:  我是建议用第一种方法  

首先界面干净清晰 不用太依赖于xml文件

其次 因为我这个案例比较小, 如果切面有很多  其他的写法会有很多切面在XML 中存在  对了 我们可以设置Order 属性来确定切面的顺序, 在 XML 中 我有表现出这种形式

当然了  每种写法都可以, 大家择其喜欢的吧

原文地址:https://www.cnblogs.com/mythdoraemon/p/7565275.html