Spring aop<4>

AOP(Aspect Oriented Programing)
面向切面编程采用横向抽取机制,以取代传统的纵向继承体系的重复性代码(如性能监控/事务管理/安全检查/缓存实现等)

Spring 实现AOP方式有cglib和Java动态代理,默认实现方式为Java动态代理。

AOP相关术语

  • Joinpoint:连接点 指那些被拦截到的点.在Spring中,这些点指方法(因为Spring只支持方法类型的连接点).
  • Pointcut:切入点 指需要(配置)被增强的Joinpoint
  • Advice:通知/增强 指拦截到Joinpoint后要做的操作.通知分为前置通知/后置通知/异常通知/最终通知/环绕通知等.
  • Aspect:切面 切入点和通知的结合.
  • Target:目标对象 需要被代理(增强)的对象.
  • Proxy:代理对象 目标对象被AOP 织入 增强/通知后,产生的对象.
  • Weaving:织入 指把增强/通知应用到目标对象来创建代理对象的过程(Spring采用动态代理织入,AspectJ采用编译期织入和类装载期织入).
  • Introduction: 引介 一种特殊通知,在不修改类代码的前提下,可以在运行期为类动态地添加一些Method/Field(不常用).
  1. 引入图中的包


    Spring aop<4>_第1张图片
    image.png
  2. 在spring配置文件中配置




  1. 使用@Aspect定义一个切面类
@Aspect
public class TestAspectModel {
...
}
  1. 定义一个需要增强的类
@Component(value = "firstaspect")
public class Firstaspect {
    public void noReturn() {
        System.out.println("执行方法noReturn()");
    }
    public String hasReturn(String argument) {
        System.out.println("执行方法hasReturn()"+argument);
        return " (hasReturn方法执行返回值) "+argument;
    }

    public void throwException() {
        System.out.println("执行方法throwException()");
        throw new IllegalArgumentException("i am a runtime exception");
    }
}
  1. 定义增强方法
    增强方式有:

@Before
@AfterReturning
@AfterThrowing
@After
@Around

5.1 @Before

@Aspect
public class TestAspectModel {

    @Before("execution(* com.lq.play.model.*.*(..))")
    public void before() {
        System.out.println("Before!");
    }
}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {

    @Autowired
    private Firstaspect firstaspect;

    @Test
    public void test() throws Exception {
        firstaspect.hasReturn("");
    }
}

执行结果:

Before!
执行方法hasReturn()

@Before 增强只能在目标方法执行之前织入增强,如果需要阻止目标方法执行,可通过抛出一个异常来实现。
5.2 @AfterReturning
@AfterReturning增强方法将在目标方法正常完成后被织入。
@AfterReturning注解有两个属性:

  • pointcut/value:两个属性作用相同,既可以是一个已有的切入点,也可以是一个切入点表达式,当指定了pointcut属性之后,value值将被覆盖。
  • returning:指定一个形参名,该形参可用于访问方法返回值。在Advice方法中可以指定形参的类型,会限制目标方法必须返回指定类型的值或者没有返回值。
    在TestAspectModel 中增加一个织入方法:
@AfterReturning(returning = "returnValue",pointcut = "execution(* com.lq.play.model.*.*(..))")
//@AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
public void afterReturning(JoinPoint joinPoint,Object returnValue) {
    System.out.println("afterReturning!");
    System.out.println("afterReturning!: 目标方法返回值:"+returnValue);
    System.out.println("afterReturning! 被织入增强处理的目标方法为:"+joinPoint.getSignature().getName());
    System.out.println("afterReturning! 被织入增强处理的目标方法的参数为:"+joinPoint.getArgs());
    System.out.println("afterReturning! 被织入增强处理的目标对象为:" +joinPoint.getTarget());
}

执行结果:

执行方法hasReturn()
afterReturning!
afterReturning!: 目标方法返回值: (hasReturn方法执行返回值)
afterReturning! 被织入增强处理的目标方法为:hasReturn
afterReturning! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@2a8448fa
afterReturning! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@6f204a1a

