使用@AspectJ注解开发Spring AOP
1.选择连接点(join point)
spring是方法级别的AOP框架,而我们主要也是以某个类的某个方法作为连接点,用动态代理的理论来说,就是要拦截某个方法织入对应AOP通知。
public interface RoleService {
public void printRole(Role role);
}
@Component
public class RoleServiceImpl implements RoleService {
@Override
public void printRole(Role role) {
// TODO Auto-generated method stub
System.out.println("{id="+role.getId()+",roleName="+role.getRoleName()+",note="+role.getNote()+"}");
}
}
把
printRole方法作为AOP的连接点。用动态代理来说就是要为类RoleServiceImpl生成代理对象,然后拦截printRole方法,于是可以产生各种AOP通知方法。
2.创建切面
选择好连接点就可以创建切面了,用动态代理来说,它就如同一个拦截器。在Spring中用@Aspect注解一个类,IOC容器就会认为这是一个切面了。
@Aspect
public class RoleAspect {
@Pointcut("execution(* test1.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....");
}
}
@Pointcut定义一个切点。
4.测试AOP
//这个类是对Spring的Bean进行配置,采用的是注解方式
//表明这是个配置类,相当于xml文件
@Configuration
//开启动态代理,开启AOP
@EnableAspectJAutoProxy
//扫描这个包下的所有bean
@ComponentScan("test1")
public class AopConfig {
//表示创建一个bean,返回值类型表示bean的类型,方法名表示bean的id
@Bean
//生成一个切面实例
public RoleAspect getRoleAspect() {
return new RoleAspect();
}
}
//这个类是对Spring的Bean进行配置,采用的是注解方式
//表明这是个配置类,相当于xml文件
@Configuration
//开启动态代理,开启AOP
@EnableAspectJAutoProxy
//扫描这个包下的所有bean
@ComponentScan("test1")
public class AopConfig {
//表示创建一个bean,返回值类型表示bean的类型,方法名表示bean的id
@Bean
//生成一个切面实例
public RoleAspect getRoleAspect() {
return new RoleAspect();
}
}
结果:
before....
{id=1,roleName=role_name_1,note=note_1}
after....
afterReturning....
############
before....
after....
afterThrowing....
Exception in thread "main" java.lang.NullPointerException
显然切面的通知已经通过AOP织入约定的流程中了,这是可以使用AOP来处理一切需要切面的场景了。
环绕通知
是spring AOP中最强大的通知,它可以同时实现前置通知和后置通知,还保留了调度被代理对象原有方法的功能,强大而灵活,但如果不需要大量改变业务的逻辑,一般而言并不需要使用它。
@Around("print()")
public void around(ProceedingJoinPoint jp) {
System.out.println("around before....");
try {
//反射连接点/切点方法
jp.proceed();
}catch(Throwable e) {
e.printStackTrace();
}
System.out.println("around after....");
}
给通知传递参数
上面的通知除了around()方法之外都是无参的,其实是可以传递参数来使用的。比如:
@Before("execution(* test1.RoleServiceImpl.printRole(..))"+"&&args(role)")
public void before(Role role) {
System.out.println("before....");
}
引入
Spring AOP只是通过动态代理技术,把各类通知织入到它所约定的流程中,而事实上有时我们希望通过引入其他类的方法来得到更好的实现,这时就可以引入其他的方法了。
比如说引入一个方法检测printRole角色为空时不再打印:
public interface RoleVerifier {
public boolean verify(Role role);
}
public class RoleVerifierImpl implements RoleVerifier {
@Override
public boolean verify(Role role) {
return role!=null;
}
}
加入RoleVerifier到切面中:
//value="test1.RoleServiceImpl+":表示对RoleServiceImpl类进行增强,也就是在RoleServiceImpl中引入一个新的接口
//defaultImpl=RoleVerifierImpl.class:代表其默认的实现类
@DeclareParents(value="test1.RoleServiceImpl+",defaultImpl=RoleVerifierImpl.class)
public RoleVerifier roleVerifier;
ApplicationContext ctx=new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService=(RoleService)ctx.getBean(RoleService.class);
RoleVerifier roleVerifier=(RoleVerifier)roleService;
Role role=new Role();
role.setId(1);
role.setRoleName("role_name_1");
role.setNote("note_1");
if(roleVerifier.verify(role)) {
roleService.printRole(role);
}
System.out.println("############");
//测试异常通知
role=null;
if(roleVerifier.verify(role)) {
roleService.printRole(role);
}else {
System.out.println("空的");
}
只要Spring AOP让代理对象(RoleServiceImpl)挂到RoleService和RoleVerifier两个接口之下,那么就可以把对应的bean通过强制转换,让其在RoleService和RoleVerifier之间相互转换,调用各自的方法。