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标记接口。

Spring AOP APIS的更多相关文章

  1. Spring 4 官方文档学习(七)核心技术之Spring AOP APIs

    请忽略本篇内容!!! 1.介绍 2.Spring中的pointcut API 2.1.概念 2.2.对pointcut的操作 2.3. AspectJ expression pointcut 2.4. ...

  2. Spring 4 官方文档学习(六)核心技术之Spring AOP

    目录 1.介绍 1.1.AOP概念 1.2.Spring AOP 能力 和 目标 1.2.1.简介 1.2.2.@AspectJ 支持 1.2.3.声明一个aspect 例子 1.2.4.声明advi ...

  3. 学习AOP之深入一点Spring Aop

    上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...

  4. 学习AOP之认识一下Spring AOP

    心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...

  5. spring aop

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...

  6. spring aop注解方式与xml方式配置

    注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...

  7. 基于Spring AOP的JDK动态代理和CGLIB代理

    一.AOP的概念  在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...

  8. Spring AOP详解

    一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...

  9. Spring AOP实例——异常处理和记录程序执行时间

    实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...

随机推荐

  1. 关于java项目与web项目中lib包的那点事

    一.在java项目中如何引入外部jar包:1.在我们的java项目下新建一个lib文件夹:2.将我们需要引入的jat包复制到lib文件夹下:3.选中我们lib包下的jar,右键选择Build Path ...

  2. Android 升级下载 它们的定义Updates 兼容版本

    Android 更新模块 它们的定义Update 写这个总结是由于在项目中碰到了Android系统兼容的BUG   Android项目原本使用的是API提供的下载方法   例如以下: Download ...

  3. win7(64位)彻底卸载mysql,重装不再烦恼

    [此文出身]鄙人mysql呆鸟,一时手残卸载了mysql,之后重装,始终在配置的时候始终无法通过(如图),纠结一天之久! 查看大图 在某人的鄙视鞭策下,通过度娘的多种指导及自身的多次尝试,终于在下班前 ...

  4. windows 下 TensorFlow(GPU 版)的安装

    windows 10 64bit下安装Tensorflow+Keras+VS2015+CUDA8.0 GPU加速 0. 环境 OS:Windows 10,64 bit: 显卡:NVIDIA GeFor ...

  5. Redis实战:如何构建类微博的亿级社交平台

    微博及 Twitter 这两大社交平台都重度依赖 Redis 来承载海量用户访问.本文介绍如何使用 Redis 来设计一个社交系统,以及如何扩展 Redis 让其能够承载上亿用户的访问规模. 虽然单台 ...

  6. Android中SQLite数据库操作(2)——SQLiteOpenHelper类

    如果开发者对SQL语法不熟悉,我要告诉你一个好消息,Android提供了一个SQLiteOpenHelper类. 在实际项目中很少使用SQLiteDatabase的方法(请看:http://blog. ...

  7. dos命令查看端口状态

    netstat 显示协议统计信息和当前 TCP/IP 网络连接. NETSTAT [-a] [-b] [-e] [-n] [-o] [-p proto] [-r] [-s] [-v] [interva ...

  8. Extensible Access Control List Framework

    Methods, systems, and products for governing access to objects on a filesystem. In one general embod ...

  9. Windows 窗体设计器(Windows Forms Designer)入门

      Visual Studio 2010 更新:2010 年 9 月 Windows 窗体设计器提供多个用于生成 Windows 窗体应用程序的工具. 本演练阐释如何使用设计器提供的各种工具生成应用程 ...

  10. 努比亚Z7 mini刷机教程_recovery卡刷机教程

    之前小编分享努比亚Z7 mini电话访问Root权限.recovery刷机教程. 所以对于朋友谁搞机整机的爱,左边是写第三方手机刷包.那么下面刷的家小编与您分享努比亚Z7 mini刷机教程手机. 一. ...