SpringAOP之入门
2017-02-03 09:12
316 查看
AOP的3个关键概念
切入点(Pointcut)
在介绍Pointcut之前先介绍Join Point(连接点)的概念。Join Point指程序运行中的某个阶段点,比如一个方法调用,异常抛出等。Pointcut就是Join Point的集合,它是程序中需要注入Advice的位置的集合,指明Advice在什么样的条件下才能被触发。Pointcut.java源码
接口ClassFilter:用来将切入点限定在给定的目标类中
接口MethodMatcher:用来判断切入点是否匹配目标类给定的方法
通知(Advice)
Advice是某个连接点所采用的处理逻辑,也就是向连接点注入的代码。 在spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。Advisor
Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码Spring的3种切入点(Pointcut)实现
静态切入点
静态切入点只限于给定的方法和目标类,不考虑方法的参数。Spring在调用静态切入点时只在第一次的时候计算静态切入点的位置,然后把它缓存起来,以后不再需要再进行计算。使用org.springframework.aop.support.RegexpMethodPointcut实现静态切入点<bean id="settersAndAbsquatulatePointcut" class="org.springframework.aop.support.RegexpMethodPointcut"> <property name="patterns"> <!-- 设定切入点 --> <list> <value>.*save.*</value> <value>.*do.*</value> </list> </property> </bean> </beans>
.* save.* : 表示所有以save开头的方法都是切入点
.* do.* : 表示所有以do开头的方法都是切入点
动态切入点
动态切入点不仅限定于给定的方法和类,还可以指定方法的参数。因为参数的变化性,动态切入点不能缓存,需要每次调用的时候都进行计算,因为使用动态切入点有很大的性能损耗。当切入点需要在执行时根据参数值来调用通知,就需要使用动态切入点。Spring提供了一个内建的动态切入点:控制流切入点。该切入点匹配基于当前线程的调用的堆栈,只有在当前线程执行时找到特定的类和方法才能返回true
自定义切入点
因为Spring中的切入点是Java类,所以可以定义自定义切入点Spring的通知(Advice)
Spring提供5种Advice类型:① Interception Around :在Join Point前后调用
② Before :在Join Point前调用
③ after Returning :在Join Point后调用
④ Throw :在Join Point抛出异常调用
⑤ Introduction :在Join Point调用完毕后调用
Interception Around通知
Interception Around 在在Join Point前后调用,也是Spring中最基本的通知类型,实现Interception Around的类需要实现接口MethodInterceptionpublic class LogInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throw Throwable { System.out.println("开始..."); Object ravl=invocation.proceed(); System.out.println("结束..."); return rval; } }
Before通知
Before 在Join Point前调用,需要实现接MethodBeforeAdvicepublic class LogBeforeAdvice implements MethodBeforeAdvice { @Override public void before(Method m, Object[] arg1, Object target) throws Throwable { // TODO Auto-generated method stub System.out.println("开始..."); } }
after Returning通知
after Returning 在在Join Point后调用,需要实现接口AfterReturningAdvicepublic class LogAfterAdvice implements AfterReturningAdvice{ @Override public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("结束..."); } }
Throw通知
Throw**在Join Point抛出异常**调用,需要实现接口ThrowsAdvicepublic class LogThrowAdvice implements ThrowsAdvice{ public void afterThrowing(RemoteException e) throws Throwable { System.out.println(" 抛出异常,请检查..."+e); } }
Introduction通知
Introduction 在Join Point调用完毕后调用,需要实现接口IntroductionAdvice和接口IntroductionInterceptor用ProxyFactoryBean创建AOP代理
使用Spring提供的类org.springframework.aop.framework.ProxyFactoryBean是创建AOP的最基本方式使用ProxyFactoryBean代理目标类的所有方法
在Spring中,ProxyFactoryBean是在xml里配置的<bean id="log" class="spring.aop.LogAfterAdvice"></bean> <bean id="timeBook" class="spring.aop.TimeBook"></bean> <!-- 设定代理类 --> <bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 这里代理的是接口 --> <!-- 3个属性,proxyInterfaces ;target;interceptorNames--> <property name="proxyInterfaces"> <value>spring.aop.TimeBookInterface</value> </property> <!-- 是ProxyFactoryBean要代理的目标类 --> <property name="target"> <ref bean="timeBook"/> </property> <!-- 程序中的Advice --> <property name="interceptorNames"> <list> <value>log</value> </list> </property> </bean>
id为log的Bean : 程序中的Advice
id为timeBook的Bean : ProxyFactoryBean要代理的目标类
id为logProxy的Bean : ProxyFactoryBean本身
ProxyFactoryBean的proxyInterfaces属性 : 指明要代理的接口
ProxyFactoryBean的target属性 : 指明要代理的目标类,这个目标类实现了上面proxyInterfaces属性指定的接口
ProxyFactoryBean的interceptorNames属性 : 指明要在代理的目标类中插入的Adivce
ProxyFactoryBean还有一个属性proxyTargetClass属性,如果属性设置为true,那proxyFactoryBean要使用CGLIB方式代理,而不是接口
使用ProxyFactoryBean代理目标类的指定方法
Advice会代理目标类的所有方法。使用Spring提供的org.springframework.aop.support.RegexpMethodPointcutAdvisor<bean id="log" class="spring.aop.LogAfterAdvice"></bean> <bean id="timeBook" class="spring.aop.TimeBook"></bean> <!-- 代理目标类的指定方法 --> <bean id="logAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref bean="log" /> </property> <property name="patterns"> <value>.*doAuditing.*</value> </property> </bean> <!-- 设定代理类 --> <bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>spring.aop.TimeBookInterface</value> </property> <property name="target"> <ref bean="timeBook" /> </property> <property name="interceptorNames"> <list> <value>logAdvisor</value> </list> </property> </bean>
1、在id为logAdvisor的Bean中设定Advice和指定的方法
2、把id为logProxy的Bean的interceptorNames属性值改为logAdvisor
3、logAdvisor的advice属性指定Advice
4、logAdvice的patterns属性指定要代理的方法。“.doAuditing”表示只有doAuditing()方法才能使用指定的Advice,patterns属性使用的是正则表达式(注意:因为要使用正则表达式,要引入jakarta-oro-2.0.8.jar)
正则表达式简介
正则表达式用于在一个文件中查找字符1、“ . ” 可以用来匹配任何一个字符。比如:正则表达式为“ g.f ”,它就会匹配“ gaf ”、“ g*f ”、“ g&f ”等中间是任意字符的字符
2、“ [ ] ”只有[ ]里面指定的字符才能匹配。比如:正则表达式为“ g[ abc ]f ”,它只能匹配“ gaf ”、“ gbf ”、“ gcf ”,不会匹配“ g*f ”、“ g&f ”等
3、“ * ” 表示匹配次数,可以任意次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“ g.*f ”,它只能匹配“ gaaaaaf ”、“ gf ”、“ g *f ”等
4、“ ? ” 可以匹配 0 或者 1 次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“ g.?f ”,它只能匹配“ gaf ”、“ g f ”等
5、“ \ ” 是正则表达式的连续符 , 比如:正则表达式为“ g.-f ”,它只能匹配“ g-f ”、“ ga-f ”和“ g*-f ”等
上面的示例中,只对TimeBook类的doAuditing( )方法有效,如果要对TimeBook中所有以do开头的方法有效,可以这样设定“ .do. ”
<!--对所有以do开头的方法有效--> <property name="patterns"> <value>.*do.*</value> </property>
如果要对TimeBook类中所有方法有效,可以这样设定“ .spring.aop.TimeBookInterface. ”
<property name="patterns"> <value> .*spring\.aop\.TimeBookInterface.*</value> </property>
注意:包的路径要用“ \ ”连接符来表示 “ . ”不是正则表达式的“ . ”
把输出日志的实例用Spring的AOP来实现
采用Interception Around通知的形式实现
Interception Around通知会在JointPoint的前后执行,实现Interception Around通知的类需要实现接口MethodInterception。实现思路:
① 实现接口MethodInterception
② 在Invoke()方法里编写负责输出日志信息的代码,具体的业务逻辑还使用前面的接口TimeBookInterface和它的实现类TimeBook
③ 在Spring的配置文档中定义Pointcut
④ 编写测试类,执行
(1)负责输出日志信息的类LogAround,该类实现了MethodInterception,重写了Invoke()方法
public class LogAround implements MethodInterceptor { private Logger logger = Logger.getLogger(this.getClass().getName()); public Object invoke(MethodInvocation invocation) throws Throwable { logger.log(Level.INFO, invocation.getArguments()[0] + " 开始..."); try { Object result = invocation.proceed(); //返回值即是被调用的方法的返回值 return result; } finally { // TODO: handle exception logger.log(Level.INFO, invocation.getArguments()[0] + " 结束..."); } } }
<bean id="log" class="spring.aop.LogAround"></bean> <bean id="timeBook" class="spring.aop.TimeBook"></bean> <bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>spring.aop.TimeBookInterface</value> </property> <property name="target"> <ref bean="timeBook" /> </property> <property name="interceptorNames"> <list> <value>log</value> </list> </property> </bean>
public class Test { public static void main(String[] args) { timeBookProxy.doAuditing("张三"); //通过ApplicationContext获取XML ApplicationContext context=new FileSystemXmlApplicationContext("classpath:aop-config.xml"); TimeBookInterface timeBookProxy=(TimeBookInterface) context.getBean("logProxy"); timeBookProxy.doAuditing("CSDN"); } }
运行结果
相关文章推荐
- Spring AOP 入门实例
- Spring AOP 入门学习
- Spring 系列: Spring 框架简介- Spring AOP 和 IOC 容器入门
- Spring学习笔记(三)AOP入门<原创>
- Spring AOP 简单入门示例
- AOP入门的简单例子(Spring AOP实现)
- Spring入门Blog[九、Spring Aop 用xml的方式实现]
- spring aop 入门经典的 和 aop实现方式的几篇文章
- Spring 框架简介(Spring AOP 和 IOC 容器入门)
- Spring入门Blog[七、Spring Aop的理解和简单实现]
- Spring AOP 入门学习
- Spring AOP @AspectJ 入门实例
- spring 学习(1.4) 入门基础- AOP教程
- Spring AOP入门实例详解
- Spring 系列: Spring 框架简介----Spring AOP 和 IOC 容器入门
- Spring AOP 入门实例
- spring 学习(1.3) 入门基础- AOP 实例
- Spring AOP @AspectJ 入门实例
- Spring 注解AOP 入门
- Spring入门Blog[八、Spring Aop织入点语法和相关案例总结]