Spring AOP 基本概念 spring

上文讲述了AOP的一些基本概念,下面来讲讲SpringAOP的应用。 ## Joinpoint ## 说到Joinpoint其实需要明确的是什么样的类可以作为一个Joinpoint?在AOP的Joinpoint可以支持需要的类型:构造方法的调用、字段的设置和获取、方法的调用和方法的执行等。 但是在SpringAOP中只支持方法的执行(Method Execution)

Pointcut

前面说了Pointcut的作用就是用来找到系统中的Joinpot。而SpringAOP提供了一系列查找Joinpoint的方法。下面将就各个做简单的介绍,在这之前先就Spring中Pointcut做个介绍。在Spring中Pointcut 是以一个叫Pointcut的接口来作为最顶层的抽象:

public interface Pointcut {

	ClassFilter getClassFilter();

	MethodMatcher getMethodMatcher();

	Pointcut TRUE = TruePointcut.INSTANCE;

}

这里ClassFilter和MethodMatcher主要用来匹配植入对象的类和方法。也就是说如果是对类匹配,我们就需要实现ClassFilter,我们来看看:

public interface ClassFilter {

	boolean matches(Class<?> clazz);

	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

很明显,如果我们需要对指定的类做匹配,那么方法matches必须返回true,如下方法会匹配Foo:

public boolean matches(Class<?> clazz){
	return Foo.class.equals(clazz);
}

接下来是MethodMatcher,之前已经说过SpringAOP主要针对方法的执行进行,那么这个就显得特别重要了。

public interface MethodMatcher {

	boolean matches(Method method, Class<?> targetClass);

	boolean isRuntime();

	boolean matches(Method method, Class<?> targetClass, Object[] args);

	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

MethodMatcher中提供了两个重载的matches方法,这两个方法分别用在什么场景呢?很明显后者多了args,根据我们的经验这就是方法的参数。是的,后者是可以获取方法的参数。那么怎样区分呢?这就是方法 isRuntime()的作用,如果返回true将调用后者,此时你能够获取方法的参数,也许你能够对方法参数进行某些检验。如:

public void doSomething(String name, int age)

比如上面的方法,传入了两个参数。你可能需要对参数做一些校验才能被执行: return "robin".equals(name) && age > 20。而这被称为DynamicMethodMatcher,由于每次都需要对方法参数进行校验,存在一定的效率影响,同时由于参数的不确定性也不能被缓存。 而另一种叫做StaticMethodMatcher,主要是在isRuntime()返回false时被调用,这也是比较常见的情况,由于不需要每次都去校验方法参数,在很多框架内部对其进行了缓存以提高性能。
下图是常见的Pointcut类图

下面对常见的几种Pointcut进行一些说明:
1、NameMatchMethodPointcut
根据类名可以很容易看出,这是根据方法名来与Jointcut的方法匹配,可以参考以下几种配置:

    NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
    nameMatchMethodPointcut.addMethodName("sayHi");//匹配sayhi
    nameMatchMethodPointcut.addMethodName("greet");//匹配greet
    nameMatchMethodPointcut.setMappedName("meth*");//匹配以meth开头的方法
    nameMatchMethodPointcut.setMappedNames(new String[]{"*do*", "meth*end*"});//匹配多个方法,支持简单的模糊匹配

更多实例见这里
2、JdkRegexpMethodPointcut
采用JDK的正则匹配

    JdkRegexpMethodPointcut jdkRegexpMethodPointcut = new JdkRegexpMethodPointcut();
    jdkRegexpMethodPointcut.setPattern(".*say*.*");
    jdkRegexpMethodPointcut.setPatterns(new String[]{});

更多实例见这里

3、AnnotationMatchingPointcut
AnnotationMatchingPointcut通过申明相应的Annotation来进行拦截,具体可见其api说明。 首先声明了需要关注的annotation:

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MonitorAround {

}

在测试用例中,需要指明:

//具体使用哪个构造函数根据需要来定
AnnotationMatchingPointcut pc = new AnnotationMatchingPointcut(null, MonitorAround.class);

更多实例见这里

4、ComposablePointcut
ComposablePointcut是可以对多个Pointcut进行逻辑运算的Pointcut,提供了”并”和”交”两种方式:

    ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE, new FooMethodMatcher());
    //新增一个Pointcut:两个必须都满足
    pc.intersection(new BarMethodMatcher());
    