5.3 @AfterThrowing
@AfterThrowing增强方法将在目标方法正常完成后被织入。
@AfterThrowing注解有两个属性:

  • pointcut/value:两个属性作用相同,既可以是一个已有的切入点,也可以是一个切入点表达式,当指定了pointcut属性之后,value值将被覆盖。
  • throwing:指定一个形参名,该形参可用于访问目标方法抛出的异常。在Advice方法中可以指定形参的类型,会限制目标方法必须抛出指定类型的异常。
    向TestAspectModel增加织入方法:
 @AfterThrowing(throwing = "ex",pointcut = "execution(* com.lq.play.model.*.*(..))")
    public void afterThrowing(IllegalArgumentException ex) {
        System.out.println("afterThrowing:"+ex);
    }

向Firstaspect添加抛异常方法

 public void throwException() {
        System.out.println("执行方法throwException()");
        throw new IllegalArgumentException("i am a runtime exception");
    }

测试用例

@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {

    @Autowired
    private Firstaspect firstaspect;

    @Test
    public void test() throws Exception {
//        firstaspect.hasReturn("");
        firstaspect.throwException();
    }
}

返回结果:

执行方法throwException()
afterThrowing:java.lang.IllegalArgumentException: i am a runtime exception

java.lang.IllegalArgumentException: i am a runtime exceptionat com.lq.play.model.Firstaspect.throwException(Firstaspect.java:22)at com.lq.play.model.Firstaspect$$FastClassBySpringCGLIB$$89d307c5.invoke()at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)...

