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)
引入通知需要一个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的更多相关文章
- Spring 4 官方文档学习(七)核心技术之Spring AOP APIs
请忽略本篇内容!!! 1.介绍 2.Spring中的pointcut API 2.1.概念 2.2.对pointcut的操作 2.3. AspectJ expression pointcut 2.4. ...
- 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 ...
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- 学习AOP之认识一下Spring AOP
心碎之事 要说知道AOP这个词倒是很久很久以前了,但是直到今天我也不敢说非常的理解它,其中的各种概念即抽象又太拗口. 在几次面试中都被问及AOP,但是真的没有答上来,或者都在面上,这给面试官的感觉就是 ...
- spring aop
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将 ...
- spring aop注解方式与xml方式配置
注解方式 applicationContext.xml 加入下面配置 <!--Spring Aop 启用自动代理注解 --> <aop:aspectj-autoproxy proxy ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- Spring AOP详解
一.前言 在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,就转载来学习一下,博文地址:http://www.cnbl ...
- Spring AOP实例——异常处理和记录程序执行时间
实例简介: 这个实例主要用于在一个系统的所有方法执行过程中出线异常时,把异常信息都记录下来,另外记录每个方法的执行时间. 用两个业务逻辑来说明上述功能,这两个业务逻辑首先使用Spring AOP的自动 ...
随机推荐
- 【23.15%】【codeforces 703C】Chris and Road
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- C# 反射调用私有事件
原文:C# 反射调用私有事件 在 C# 反射调用私有事件经常会不知道如何写,本文告诉大家如何调用 假设有 A 类的代码定义了一个私有的事件 class A { private event EventH ...
- Shell Step by Step (3) —— Stdin & if
4.输入输出 #! /bin/bash # Read users input and then get his name read -p "Please input your first n ...
- 维纳过程(Wiener Process)与高斯过程(Gaussian Process)
维纳过程又叫布朗运动过程(Brownian motion): 1. 维纳过程 维纳过程 Wt 由如下性质所描述: W0=1, a.s.(a.s.,almost surely)
- node lesson5--async
https://github.com/alsotang/node-lessons/tree/master/lesson5 https://github.com/nswbmw/N-blog/wiki/% ...
- UVa 12657 Boxes in a Line(应用双链表)
Boxes in a Line You have n boxes in a line on the table numbered 1 . . . n from left to right. Your ...
- matlab 工具函数(一) —— 添加指定 SNR 的噪声
SNR=PsignalPnoise=10⋅log10∑x=1Nx∑y=1Nyf2(x,y)∑x=1Nx∑y=1Ny(f(x,y)−f^(x,y))2=20⋅log10∥f(x,y)∥∥f^(x,y)− ...
- wpf CefSharp 与 js交互
原文:wpf CefSharp 与 js交互 通过 NuGet 获取 CefSharp.WpF 组件. xmlns:cefSharp="clr-namespace:CefSharp.Wpf ...
- 大约Android远程监控APP源代码
这篇文章的目的,关心询问名人,要打开源代码.这里说明,远程监控摄像头场外,相反,用手机摄像头摄像头server上,要理解这一点.关于非常网上的文章达到server道路.它能够准确,念就乱发博文,当然假 ...
- U3D游戏开发商思考
代码驱动带来的技术题 游戏碎片化.U3D 引擎有个非常有力的特色,就是实时编译执行.这意味着不管在不论什么时候,仅仅要按下执行图标,当前的场景就会进入可执行状态. 这导致了游戏在开发的过程中常常陷入一 ...