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)

引入通知需要一个IntroductionAdvisor和IntroductionInterceptor,如下实现


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. android游戏开发系列(1)——迅雷不及掩耳的声音

    这种声音是短而快的声音,应该采用android.media.SoundPool实现. SoundPool的特点: 1. SoundPool载入音乐文件使用了独立的线程,不会阻塞UI主线程的操作.但是这 ...

  2. altium designer电气符号和包的常用元素

    1. 标准电阻:RES1.RES2.包裹:AXIAL-0.3至AXIAL-1.0 两port可变电阻器:RES3.RES4.包裹:AXIAL-0.3至AXIAL-1.0 三port可变电阻:RESIS ...

  3. 解决eclipse中找不到jar包问题

    当使用eclipse导入外部的web工程时,有时会提示HttpServletRequest, ServletActionContext找不到的情况,解决办法: (注:我已经引用了struts2的jar ...

  4. Python类型和对象

    关键字:Python 类型 对象原文:http://wiki.woodpecker.org.cn/moin/PyTypesAndObjects 关于本书 解释新式的Python对象(new-style ...

  5. ASP.NET Core 异常和错误处理 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 异常和错误处理 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 异常和错误处理 上一章节中,我们学习了 ASP.NET Cor ...

  6. vi学习(1)

    今天下午看了vi频繁使用的操作,现在记录,为了方便日后查询. 按vi模式.进入命令3部分. (一) 一般模式下 字符操作:上下左右箭头(或kjhl)能够实现光标上下左右移动一位. 假设想要进行多次移动 ...

  7. [bug系列]Method not found: 'Void Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory

    bug由来 最近开始学习NetCore,想通过实战使用NetCore做一个集成数据库存储Redis缓存的WebApi项目,由于MSSQL的庞大体积,最终决定使用轻量级关系型数据库MySql. 所以最终 ...

  8. C# 写CSV文件字符串前面0不显示的解决办法

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Tex ...

  9. JAVASCRIPT高程笔记-------JSON与AJAX

    json对象——语法 简单值:与JS相同语法,可以是字符串,数值,布尔值,null:但不支持undefined 对象: 复杂数据类型,表示一组有序的键值对,键值对的值可以是简单数据,也可以是复杂数据 ...

  10. MVC 异步调用

    @{    Layout = null;}<!DOCTYPE html><html><head>    <meta name="viewport&q ...