目录
一、AspectJ基本介绍
二、@AspectJ的通知类型
三、在通知中通过value属性定义切点
四、使用案例
1.首先创建目标类以及切面类
2.使用AspectJ进行AOP开发
①:@Before前置通知
②:@AfterReturing前置通知
③:@Around环绕通知
④:@AfterThrowing异常抛出通知
⑤:@After最终通知
五、使用@Pointcut 为切点命名
Aspectj是一个基于Java语言的AOP框架,由于传统AOP开发过于麻烦,Spring2.0后将其添加到自己体系中。
使用AspectJ需要导入Spring AOP 和 AspectJ相关的jar包
需要在spring包的基础上再添加如下jar包
org.springframework
spring-aop
5.1.7.RELEASE
aopalliance
aopalliance
1.0
org.aspectj
aspectjweaver
1.8.9
com.xyz.spring
spring_aspect
1.0-SNAPSHOT
applicationContext.xml配置文件
使用
@Before前置通知,在目标方法执行之前,相当于Beforeadvice
@Afterreturning后置通知,在目标方法执行之后,相当于Afterreturningadvice
@Around环绕通知,在目标方法执行前和执行后,相当于Methodinterceptor
@Afterthrowing异常抛出通知,相当于Throwadvice
@After最终final通知,不管是否异常,该通知都会执行
通过execution函数,可以定义切点的方法切入
语法:execution(<访问修饰符><返回类型><方法名>(参数)<异常>)
其中访问修饰符可以省略
举例:
1.匹配所有类public方法 :execution(public * *(..))
第一个public是访问修饰符,第一个*是返回类型任意,第二个*是任意方法名,(..)标识任意参数
2.匹配指定包下所有类方法:execution(* com.xyz.dao.*(..))
第一个*代表任意的返回类型,com.xyz.dao.*(..)代表dao包下的所有类的任意方法,但不包含子包
execution(* com.xyz.dao..*(..)) 这种写法dao后面两"."是包含dao包及其子包
3.匹配实现了特定接口的所有类方法 :execution(* com.xyz.dao.UserDao+.*(..))
4.匹配所有save开头的方法:execution(* save*(..))
创建目标类
public class ProductDao {
public void save(){
System.out.println("保存商品");
}
public void update(){
System.out.println("修改商品");
}
public void delete(){
System.out.println("删除商品");
}
public void findOne(){
System.out.println("查询某个商品");
}
public void findAll(){
System.out.println("查询所有商品");
}
}
创建创建切面类(需使用@Aspect进行标识)
@Aspect
public class MyAspectAnno {
public void before(){
System.out.println("前置通知");
}
}
在配置文件中将类配置
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.xyz.aspectj.demo1.ProductDao.*(..))")
public void before(){
System.out.println("前置通知");
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test {
@Resource(name = "ProductDao")
private ProductDao productDao;
@org.junit.Test
public void demo1(){
productDao.save();
productDao.delete();
productDao.findAll();
productDao.findOne();
productDao.update();
}
}
可以看到在每个方法前都执行了@Before指定的前置通知
在切面类中加上一个@AfterReturining后置通知,并且仅对ProductDao类的update方法起作用
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.xyz.aspectj.demo1.ProductDao.*(..))")
public void before(){
System.out.println("前置通知");
}
@AfterReturning(value = "execution(* com.xyz.aspectj.demo1.ProductDao.update(..))")
public void after(){
System.out.println("后置通知!!");
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test {
@Resource(name = "ProductDao")
private ProductDao productDao;
@org.junit.Test
public void demo1(){
productDao.save();
productDao.delete();
productDao.findAll();
productDao.findOne();
productDao.update();
}
}
后置通知还可以得到方法的返回值,此时将ProductDao方法的update方法改成有返回值的
修改切面类,在@AfterReturning中新增了一个参数为returning接收返回值,随意命名,但要保证与下面after方法中参数名一直
@Aspect
public class MyAspectAnno {
@Before(value="execution(* com.xyz.aspectj.demo1.ProductDao.*(..))")
public void before(){
System.out.println("前置通知");
}
@AfterReturning(value = "execution(* com.xyz.aspectj.demo1.ProductDao.update(..))",returning = "result")
public void after(Object result){
System.out.println("后置通知!!"+result);
}
}
可以看到测试结果,表明收到了返回值
around()方法的返回值就是目标方法的返回值,使用Object保证任意返回值都OK
around()方法中的ProceedingJoinPoint参数用来调用目标方法执行,当joinPoint.proceed()就相当于目标方法的执行
@Aspect
public class MyAspectAnno {
@Around(value = "execution(* com.xyz.aspectj.demo1.ProductDao.delete(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
Object proceed = joinPoint.proceed();
System.out.println("环绕后通知");
return proceed;
}
}
将目标方法修改为下图
测试结果
如果joinPoint.proceed()方法不执行,则目标方法也不会执行,这时就可以在这里加一下业务上的判断
使用该注解在目标方法发生异常时会触发,可以用于事务回滚。
相当于try catch的finally,无论目标方法如何都会执行
在案例4可以看出,添加切点时优点麻烦,每次都要写长长的一串,这时就可以使用@Pointcut简化
举例:下面是案例4的代码,其中execution(* com.xyz.aspectj.demo1.ProductDao.delete(..))表示对包下的delete方法进行环绕通知,如果此时该delete方法有多个通知就可以采用@Pointcut进行别名
@Aspect
public class MyAspectAnno {
@Around(value = "execution(* com.xyz.aspectj.demo1.ProductDao.delete(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
Object proceed = joinPoint.proceed();
System.out.println("环绕后通知");
return proceed;
}
}
下图是使用了@Pointcut将execution(* com.xyz.aspectj.demo1.ProductDao.delete(..))直接用myPointcut1()使用
@Aspect
public class MyAspectAnno {
@Around(value = "myPointcut1()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
Object proceed = joinPoint.proceed();
System.out.println("环绕后通知");
return proceed;
}
@Pointcut(value = "execution(* com.xyz.aspectj.demo1.ProductDao.delete(..))")
private void myPointcut1(){}
}