现在使用@AspectJ注解的方式已经成为了主流。所以我用一个简单的例子来了解一下他的整个过程。
选择连接点
Spring是方法级别的AOP框架,我们主要是以某个类的某个方法作为连接点,用动态代理的理论来说,就是要拦截哪个方法织入对应AOP通知。
首先我们建一个接口:
package aop.service;
import game.bigLiZi.game.pojo.Role;
public interface RoleService {
public void printRole(Role role);
}
这个接口很简单,接下来提供一个实现类:
package aop.service.impl;
import aop.service.RoleService;
import game.bigLiZi.game.pojo.Role;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImpl implements RoleService {
@Override
public void printRole(Role role) {
System.out.println("{ id:"+role.getId()+"roleName:"+role.getRoleName()+
"note:"+role.getNote()+"}");
}
}
这个时候如果把printRole作为AOP的连接点,那么用动态代理的语言就是要为类RoleServiceImpl生成代理对象,然后拦截printRole方法,于是可以产生各种AOP通知方法。
创建切面
选择好了连接点就可以创建切面了,在Spring中只要使用@Aspect注解一个类,那么Spring IoC容器就会认为这是一个切面了。
package aop.aspect;
import org.aspectj.lang.annotation.*;
@Aspect
public class RoleAspect {
@Before("execution(* aop.service.impl.RoleServiceImpl.printRole(..))")
public void before(){
System.out.println("before..........");
}
@After("execution(* aop.service.impl.RoleServiceImpl.printRole(..))")
public void after(){
System.out.println("after..........");
}
@AfterReturning("execution(* aop.service.impl.RoleServiceImpl.printRole(..))")
public void afterReturning(){
System.out.println("afterReturning..........");
}
@AfterThrowing("execution(* aop.service.impl.RoleServiceImpl.printRole(..))")
public void afterThrowing(){
System.out.println("afterThrowing..........");
}
}
这里我们就要说一说AspectJ注解了:
- @Before:在被代理对象的方法前调用,前置通知。
- @Around:将被代理对象的方法封装起来,并用环绕通知取代它。 它将覆盖原有方法,但是允许你通过反射调用原有的方法。
- @After:在被代理对象的方法后调用, 后置通知。
- @AfterReturning:在被代理对象的方法正常返回后调用,返回通知,要求被代理对象的方法执行过程中没有发生异常。
- @AfterThrowing:在被代理对象的方法抛出异常后调用,异常通知,要求被代理对象的方法执行过程中返回异常。
上面那段代码的注解使用了对应的正则表达式,这些正则表达式是切点的问题,也就是要告诉Spring AOP,需要拦截什么对象的什么方法。
定义切点
上面代码在注解中定义了execution的正则表达式,Spring是通过这个正则表达式判断是否需要拦截你的方法,这个表达式是:
对这个表达式分析一下:
- execution:代表执行方法的时候会触发
- *:代表任意返回类型的方法
- aop.service.impl.RoleServiceImpl:代表类的全限定名
- printRole:被拦截方法名称
- (…):任意的参数
上面的表达式还有些简单,AspectJ的指示器还有很多,下面我列举几个:
- arg():限制连接点匹配参数为指定类型的方法
- execution:用于匹配连接点的执行方法
- this():限制连接点匹配AOP代理的Bean,引用为指定类型的类
- target:限制连接点匹配被代理对象为指定的类型
- within():限制连接点匹配的包
此外,上面的正则表达式需要重复书写多次,比较麻烦,这里我们可以使用@Pointcut来避免这个麻烦。代码如下:
package aop.aspect;
import org.aspectj.lang.annotation.*;
public class anotherAspect {
@Pointcut("execution(* aop.service.impl.RoleServiceImpl.printRole(..))")
public void print(){
}
@Before("print()")
public void before(){
System.out.println("before..........");
}
@After("print()")
public void after(){
System.out.println("after..........");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning..........");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing..........");
}
}
测试AOP
连接点、切面以及切点都有了,这个时候就可以编写测试代码来测试AOP的内容。
首先要对Spring的Bean进行配置,采用Java配置,代码如下:
package aop.config;
import aop.aspect.RoleAspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("aop")
public class AopConfig {
@Bean
public RoleAspect getRoleAspect(){
return new RoleAspect();
}
}
测试AOP流程
package aop.main;
import aop.config.AopConfig;
import aop.service.RoleService;
import game.bigLiZi.game.pojo.Role;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args){
ApplicationContext ctx=new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService=(RoleService)ctx.getBean(RoleService.class);
Role role=new Role();
role.setId(1L);
role.setRoleName("role_name_1");
role.setNote("role_note_1");
roleService.printRole(role);
System.out.println("########################");
role=null;
roleService.printRole(role);
}
}
在第二次打印之前,将role设置为null,这样是为了测试异常返回通知。
before..........
{ id:1roleName:role_name_1note:role_note_1}
after..........
afterReturning..........
########################
before..........
after..........
afterThrowing..........
Exception in thread "main" java.lang.NullPointerException