Spring中的通知(Advice)和顾问(Advisor)
在Spring中,目前我学习了几种增强的方式,和大家分享一下
之前的话:
1.AOP (Aspect Oriented Programming 面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。
基本术语(一些名词):
(1)切面(Aspect)
切面泛指[交叉业务逻辑]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。
(2)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。
(3) 连接点(JoinPoint)
连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点
(4)切入点(PointCut)
切入点指切面具体织入的方法
注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(5)目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(6)通知(Advice)
通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。
(7)顾问(Advisor)
顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。
AOP是一种思想,而非实现
AOP是基于OOP,而又远远高于OOP,主要是将主要核心业务和交叉业务分离,交叉业务就是切面。例如,记录日志和开启事务。
一:前置增强和后置增强
源码介绍:
1.User.java
View Code
2.IDao.java
View Code
3.UserDao.java
View Code
4.IUserBiz.java
View Code
5.UserBiz.java
View Code
6.LoggerAfter.java(后置增强)
View Code
7.LoggerBefore.java(前置增强)
View Code
8.applicationContext.xml(Spring配置文件)
View Code
当然,针对AOP的配置也可以使用代理对象 ProxyFactoryBean 代理工厂bean来实现,在测试类中:IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");
View Code
9.MyTest.java
View Code
10.log4j.properties(日志的配置文件)
View Code
当然,别忘了引入我们需要的jar包啊!
常用的jar:
二:异常抛出增强和环绕增强
源码介绍:
1.User.java
View Code
2.UserService.java
View Code
3.ErrorLog.java(异常抛出增强)
View Code
4.AroundLog(环绕增强)
View Code
5.applicationContext.xml(Spring配置文件)
View Code
6.MyTest.java
View Code
三:注解增强方式实现前置增强和后置增强
源码介绍:
1.UserService.java
View Code
2.AnnotationAdvice.java(注解增强)
View Code
注:
java.lang.Object[] getArgs():获取连接点方法运行时的入参列表
Signature getSignature() :获取连接点的方法签名对象
java.lang.Object getTarget() :获取连接点所在的目标对象
java.lang.Object getThis() :获取代理对象本身
3.applicationContext.xml(Spring配置文件)
View Code
4.MyTest.java
View Code
四: 顾问(Advisor)实现前置增强
通知Advice是Spring提供的一种切面(Aspect)。但其功能过于简单,只能
将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。
顾问Advisor是Spring提供的另一种切面。其可以完成更为复杂的切面织入功能。PointcutAdvisor是顾问的一种,可以指定具体
的切入点。顾问将通知进行了包装,会根据不同的通知类型,在不同的时间点,将切面织入到不同的切入点。
PointcutAdvisor接口有两个较为常用的实现类:
:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问
:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问
<property name="pattern" value=".do."></property> 表示方法全名(包名,接口名,方法名)
运算符 名称 意义
. 点号 表示任意单个字符
- 加号 表示前一个字符出现一次或者多次
- 星号 表示前一个字符出现0次或者多次
=====默认Advisor自动代理生成器
DefaultAdvisorAutoProxyCreator
=====BeanName自动代理生成器
BeanNameAutoProxyCreator
实例:
源码介绍:
1.ISomeService.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package service; //接口
public interface ISomeService { //待实现的方法
public void doFirst(); public void doSecond();
}</pre>
](javascript:void(0); "复制代码")
2.SomeServiceImpl.java
View Code
3.MyMethodBeforeAdvice.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package aop; //前置增强
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("目标方法执行之前执行");
}
}</pre>
[](javascript:void(0); "复制代码")
4.applicationContext.xml(Spring配置文件)
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目标对象 -->
<bean id="someService" class="service.SomeServiceImpl"></bean>
<!-- 切面:通知 -->
<bean id="beforeAdvice" class="aop.MyMethodBeforeAdvice"></bean>
<!-- *********************************************** -->
<!-- 1.*:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问 -->
<!-- 切面:顾问 顾问(Advisor)要包装通知(Advice) -->
<bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<!--指定需要增强的方法:这里是doFirst()方法,而doSecond()方法则不会增强 -->
<property name="mappedName" value="doFirst"></property>
<!-- 也可以使用mappedNames指定多个方法
<property name="mappedNames" value="doFirst,doSecond"></property> -->
</bean>
<!-- *********************************************** -->
<!-- 2.*:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问 -->
<!-- 切面: 顾问 顾问(Advisor)要包装通知(Advice) -->
<!-- <bean id="beforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pattern" value=".*doF.*t"></property>
</bean> -->
<!-- *********************************************** -->
<!-- 代理工厂Bean -->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someService"></property>
<property name="interceptorNames" value="beforeAdvisor"></property>
</bean>
</beans></pre>
[](javascript:void(0); "复制代码")
5.MyTest.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package test; //对顾问(Advisor)测试
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.ISomeService; public class MyTest {
@Test public void testOne(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService)ctx.getBean("serviceProxy");
service.doFirst();
service.doSecond();
}
}</pre>
[
Spring中的通知(Advice)和顾问(Advisor)
在Spring中,目前我学习了几种增强的方式,和大家分享一下
之前的话:
1.AOP (Aspect Oriented Programming 面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。
基本术语(一些名词):
(1)切面(Aspect)
切面泛指[交叉业务逻辑]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。
(2)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。
(3) 连接点(JoinPoint)
连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点
(4)切入点(PointCut)
切入点指切面具体织入的方法
注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(5)目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(6)通知(Advice)
通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。
(7)顾问(Advisor)
顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。
AOP是一种思想,而非实现
AOP是基于OOP,而又远远高于OOP,主要是将主要核心业务和交叉业务分离,交叉业务就是切面。例如,记录日志和开启事务。
一:前置增强和后置增强
源码介绍:
1.User.java
View Code
2.IDao.java
View Code
3.UserDao.java
View Code
4.IUserBiz.java
View Code
5.UserBiz.java
View Code
6.LoggerAfter.java(后置增强)
View Code
7.LoggerBefore.java(前置增强)
View Code
8.applicationContext.xml(Spring配置文件)
View Code
当然,针对AOP的配置也可以使用代理对象 ProxyFactoryBean 代理工厂bean来实现,在测试类中:IUserBiz biz=(IUserBiz)ctx.getBean("serviceProxy");
View Code
9.MyTest.java
View Code
10.log4j.properties(日志的配置文件)
View Code
当然,别忘了引入我们需要的jar包啊!
常用的jar:
二:异常抛出增强和环绕增强
源码介绍:
1.User.java
View Code
2.UserService.java
View Code
3.ErrorLog.java(异常抛出增强)
View Code
4.AroundLog(环绕增强)
View Code
5.applicationContext.xml(Spring配置文件)
View Code
6.MyTest.java
View Code
三:注解增强方式实现前置增强和后置增强
源码介绍:
1.UserService.java
View Code
2.AnnotationAdvice.java(注解增强)
View Code
注:
java.lang.Object[] getArgs():获取连接点方法运行时的入参列表
Signature getSignature() :获取连接点的方法签名对象
java.lang.Object getTarget() :获取连接点所在的目标对象
java.lang.Object getThis() :获取代理对象本身
3.applicationContext.xml(Spring配置文件)
View Code
4.MyTest.java
View Code
四: 顾问(Advisor)实现前置增强
通知Advice是Spring提供的一种切面(Aspect)。但其功能过于简单,只能
将切面织入到目标类的所有目标方法中,无法完成将切面织入到指定目标方法中。
顾问Advisor是Spring提供的另一种切面。其可以完成更为复杂的切面织入功能。PointcutAdvisor是顾问的一种,可以指定具体
的切入点。顾问将通知进行了包装,会根据不同的通知类型,在不同的时间点,将切面织入到不同的切入点。
PointcutAdvisor接口有两个较为常用的实现类:
:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问
:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问
<property name="pattern" value=".do."></property> 表示方法全名(包名,接口名,方法名)
运算符 名称 意义
. 点号 表示任意单个字符
- 加号 表示前一个字符出现一次或者多次
- 星号 表示前一个字符出现0次或者多次
=====默认Advisor自动代理生成器
DefaultAdvisorAutoProxyCreator
=====BeanName自动代理生成器
BeanNameAutoProxyCreator
实例:
源码介绍:
1.ISomeService.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package service; //接口
public interface ISomeService { //待实现的方法
public void doFirst(); public void doSecond();
}</pre>
](javascript:void(0); "复制代码")
2.SomeServiceImpl.java
View Code
3.MyMethodBeforeAdvice.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package aop; //前置增强
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
@Override public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("目标方法执行之前执行");
}
}</pre>
[](javascript:void(0); "复制代码")
4.applicationContext.xml(Spring配置文件)
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 目标对象 -->
<bean id="someService" class="service.SomeServiceImpl"></bean>
<!-- 切面:通知 -->
<bean id="beforeAdvice" class="aop.MyMethodBeforeAdvice"></bean>
<!-- *********************************************** -->
<!-- 1.*:NameMatchMethodPointcutAdvisor 名称匹配方法切入点顾问 -->
<!-- 切面:顾问 顾问(Advisor)要包装通知(Advice) -->
<bean id="beforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<!--指定需要增强的方法:这里是doFirst()方法,而doSecond()方法则不会增强 -->
<property name="mappedName" value="doFirst"></property>
<!-- 也可以使用mappedNames指定多个方法
<property name="mappedNames" value="doFirst,doSecond"></property> -->
</bean>
<!-- *********************************************** -->
<!-- 2.*:RegexpMethodPointcutAdvisor 正则表达式匹配方法切入点顾问 -->
<!-- 切面: 顾问 顾问(Advisor)要包装通知(Advice) -->
<!-- <bean id="beforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="beforeAdvice"></property>
<property name="pattern" value=".*doF.*t"></property>
</bean> -->
<!-- *********************************************** -->
<!-- 代理工厂Bean -->
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="someService"></property>
<property name="interceptorNames" value="beforeAdvisor"></property>
</bean>
</beans></pre>
[](javascript:void(0); "复制代码")
5.MyTest.java
](javascript:void(0); "复制代码")
<pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">package test; //对顾问(Advisor)测试
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import service.ISomeService; public class MyTest {
@Test public void testOne(){
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
ISomeService service = (ISomeService)ctx.getBean("serviceProxy");
service.doFirst();
service.doSecond();
}
}</pre>
](javascript:void(0); "复制代码")