文章40 | 阅读 20143 | 点赞0
在前一讲中,我已讲过Spring使用基于AspectJ的XML配置文件的方式进行AOP开发,现在我就来讲讲怎样使用基于AspectJ的注解的方式进行AOP开发。
首先创建一个动态web项目,例如spring_demo03_aop,然后导入Spring框架相关依赖jar包,要导入哪些jar包呢?这里不废话,直接给出要导入的jar包。
首先,引入Spring的配置文件,在该文件中应引入aop约束,这样一开始applicationContext.xml文件的内容就应该是下面的样子。
<?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">
</beans>
然后,还要记得在src目录下引入Log4j的配置文件(log4j.properties),也就是日志记录文件,该文件内容如下:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c\:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
# error warn info debug trace
log4j.rootLogger= info, stdout
首先,在src目录下创建一个com.meimeixia.spring.demo03包,并在该包下创建一个名为OrderDao的类。
package com.meimeixia.spring.demo01;
public class OrderDao {
public void save() {
System.out.println("保存订单...");
}
public void update() {
System.out.println("修改订单...");
}
public void delete() {
System.out.println("删除订单...");
}
public void find() {
System.out.println("查询订单...");
}
}
然后,在Spring配置文件中对以上目标类进行配置。
现在有这样一个需求:我们想在OrderDao类的save方法执行之前,就简简单单地执行一个操作,那该咋怎呢?这时我们可以编写一个切面类,并在切面类中随便编写一个方法,例如下面的before方法。待会,我们就使用注解对该切面类进行增强,让切面类中的before方法在目标类的save方法执行之前执行。
package com.meimeixia.spring.demo01;
public class MyAspectAnno {
public void before() {
System.out.println("前置增强--------------------");
}
}
接着,在Spring配置文件中对以上切面类进行配置。
首先,我们需要在Spring的配置文件中开启注解的AOP开发。
然后,我们就可以在切面类上使用注解了。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
public void before() {
System.out.println("前置增强--------------------");
}
}
温馨提示:@Aspect注解用于定义切面类,@Before注解代表的是前置通知。
首先,在com.meimeixia.spring.demo01包下创建一个SpringDemo01的单元测试类,其内容如下,可以看出我们是通过Spring来整合JUnit进行单元测试的。
package com.meimeixia.spring.demo01;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/** * Spring的AOP的注解开发 * @author liayun * */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo01 {
@Resource(name="orderDao")
private OrderDao orderDao;
@Test
public void demo01() {
orderDao.save();//作断点调试,可以看到使用到的是Cglib的动态代理:CglibAopProxy
orderDao.update();
orderDao.delete();
orderDao.find();
}
}
然后,运行以上demo01单元测试方法,Eclipse控制台就会打印出如下内容。
Spring AOP注解开发入门之后,咱就来看一下AOP注解开发中的通知类型。
前置通知是指在目标方法执行之前进行操作。上面我演示的就是前置通知,这里不再赘述。
后置通知是指在目标方法执行之后进行操作。它除了可以获得切入点的信息以外,还可以获得方法的返回值。为了验证这一点,首先,将OrderDao类修改成下面这个样子(主要是修改了一下delete方法,让其返回一个字符串)。
package com.meimeixia.spring.demo01;
public class OrderDao {
public void save() {
System.out.println("保存订单...");
}
public void update() {
System.out.println("修改订单...");
}
public String delete() {
System.out.println("删除订单...");
return "删除订单成功";
}
public void find() {
System.out.println("查询订单...");
}
}
然后,在MyAspectAnno切面类中添加一个afterReturning方法,并在该方法上使用@AfterReturning注解,告诉Spring要在OrderDao类中的delete方法上应用后置通知。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
// @Before(value="MyAspectAnno.pointcut2()")
public void before() {
System.out.println("前置增强--------------------");
}
//后置通知
@AfterReturning(value="execution(* com.meimeixia.spring.demo01.OrderDao.delete(..))", returning="result")
public void afterReturning(Object result) {//这儿的result一定得跟returning="result"中的result对应上
System.out.println("后置增强--------------------" + result);
}
}
最后,运行SpringDemo01单元测试类中的demo01方法,你就会看到Eclipse控制台打印出了如下内容。
环绕通知是功能最强的一个通知,它是指在目标方法执行之前和之后进行操作。很重要的一点就是它可以阻止目标方法的执行。为了验证这一点,我们可以在MyAspectAnno切面类中添加一个如下的around方法,并在该方法上使用@Around注解,告诉Spring要在OrderDao类中的update方法上应用环绕通知。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
public void before() {
System.out.println("前置增强--------------------");
}
//后置通知
@AfterReturning(value="execution(* com.meimeixia.spring.demo01.OrderDao.delete(..))", returning="result")
public void afterReturning(Object result) {//这儿的result一定得跟returning="result"中的result对应上
System.out.println("后置增强--------------------" + result);
}
//环绕通知
@Around(value="execution(* com.meimeixia.spring.demo01.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强--------------------");
Object obj = joinPoint.proceed();//执行我们的目标方法,它会返回一个Object
System.out.println("环绕后增强--------------------");
return obj;
}
}
接着,运行SpringDemo01单元测试类中的demo01方法,你就会看到Eclipse控制台打印出了如下内容。
异常抛出通知是指在程序出现异常的时候而进行的操作,而且它还可以获得异常的信息。异常抛出通知能想到的一个应用场景就是在事务管理的时候会用到。为了验证这一点,首先,修改一下OrderDao类中的find方法,使其抛出一个除零异常。
package com.meimeixia.spring.demo01;
public class OrderDao {
public void save() {
System.out.println("保存订单...");
}
public void update() {
System.out.println("修改订单...");
}
public String delete() {
System.out.println("删除订单...");
return "删除订单成功";
}
public void find() {
System.out.println("查询订单...");
int i = 1 / 0;
}
}
然后,在MyAspectAnno切面类中添加如下一个afterThrowing方法,并在该方法上使用@AfterThrowing注解,告诉Spring要在OrderDao类中的find方法上应用异常抛出通知。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
public void before() {
System.out.println("前置增强--------------------");
}
//后置通知
@AfterReturning(value="execution(* com.meimeixia.spring.demo01.OrderDao.delete(..))", returning="result")
public void afterReturning(Object result) {//这儿的result一定得跟returning="result"中的result对应上
System.out.println("后置增强--------------------" + result);
}
//环绕通知
@Around(value="execution(* com.meimeixia.spring.demo01.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强--------------------");
Object obj = joinPoint.proceed();//执行我们的目标方法,它会返回一个Object
System.out.println("环绕后增强--------------------");
return obj;
}
//异常抛出通知
@AfterThrowing(value="execution(* com.meimeixia.spring.demo01.OrderDao.find(..))", throwing="e")
public void afterThrowing(Throwable e) {//这儿的e一定得跟throwing="e"中的e对应上
System.out.println("异常抛出增强--------------------" + e.getMessage());
}
}
最后,运行SpringDemo01单元测试类中的demo01方法,你就会看到Eclipse控制台打印出了如下内容。
无论目标方法是否出现异常,最终通知都会执行。此时,我们已经知道了OrderDao类中的find方法(也即目标方法)抛出了一个除零异常。现在,咱就是要看看find方法(也即目标方法)出现了异常,最终通知会不会执行。为了验证这一点,我们在MyAspectAnno切面类中添加如下的一个after方法,并在该方法上使用@After注解,告诉Spring要在OrderDao类中的find方法上应用最终通知。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
public void before() {
System.out.println("前置增强--------------------");
}
//后置通知
@AfterReturning(value="execution(* com.meimeixia.spring.demo01.OrderDao.delete(..))", returning="result")
public void afterReturning(Object result) {//这儿的result一定得跟returning="result"中的result对应上
System.out.println("后置增强--------------------" + result);
}
//环绕通知
@Around(value="execution(* com.meimeixia.spring.demo01.OrderDao.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强--------------------");
Object obj = joinPoint.proceed();//执行我们的目标方法,它会返回一个Object
System.out.println("环绕后增强--------------------");
return obj;
}
//异常抛出通知
@AfterThrowing(value="execution(* com.meimeixia.spring.demo01.OrderDao.find(..))", throwing="e")
public void afterThrowing(Throwable e) {//这儿的e一定得跟throwing="e"中的e对应上
System.out.println("异常抛出增强--------------------" + e.getMessage());
}
//最终通知
@After(value="execution(* com.meimeixia.spring.demo01.OrderDao.find(..))")
public void after() {
System.out.println("最终增强--------------------");
}
}
接着,运行SpringDemo01单元测试类中的demo01方法,你就会看到Eclipse控制台打印出了如下内容。
从上面输出的结果中,我们就证明了即使目标方法出现了异常,最终通知也会执行。
假设切面类里面有很多种通知,但它们都需要作用于目标类的同一个方法上,比如说find方法,假如说我有一天不想作用在find方法上了,而想作用在save方法或者update方法上,那该咋办呢?这时,我们得到切面类里面一个一个改,如果切面类这里面的通知很多,那么我们是不是得改好几个啊!这势必会很麻烦!为了解决这种问题,咱可以使用@Pointcut注解来定义切入点。例如,咱可以在MyAspectAnno切面类里面定义如下一些切入点。
那么,我们怎么把之前写好的各种类型的通知应用在这些切入点上呢?也就是说MyAspectAnno切面类定义了以上一系列切入点之后,可以修改成下面这个样子。
package com.meimeixia.spring.demo01;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/** * 切面类(注解的切面类) * @author liayun * */
@Aspect //Spring会把这个类识别成一个切面
public class MyAspectAnno {
//@Before注解代表前置增强,还要告诉Spring在哪个类上的哪个方法上应用前置增强
@Before(value="MyAspectAnno.pointcut2()")
public void before() {
System.out.println("前置增强--------------------");
}
//后置通知
@AfterReturning(value="MyAspectAnno.pointcut4()", returning="result")
public void afterReturning(Object result) {//这儿的result一定得跟returning="result"中的result对应上
System.out.println("后置增强--------------------" + result);
}
//环绕通知
@Around(value="MyAspectAnno.pointcut3()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前增强--------------------");
Object obj = joinPoint.proceed();//执行我们的目标方法,它会返回一个Object
System.out.println("环绕后增强--------------------");
return obj;
}
//异常抛出通知
@AfterThrowing(value="MyAspectAnno.pointcut1()", throwing="e")
public void afterThrowing(Throwable e) {//这儿的e一定得跟throwing="e"中的e对应上
System.out.println("异常抛出增强--------------------" + e.getMessage());
}
//最终通知
@After(value="MyAspectAnno.pointcut1()")
public void after() {
System.out.println("最终增强--------------------");
}
/* * 这里面有一个问题:假设我这里面有很多种通知,但都需要作用于同一个方法上,比如说find()方法,假如说我有一天不想作用在find()方法上了, * 我想作用在save()方法或者update()方法上,咋办呢?你是不是得到这边一个一个改啊!那如果我这里面的通知很多,但都 * 作用于同一个方法上,你是不是得改好几个啊?很麻烦! * */
//切入点的注解
@Pointcut(value="execution(* com.meimeixia.spring.demo01.OrderDao.find(..))")
private void pointcut1() {}//该方法没有特殊的意义,私有就行
@Pointcut(value="execution(* com.meimeixia.spring.demo01.OrderDao.save(..))")
private void pointcut2() {}
@Pointcut(value="execution(* com.meimeixia.spring.demo01.OrderDao.update(..))")
private void pointcut3() {}
@Pointcut(value="execution(* com.meimeixia.spring.demo01.OrderDao.delete(..))")
private void pointcut4() {}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://liayun.blog.csdn.net/article/details/100527764
内容来源于网络,如有侵权,请联系作者删除!