引言:
Spring——AspectJ
AspectJ
一个基于Java语言开发的AOP框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.2.4.RELEASE</version> </dependency>
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency>
|
AspectJ
依赖于原本的AOP,所以需要以上四个
然后再xml文件中配置
1 2
| <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
|
现在我们就能开始愉快的学习AspectJ了
注解方式使用AspectJ
AspectJ同样提供了xml
和注解两种方式,我们先来学习注解方式
AspectJ提供的不同通知
@Before
前置通知 相当于BeforeAdvice
@AfterReturning
后置通知 相当于AfterReturningAdvice
@Around
环绕通知 相当于MethodInterceptor
@AfterThrowing
异常抛出通知 相当于ThrowAdvice
@After
最终通知,不论是否发生异常,该通知都会执行
@DeclareParents
引介通知(暂不介绍)
通知中定义切点
语法:
1 2
| execution(<访问修饰符> ? <返回类型> <方法名> (<参数>) <异常>)
|
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| execution(public * *(..))
execution(* com.xxx.xxx.*(..))(不包含子包) execution(* com.xxx.xxx..*(..))(包含子包,多一个点)
execution(* com.xxx.xxx.AnClass.*(..))
execution(* com.xxx.xxx.AnInterface+.*(..))(这里使用了+号表示所有子类)
execution(* save*(..))
|
前置、后置通知
1 2 3 4 5 6
| <aop:aspectj-autoproxy />
<bean id="productDao" class="com.dongwenhao.demo11.ProductDao" />
<bean id="myAspectAnno" class="com.dongwenhao.demo11.MyAspectAnno"></bean>
|
切点
1 2 3 4 5
| public class ProductDao { public void save(){ System.out.println("保存商品"); } }
|
切面
1 2 3 4 5 6 7 8 9 10 11
| @Aspect public class MyAspectAnno { @Before(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") public void before(){ System.out.println("前置通知"); } @AfterReturning(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") public void afterReturning(){ System.out.println("后置通知"); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:AspectJconfig.xml") public class SpringTest11 { @Resource(name = "productDao") private ProductDao productDao;
@Test public void test(){ productDao.save(); } }
|
前置通知可以在方法中传入切入点对象,后置通知可以传入方法的返回值对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Aspect public class MyAspectAnno { @Before(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") public void before(JoinPoint joinPoint){ System.out.println("前置通知" + joinPoint); } @AfterReturning(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))", returning = "result") public void afterReturning(Object result){ System.out.println("后置通知" + result); } }
|
环绕通知
1 2 3 4 5 6 7
| @Around(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕方法执行前"); Object proceed = joinPoint.proceed(); System.out.println("环绕方法执行后"); return proceed; }
|
环绕通知注解@Around
,带有一个ProceedingJoinpoint
,这个对象有一个方法proceed
,如果不调用proceed
方法,目标方法将会被拦截
异常抛出通知
1 2 3 4 5
| @AfterThrowing(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))",throwing = "e") public void afterThrowing(Throwable e){ System.out.println("异常抛出通知" + e.getMessage()); }
|
正常情况下,异常抛出通知不会执行,只有当异常出现时,这个方法才会调用
可以传入一个Throwable
对象,获得异常的信息
最终通知
无论方法发生了什么,甚至有异常抛出,这个方法总会被执行
1 2 3 4
| @After(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") public void after(){ System.out.println("最终通知"); }
|
@Pointcut
为切点命名
1 2 3 4 5 6 7
| @Pointcut(value = "execution(* com.dongwenhao.demo11.ProductDao.*(..))") private void myPointcut(){}
@Before(value = "myPointcut()") public void before(JoinPoint joinPoint){ System.out.println("前置通知" + joinPoint); }
|
为了简化开发,切入点不需要一个一个配置,我们可以使用这样的方式,配置一个切入点
格式一般都是这样
1 2
| @Pointcut(value="execution(...)") private void pointcut(){}
|
XML
方式使用AspectJ
这次我们写一个接口实现类的切点
接口
1 2 3
| public interface CustomerDao { public void save(); }
|
实现类
1 2 3 4 5 6
| public class CustomerImpl implements CustomerDao{ @Override public void save() { System.out.println("保存数据"); } }
|
切面
1 2 3 4 5
| public class MyAspectXml { public void before(){ System.out.println("前置通知"); } }
|
配置
1 2 3 4 5 6 7 8 9 10
| <bean id="customerDao" class="com.dongwenhao.demo12.CustomerImpl" />
<bean id="aspectXml" class="com.dongwenhao.demo12.MyAspectXml"></bean>
<aop:config> <aop:pointcut id="pointcut1" expression="execution(* com.dongwenhao.demo12.CustomerDao.*(..))" /> <aop:aspect ref="aspectXml"> <aop:before method="before" pointcut-ref="pointcut1"></aop:before> </aop:aspect> </aop:config>
|
这里以前置通知示例,其他通知同理可以写出