    //新增一个Pointcut:两个只需要其中一个为TRUE即可
    pc.union(new BarMethodMatcher());

这里自定义了两个Pointcut继承StaticMethodMatcher

public class FooMethodMatcher extends StaticMethodMatcher {

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return "sayHi".equals(method.getName());
    }

}

更多实例见这里

5、ControlFlowPointcut
ControlFlowPointcut简单的说是空值方法调用,比如有方法foo(),只有在方法method()调用是才被拦截,那么就是ControlFlowPointcut使用的场景了

	//这里只有ControlFlowPointcutTest中的method1调用目标方法时才会进行拦截
    ControlFlowPointcut controlFlow = new ControlFlowPointcut(ControlFlowPointcutTest.class, "method1");

更多实例见这里

Advice

Advice是Pointcut植入到Joinpoint处运行逻辑,通常可以区分为BeforeAdvice、AfterAdvice等。下面是UML类图

下面就几种常见的Advice做简单的介绍
1、BeforeAdvice
在SpringAOP中要实现BeforeAdvice需要实现接口MethodBeforeAdvice,定义如下:

public interface MethodBeforeAdvice extends BeforeAdvice {
	
	void before(Method method, Object[] args, Object target) throws Throwable;

}

BeforeAdvice适合在方法执行前做一些必要的处理,如权限校验、参数校验或者进行必要的初始化准备等,但是不能获取到方法的返回值的。

2、AfterReturningAdvice
SpringAop中后处理通知需要实现接口AfterReturningAdvice,通过他我们可以获取到当前Joinpoint的返回值、方法、参数等。如下只是简单列出了方法的返回值:

public class MethodAfterAdviceSample implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("After Method " + method.getName() + "(), Return "
                + (returnValue == null ? "null" : returnValue));
    }

}

当然,这里是只有方法被正确执行后才会执行AfterReturningAdvice(如果抛出异常属于后面介绍的Advice)。而且也不能更改方法的返回值,适合的场景比如在进行某些操作后做进一步的校验

3、ThrowsAdvice
SpringAop采用ThrowsAdvice来描述AOP中的AfterThrowingAdvice:

public interface ThrowsAdvice extends AfterAdvice {

}

在这个接口中并没有任何方法,但是在实际实现时也是可以灵活处理,只是方法名必须是afterThrowing如:

public class AfterThrowAdviceSample implements ThrowsAdvice {

    //afterThrowing([Method], [args], [target], subclassOfThrowable) 
    public void afterThrowing(NullPointerException exception) {
        //
        System.out.println("Exception: " + exception.getClass().getName());
    }

    public void afterThrowing(Method method, Object[] args, Object target, ClassCastException exception) {
        System.out.println("Process Method: " + method.getName() + "() Exception: " + exception.getClass().getName());
    }

}

这里可以重载多个afterThrowing方法,最后的参数是捕获可能抛出的异常

4、AroundAdvice
AroundAdvice是功能强大的一个Advice了,在SpringAop中并没有单独处理,而是直接运用了AOP Alliance中的MethodInterceptor: 

public interface MethodInterceptor extends Interceptor {
	
    Object invoke(MethodInvocation invocation) throws Throwable;
}

与之前的Advice不一样的是这里方法有返回值,这就意味着可以影响Joinpoint方法的返回值。这也是应用很广泛的Advice,比如性能监控、日志记录等。

public class AroundAdviceSample implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Proceed " + invocation.getMethod().getName() + "()");
        Object returnValue = invocation.proceed();
        return ((Integer) returnValue) * 0.85;
    }
}

这里展示了方法改变返回结果的处理,这样的场景可能是在促销时对总价打折处理



blog comments powered by Disqus