其他扩展可参考之前的文章:
1、深入理解Spring IOC之扩展篇(一)、自定义xml标签
2、深入理解Spring IOC之扩展篇(二)、BeanFactoryPostProcessor和BeanPostProcessor
3、深入理解Spring IOC之扩展篇(三)、InitializingBean、@PostConstruct、SmartInitializingSingleton
4、深入理解Spring IOC之扩展篇(四)、Aware接口
5、深入理解Spring IOC之扩展篇(五)、基于注解整合Spring框架
上篇我们基于Spring的各种组件和注解把我们的业务逻辑和Spring进行了集成,其中我们定义了很多我们自己的注解。在本篇我们也将定义我们自己的注解,但是目的和上篇不同的是,上篇目的自定义注解是为了集成,而这篇的自定义注解是为了增强。
在demo开始之前,我先简单说下Aop中的这几个重要概念:
连接点(Joinpoint):在程序执行过程中某个特定的点,比如类初始化前、类初始化后,方法调用前,方法调用后;
切点(Pointcut):所谓切点就是你所切取的类中的方法,比如你横切的这个类中有两个方法,那么这两个方法都是连接点,对这两个方法的定位就称之为切点;
增强(Advice):增强是织入到连接点上的一段程序,另外它还拥有连接点的相关信息;
目标对象(Target):增强逻辑的织入目标类,就是我的增强逻辑植入到什么位置;
引介(Introduction):一种特殊的增强,它可以为类添加一些属性喝方法;
织入(Weaving):织入就是讲增强逻辑添加到目标对象的过程;
代理(Proxy):一个类被AOP织入增强后,就会产生一个结果类,他是融合了原类和增强逻辑的代理类;
切面(Aspect):切面由切点和增强组成,他是横切逻辑定义和连接点定义的组成;
我们来看看具体的做法:
首先按照惯例,我们需要有我们自己的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyAspect {
}
然后是自己注解的处理类:
@Aspect
// 注意这里必须有Component注解
@Component
public class AspectHandler {
@Pointcut(value = "@annotation(com.example.demo.external6.MyAspect)")
public void myJoinPoint(){}
@Around(value = "myJoinPoint()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("around before....");
joinPoint.proceed();
System.out.println("around after....");
return new MyEntity();
}
@Before(value = "myJoinPoint()", argNames = "joinPoint")
public void before(JoinPoint joinPoint){
System.out.println("before ======");
MyEntity entity = (MyEntity)joinPoint.getArgs()[0];
System.out.println(entity.getId());
System.out.println("before ======");
}
@After(value = "myJoinPoint()", argNames = "joinPoint")
public void after(JoinPoint joinPoint){
System.out.println("after ==========");
MyEntity entity = (MyEntity)joinPoint.getArgs()[0];
System.out.println(entity.getId());
System.out.println("after ==========");
}
@AfterReturning(value = "myJoinPoint()", argNames = "joinPoint,r", returning = "r")
public void afterReturning(JoinPoint joinPoint,Object r){
System.out.println("afterReturning");
MyEntity r1 = (MyEntity)r;
System.out.println(r1.getId());
}
@AfterThrowing(value = "myJoinPoint()", argNames = "joinPoint,tx",throwing = "tx")
public void afterThrowing(JoinPoint joinPoint,Throwable tx){
System.out.println("afterThrowing");
System.out.println(tx.getMessage());
}
}
用作测试的业务代码
@Component
public class TestService{
@MyAspect
public MyEntity doService(MyEntity entity){
// if(entity.getId() == 1){
// throw new RuntimeException("挂了");
// }
entity.setId(2);
System.out.println("do service");
return new MyEntity();
}
}
简单构造一个实体类:
@Data
public class MyEntity {
private int id;
}
配置类
@Configuration
@ComponentScan(basePackages = "com.example.demo.external6")
@EnableAspectJAutoProxy
public class Config {
}
注意EnableAspectJAutoProxy其中一个参数是expose-proxy,默认为false,设置为true之后你可以在被代理的类中使用AopUtil.currentProxy()方法来获取当前类的代理。另外一个参数proxyTargetClass是为你的类设置什么样的代理方式,我们知道spring aop当你的类实现了接口的时候,它会为你使用jdk动态代理,而当没有实现接口的时候会使用cglib代理,当我们把proxyTargetClass设置为true的时候,不论什么时候,都会使用cglib代理。
测试代码:
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);
TestService testService = (TestService) annotationConfigApplicationContext.getBean("testService");
MyEntity a = new MyEntity();
a.setId(1);
testService.doService(a);
}
输出结果:
我们其实可以轻松看到执行顺序。然后我们再测试测试异常的结果,测试代码其实就是把TestService中的异常放开就行。
测试结果:
两个测试结果均符合我们的预期。