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的自动 ...
随机推荐
- mac在终端打开应用程序
今天研究了下mac终端的启动流程.以下以sublime为例,介绍怎么在mac的终端中加入app启动方法. 方法1 :使用"open -a /Applications/Sublime\ Tex ...
- exec 与 open 打开进程
1 exec ?-keepnewline ?-ignorestderr args(?号后面表示可以跟的参数) 这个东西一旦执行 , 没有执行完毕父进程会处于等待中 使用一个或多个子进程运行 由arg ...
- Smarty优缺点
Smarty优点: 1. 速度:采用smarty编写的程序可以获得最大速度的提高,这一点是相对于其它的模板引擎技术而言的. 2. 编译型:采用smarty编写的程序在运行时要编译成一个非模板技术的PH ...
- oracle 数组
定义一个长度为5的字符串数组 type str_array is varray(5) of varchar2(30); v_str_array str_array := expr_key_array( ...
- PowerDesigner模型分类
原文:PowerDesigner模型分类 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zjws23786/article/details/8005 ...
- HDU-4249-A Famous Equation(DP)
Problem Description Mr. B writes an addition equation such as 123+321=444 on the blackboard after cl ...
- C#操作EXCEL常见操作集合(行高,列宽,合并单元格,单元格边框线)
private _Workbook _workBook = null; private Worksheet _workSheet = null; private Excel.Application _ ...
- bigdata_ Kafka集群模式部署
环境:kafka 0.8.1.1 基本概念 Kafka维护按类区分的消息,称为主题(topic) 生产者(producer)向kafka的主题发布消息 消费者(consumer)向主题注册,并且接收发 ...
- Angular自定义表单验证
前端表单验证 为年龄输入框添加了两个验证,并分情况填写了提示语 <form nz-form [formGroup]="validateForm"> <nz-for ...
- sqlite 初
1.SQLite是什么 基于文件的轻型数据库 无服务器 零配置 支持事务 开源 2.SQLite 怎么用 2.1 安装 SQLite官网上下载对应的DLL 与工具 配置环境变量 安装完成以后 ...