Spring AOP APIS

1:Pointcut API in Spring

(1):切点接口定义

org.springframework.aop.Pointcut接口是中心接口。用来将Advice(通知)定位到特定的类和方法

public interface Pointcut {
​
    ClassFilter getClassFilter();
​
    MethodMatcher getMethodMatcher();
​
}

ClassFilter接口用来决定切点作用的类。如果matches()方法总是返回true,所有的目标均匹配。


public interface ClassFilter {
​
    boolean matches(Class clazz);
}

MethodMatcher接口通常更重要。

public interface MethodMatcher {
​
    boolean matches(Method m, Class targetClass);
​
    boolean isRuntime();
​
    boolean matches(Method m, Class targetClass, Object[] args);
}

大多数MethodMatcher实现是静态的,意味着它们的isRuntime()返回false。由于这个原因,三参数的matches方法永远不会被调用。

`注意:

如果可能的话,让切点为静态,允许AOP框架缓存切点计算结果当AOP代理被创建的时候。

(2):切点操作

Spring支持切点操作有联合(union)和交集(intersection)。联合意味着方法任意一个匹配即可,交集

意味着所有切点都匹配。然而,使用AspectJ point expression通常更简便。

AspectJExpressionPointcut:AspectJ point expression

(3):便利切点实现

  • 静态切点(Static Pointcuts)

    静态切入点基于方法和目标类,不能考虑方法参数。对于大多数用途,静态切入点足够并且最好。首次调用方法,Spring只评估一次静态切入点。之后,无需再次使用每个方法调用来评估切入点。

  • 正则表达式切入点(Regular Expression Pointcuts)

    • JdkRegexpMethodPointcut :JDK支持的正则表达式。你可以设置正则表达式的列表,任何一个满足,即切点评估为true

  • 动态切点(Dynamic Pointcuts)

​ 动态切点,评估成本比静态成本高。考虑了方法参数和静态信息。这意味着必须使用每个方法调用来评估它们,并且不能缓存结果,因为参数会有所不同。

  • 控制流切点(Control Flow Pointcuts)

    和AspectJ的cflow类似。

(4):切点父类(Pointcut Superclasses)

由于static pointcuts是最有用的,StaticMethodMatcherPointcut。使用示例如下:


class TestStaticPointcut extends StaticMethodMatcherPointcut {
​
    public boolean matches(Method m, Class targetClass) {
        // return true if custom criteria match
    }
}

2:Advice API in Spring

(1):通知生命周期

每一个通知都是一个Spring Bean通知示例可以在所有通知对象之间共享

(2):Spring通知类型

Around Advice(环绕通知)

Spring最基本的通知是环绕通知。使用了method interception。类实现MethodInterceptor


public interface MethodInterceptor extends Interceptor {
    
    /**
     * Implement this method to perform extra treatments before and
     * after the invocation. Polite implementations would certainly
     * like to invoke {@link Joinpoint#proceed()}.
     * @param invocation the method invocation joinpoint
     * @return the result of the call to {@link Joinpoint#proceed()};
     * might be intercepted by the interceptor
     * @throws Throwable if the interceptors or the target object
     * throws an exception
     */
    Object invoke(MethodInvocation invocation) throws Throwable;
​
}
​
/**
 * Description of an invocation to a method, given to an interceptor
 * upon method-call.
 *
 * <p>A method invocation is a joinpoint and can be intercepted by a
 * method interceptor.
 */
public interface MethodInvocation extends Invocation {
​
    /**
     * Get the method being called.
     * <p>This method is a frienly implementation of the
     * {@link Joinpoint#getStaticPart()} method (same result).
     * @return the method being called
     */
    Method getMethod();
​
}
​
​
public interface Invocation extends Joinpoint {
​
    /**
     * Get the arguments as an array object.
     * It is possible to change element values within this
     * array to change the arguments.
     * @return the argument of the invocation
     */
    Object[] getArguments();
}
​
​
public interface Joinpoint {
​
    /**
     * Proceed to the next interceptor in the chain.
     * <p>The implementation and the semantics of this method depends
     * on the actual joinpoint type (see the children interfaces).
     * @return see the children interfaces' proceed definition
     * @throws Throwable if the joinpoint throws an exception
     */
    Object proceed() throws Throwable;
​
    /**
     * Return the object that holds the current joinpoint's static part.
     * <p>For instance, the target object for an invocation.
     * @return the object (can be null if the accessible object is static)
     */
    Object getThis();
​
    /**
     * Return the static part of this joinpoint.
     * <p>The static part is an accessible object on which a chain of
     * interceptors are installed.
     */
    AccessibleObject getStaticPart();
​
}
 

invoke()方法的MethodInvocation参数公开了被调用的方法,目标连接点,AOP代理和方法的参数。 invoke()方法应该返回调用的结果:连接点的返回值

Before Advice(前置通知)

简单的通知类型是前置通知。不需要MethodInvocation对象。它是在进入方法之前调用

前置通知主要优点不需要调用proceed()方法,因此不会无意中无法继续拦截链。


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

Spring API设计允许在通知之前提供字段,尽管通常的对象适用于字段拦截,但是Spring不太可能实现它。请注意,返回类型为void。在通知可以在连接点执行之前插入自定义行为但是不能更改返回值之前。如果before advice抛出异常,则会中止拦截器链进一步执行。异常传播回拦截器链。

返回所有方法的调用次数


public class CountingBeforeAdvice implements MethodBeforeAdvice {
​
    private int count;
​
    public void before(Method m, Object[] args, Object target) throws Throwable    {
        ++count;
    }
​
    public int getCount() {
        return count;
    }
}

Throws Advice(异常通知)

异常通知在返回连接点后,如果连接点跑出异常,则该通知被调用。ThrowsAdvice,该接口是一个标记接口,继承了AfterAdvice。示例如下:异常参数必须存在,其他参数像method,arguments是否存在,取决你是否需要。


public void afterThrowing(Exception ex){
​
}
public void afterThrowing(RemoteException){
​
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
​
}
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex){
​
}

注意

不要抛出与目标签名方法不兼容的未声明的已检查异常

后置返回通知(After Returning Advice)

后置返回通知需要实现`AfterReturningAdvice `。


public interface AfterReturningAdvice extends Advice {
​
    void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable;
}

后置返回通知可以访问返回值(但是它不能修改),被调用的方法,方法参数,目标对象.如果它抛出异常,

则抛出拦截器链而不是返回值。

统计所有成功调用但是没有抛出异常的方法次数


public class CountingAfterReturningAdvice implements AfterReturningAdvice {
​
    private int count;
​
    public void afterReturning(Object returnValue, Method m, Object[] args, Object target)
            throws Throwable {
        ++count;
    }
​
    public int getCount() {
        return count;
    }
}

引入通知(Introduction Advice)

引入通知需要一个IntroductionAdvisorIntroductionInterceptor,如下实现


public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
​
}
​
public interface DynamicIntroductionAdvice extends Advice {
​
    /**
     * Does this introduction advice implement the given interface?
     * @param intf the interface to check
     * @return whether the advice implements the specified interface
     */
    boolean implementsInterface(Class<?> intf);
​
}

invoke()方法继承了AOP aopalliance MethodInterceptor接口。如果调用的方法在引入的接口上,

则引入拦截器负责处理调用,它不能调用proceed()

引入通知不能适用于任意切点。它仅适用于类,而不是方法级别的。只能在IntroductionAdvisor中使用引入通知。


public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
​
    ClassFilter getClassFilter();
​
    void validateInterfaces() throws IllegalArgumentException;
}
​
public interface IntroductionInfo {
​
    Class[] getInterfaces();
}

仅仅含有class过滤逻辑。getInterfaces()返回切面引入的接口。validateInterfaces()方法被用于判断引入的接口是否能被IntroductionInterceptor配置。

DelegatingIntroductionInterceptor 被设计成代理一个引入给实际实现引入的接口。

3:The Advisor API in Spring

在Spring中,Advisor是一个仅含有一个通知对象和一个切点表达式对象关联切面

`DefaultPointcutAdvisor `是最通用advisor。

(2):使用ProxyFactoryBean创建AOP代理

在Spring中创建AOP代理的基本方法是使用ProxyFactoryBean。他可以完全控制切入点,任何适用通知以及它们顺序。

JavaBean属性

一些key属性继承自ProxyConfig

  • proxyTargetClass:如果代理的是目标类,而不是接口,该值为true。默认是false

  • optimize:控制是否将积极优化应用与通过CGLIB创建的代理。除非您完全了解相关AOP的优化,否则不应该轻易使用此设置。

  • frozen:如果代理配置被冻结,则不再允许更改配置。默认为false

  • exposeProxy:决定是否将当前代理对象暴露到ThreadLocal中,以便可以被目标对象访问。可以通过

AopContext.currentProxy()获取。

其他属性来自于ProxyFactoryBean

  • proxyInterfaces:代理的接口数组。如果没有被支持,则CGLIB代理被使用.

  • interceptorNames:拦截器数组(Advisor)。这些名字存在当前bean工厂中,包含祖先工厂。

  • singleton:决定工厂是否返回单例对象,默认为true

(3):JDK和CGLIB代理

  • 如果一个目标对象没有实现任何接口,则使用CGLIB代理。

  • 如果一个目标对象实现了任何一个接口,默认使用JDK代理

  • 如果一个目标对象实现了任何一个接口,但是proxyTargetClass属性为true,使用GGLIB代理。

(4):代理接口

interceptorNames属性持有一个列表,这个列表是拦截器(MethoInterceptor)或者(Advisor)

在当前工厂的bean names

注意:

您可能想知道为什么列表不包含bean引用。 原因是,如果ProxyFactoryBean的singleton属性设置为false,则它必须能够返回独立的代理实例. 如果任何顾问本身就是原型,则需要返回一个独立的实例,因此必须能够从工厂获得原型的实例. 持有引用是不够的.

(5):代理类

CGLIB代理的局限性

  • final方法不能被通知。由于它们不能被重写

  • Spring3.2,CGOLIB被重新打包到spring-core jar中。

(6):使用全局(Advisors)

通过在拦截器后附加星号,将所有与星号前面的部分匹配的bean名称的advisor程序添加到拦截器链中。如下所示:


<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="service"/>
    <property name="interceptorNames">
        <list>
            <value>global*</value>
        </list>
    </property>
</bean><bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>

(7):使用ProxyFactory创建AOP代理

一个目标对象,一个通知,一个顾问。

   @Bean
    public AspectJProxyFactory aspectJProxyFactory() {
        AspectJProxyFactory proxyFactoryBean = new AspectJProxyFactory(new UserService());
        String expression = "execution(* com.ley.springboot.UserService.*(..))";
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(expression);
        Advice advice = new UserServiceMethodBeforeAdvice();
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
        proxyFactoryBean.addAdvisor(advisor);
        return proxyFactoryBean;
    }
​
​
    @Bean(name = "proxyFactoryBean")
    public ProxyFactoryBean proxyFactoryBean() {
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(new UserService());
        String expression = "execution(* com.ley.springboot.UserService.*(..))";
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(expression);
        Advice advice = new UserServiceMethodBeforeAdvice();
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
        proxyFactoryBean.addAdvisors(advisor);
        return proxyFactoryBean;
    }
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface) factory.getProxy();

(8):操控通知对象

Advised接口是用来操控通知对象的。


Advisor[] getAdvisors();
​
void addAdvice(Advice advice) throws AopConfigException;
​
void addAdvice(int pos, Advice advice) throws AopConfigException;
​
void addAdvisor(Advisor advisor) throws AopConfigException;
​
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
​
int indexOf(Advisor advisor);
​
boolean removeAdvisor(Advisor advisor) throws AopConfigException;
​
void removeAdvisor(int index) throws AopConfigException;
​
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
​
boolean isFrozen();

 

 

可以添加DefaultPointcutAdvisor,它持有一个pointcut和advised。并且可以被用于添加任意一个

Advisor

(9):使用auto-proxy策略

  • BeanNameAutoProxyCreator:该类是一个BeanPostProcessor,根据bean的名称自动创建AOP

    代理。


<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames" value="jdk*,onlyJdk"/>
    <property name="interceptorNames">
        <list>
            <value>myInterceptor</value>
        </list>
    </property>
</bean>
 
  • DefaultAdvisorAutoProxyCreator通用且功能更强大的自动代理创建器。会在当前上下文中自动应用符合添加的advisor程序,而无需在auto-proxy advisor的bean定义中包含特定的bean名称。该类在将相同建议一致的应用于许多业务对象很有用。例如跟踪或者性能监视方面。

(10):定义新的通知类型

org.springframework.aop.framework.adapter包是一个SPI包,扩展新的通知类型。自定义新的通知类型唯一约束是它必须实现org.aopalliance.aop.Advice标记接口。

posted @ 2019-06-26 20:39  海渊  阅读(741)  评论(0编辑  收藏  举报