5.4 @After
@AfterReturning增强处理在目标方法成功执行后才会被织入。
@After增强处理不管目标方法成功与否,它都会被织入,和异常捕捉的finally关键字类似。
增强处理类:
```java
@Aspect
public class TestAspectModel {

    @After("execution(* com.lq.play.model.*.*(..))")
    public void after() {
        System.out.println("After!");
    }

    @AfterReturning(returning = "returnValue", pointcut = "execution(* com.lq.play.model.*.*(..))")
    public void afterReturning(JoinPoint joinPoint, Object returnValue) {
        System.out.println("afterReturning!");
        System.out.println("afterReturning!: 目标方法返回值:" + returnValue);
        System.out.println("afterReturning! 被织入增强处理的目标方法为:" + joinPoint.getSignature().getName());
        System.out.println("afterReturning! 被织入增强处理的目标方法的参数为:" + joinPoint.getArgs());
        System.out.println("afterReturning! 被织入增强处理的目标对象为:" + joinPoint.getTarget());
    }
}

测试用例

@ContextConfiguration(locations = {"classpath:config/spring/spring-servlet.xml"})
public class ApplicationTest {

    @Autowired
    private Firstaspect firstaspect;

    @Test
    public void test() throws Exception {
//        firstaspect.hasReturn("");
        firstaspect.throwException();
    }
}

返回结果

执行方法throwException()
After!
java.lang.IllegalArgumentException: i am a runtime exception
at com.lq.play.model.Firstaspect.throwException(Firstaspect.java:22)at com.lq.play.model.Firstaspect$$FastClassBySpringCGLIB$$89d307c5.invoke()at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

执行了@After增强,没有执行@AfterReturning

5.6 @Around
这个是功能最强大的增强处理,既可以在执行方法前织入增强动作,也可以在执行目标方法之后织入增强动作。

  • @Around可以决定目标方法在什么时候执行,如何执行,甚至可以组织目标方法的执行。
  • @Around可以改变执行目标方法的参数值,也可以改标执行目标方法之后的返回值。
  • @Around增强方法的第一个参数必须是ProceedingJoinPoint类型(至少包含一个形参),如果程序没有调用ProceedingJoinPoint 参数的proceed方法,则目标方法不会被执行。
  • 调用ProceedingJoinPoint 参数的proceed方法时,可以传入一个Object[]对象作为参数,该数组中的值将被传入目标方法为执行方法的实参。
    织入方法:
@Aspect
public class TestAspectModel {
    @Around("execution(* com.lq.play.model.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{

        System.out.println("around!");
        System.out.println("around! 被织入增强处理的目标方法为:"+pjp.getSignature().getName());
        System.out.println("around! 被织入增强处理的目标方法的参数为:"+Arrays.toString(pjp.getArgs()));
        System.out.println("around! 被织入增强处理的目标对象为:" +pjp.getTarget());
        System.out.println("around!");
        Object[] args = pjp.getArgs();
        if (args != null && args.length > 0) {
            args[0]="around 增加的参数前缀"+args[0];
        }

        Object returnValue =  pjp.proceed(args);

        return "around 增加的执行后前缀"+(String)returnValue;
    }
}

目标方法

   public String hasReturn(String argument) {
        System.out.println("执行方法hasReturn()"+"传入的参数值:"+argument);
        return "(hasReturn方法执行返回值)"+argument;
    }

测试用例:

   System.out.println(firstaspect.hasReturn(""));

执行结果:

around!
around! 被织入增强处理的目标方法为:hasReturn
around! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@533bda92
around! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@304bb45b
around!
执行方法hasReturn()传入的参数值:around 增加的参数前缀
around 增加的执行后前缀(hasReturn方法执行返回值)around 增加的参数前缀

五个增强都加入执行结果

around!
around! 被织入增强处理的目标方法为:hasReturn
around! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@7bd7d6d6
around! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@43f02ef2
around!
Before!
执行方法hasReturn()传入的参数值:around 增加的参数前缀
After!
afterReturning!
afterReturning!: 目标方法返回值:around 增加的执行后前缀(hasReturn方法执行返回值)around 增加的参数前缀
afterReturning! 被织入增强处理的目标方法为:hasReturn
afterReturning! 被织入增强处理的目标方法的参数为:[Ljava.lang.Object;@5b7a7f33
afterReturning! 被织入增强处理的目标对象为:com.lq.play.model.Firstaspect@43f02ef2

执行了

  • @Around
  • @Before
  • 目标方法
  • @After
  • @AfterReturning
  1. 访问目标方法的参数
    在增强方法里第一个参数定义为JoinPoint类型(ProceedingJoinPoint 即为JoinPoint子类),JoinPoint代表了织入增强处理的连接点。JoinPoint包含如下几个方法。
  • Object[] getArgs():返回目标方法执行时的参数。
  • Signature getSignature():返回被增强的方法的相关信息。
  • Object getTarget():返回被织入增强处理的目标对象。
  • Object getThis(): 返回AOP框架为目标对象生成的代理对象。

示例如@Around的增强方法:

 @Around("execution(* com.lq.play.model.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {

        System.out.println("around!");
        System.out.println("around! 被织入增强处理的目标方法为:" + pjp.getSignature().getName());
        System.out.println("around! 被织入增强处理的目标方法的参数为:" + Arrays.toString(pjp.getArgs()));
        System.out.println("around! 被织入增强处理的目标对象为:" + pjp.getTarget());
        System.out.println("around!");
        Object[] args = pjp.getArgs();
        if (args != null && args.length > 0) {
            args[0] = "around 增加的参数前缀" + args[0];
        }

        Object returnValue = pjp.proceed(args);

        return "around 增加的执行后前缀" + (String) returnValue;
    }

可以指定织入优先级

  • 让切面类实现org.springframework.core.Ordered接口,实现该接口值需要实现一个int getOrder()方法,该方法返回值越小,则优先级越高。
  • 直接使用@Order注解来修饰一个切面类,指定一个int类型的value属性,该方法返回值越小,则优先级越高。

可以为表达式指定参数,如下:

 @AfterReturning(returning = "returnValue", pointcut = "execution(* com.lq.play.model.*.*(..))&&args(arg1)")
//@AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
    public void afterReturning(JoinPoint joinPoint, Object returnValue,String arg1) {}
  1. 定义切入点
    当多个表达式相同时,可以定义一个
 @Pointcut("execution(* com.lq.play.model.*.*(..))")
 public void myPointCut() {
 }

然后其它引入即可

 @AfterReturning(returning = "returnValue",pointcut = "myPointCut()")
  1. 也可以使用xml配置
  
    
    
        
        
        
        
            
            
            
            
            
        
    

有如下三个属性:

  • id定义改切面的标识名

  • ref用于将ref属性所引用的普通Bean转换为切面Bean.

  • order:指定该切面Bean的优先级,值越小,优先级越高。

这几个元素都不支持子元素,但有如下属性:

  • pointcut 织入点表达式
  • pointcut-ref 和pointcut只能有一个,指定一个pointcut
  • method 指定将该方法转为增强处理方法
  • throwing 指定抛出的异常参数
  • returning 目标方法返回值

你可能感兴趣的:(Spring aop<4>)