代理模式
代理模式是常用的Java 設(shè)計(jì)模式错洁,它的特征是代理類與委托類有同樣的接口入撒,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾消息先煎、把消息轉(zhuǎn)發(fā)給委托類贼涩,以及事后處理消息等。代理類與委托類之間通常會(huì)存在關(guān)聯(lián)關(guān)系薯蝎,一個(gè)代理類的對(duì)象與一個(gè)委托類的對(duì)象關(guān)聯(lián)遥倦,代理類的對(duì)象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對(duì)象的相關(guān)方法占锯,來提供特定的服務(wù)袒哥。
-
注意:
委托類對(duì)象就是我們后面說到的 目標(biāo)對(duì)象(需要【被】代理的對(duì)象)
代理類對(duì)象就是我們后面說到的 代理對(duì)象(目標(biāo)對(duì)象就是需要這個(gè)對(duì)象做為代理) - 按照代理類的創(chuàng)建時(shí)期缩筛,代理類可分為兩種。
靜態(tài)代理類:
由程序員創(chuàng)建或由特定工具自動(dòng)生成源代碼堡称,再對(duì)其編譯瞎抛。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了却紧。
動(dòng)態(tài)代理類:在程序運(yùn)行時(shí)桐臊,運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建而成。
靜態(tài)代理
例如:
接口:HelloService
委托類:HelloServiceImpl
代理類:HelloServiceProxy
public interface HelloService{
public String echo(String msg);
public Date getTime();
}
public class HelloServiceImpl implements HelloService{
public String echo(String msg){
return "echo:"+msg;
}
public Date getTime(){
return new Date();
}
}
public class HelloServiceProxy implements HelloService{
private HelloService helloService; //表示被代理的HelloService 實(shí)例
public HelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public void setHelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public String echo(String msg){
System.out.println("before calling echo()"); //目標(biāo)方法調(diào)前處理
//調(diào)用委托類對(duì)象的方法(也就是目標(biāo)對(duì)象方法/被代理對(duì)象方法)
//這個(gè)方法才是我們真正要執(zhí)行的方法
String result=helloService.echo(msg);
System.out.println("after calling echo()"); //目標(biāo)方法調(diào)用后處理
return result;
}
public Date getTime(){
System.out.println("before calling getTime()"); //目標(biāo)方法調(diào)前處理
//調(diào)用委托類對(duì)象的方法(也就是目標(biāo)對(duì)象方法/被代理對(duì)象方法)
//這個(gè)方法才是我們真正要執(zhí)行的方法
Date date=helloService.getTime();
System.out.println("after calling getTime()"); //目標(biāo)方法調(diào)用后處理
return date;
}
}
main:
HelloService helloService=new HelloServiceImpl();
HelloService helloServiceProxy=new HelloServiceProxy(helloService);
System.out.println(helloServiceProxy.echo("hello"));
動(dòng)態(tài)代理
與靜態(tài)代理類對(duì)照的是動(dòng)態(tài)代理類晓殊,動(dòng)態(tài)代理類的字節(jié)碼在程序運(yùn)行時(shí)由Java反射機(jī)制動(dòng)態(tài)生成断凶,無需程序員手工編寫它的源代碼。動(dòng)態(tài)代理類不僅簡化了編程工作巫俺,而且提高了軟件系統(tǒng)的可擴(kuò)展性懒浮,因?yàn)镴ava 反射機(jī)制可以生成任意類型的動(dòng)態(tài)代理類。 java.lang.reflect 包下面的Proxy類和InvocationHandler 接口提供了生成動(dòng)態(tài)代理類的能力识藤。
例子:
接口:
public interface IStudentService {
void save(Student s);
void delete(long id);
Student find(long id);
}
日志類:
public class StudentLogger {
public void log(String msg){
System.out.println("log: "+msg);
}
}
實(shí)現(xiàn)類
public class StudentServiceImpl implements IStudentService {
public void delete(long id) {
// 記錄日志
System.out.println("student is deleted...");
}
public Student find(long id) {
// 記錄日志
System.out.println("student is found...");
return null;
}
public void save(Student s) {
// 記錄日志
System.out.println("student is saved...");
}
}
//InvocationHandler接口的實(shí)現(xiàn)類,java的動(dòng)態(tài)代理中需要使用
public class MyHandler implements InvocationHandler {
//目標(biāo)對(duì)象
private Object target;
private StudentLogger logger = new StudentLogger();
public MyHandler() {
}
public MyHandler(Object target) {
this.target = target;
}
// 參數(shù)1 將來所產(chǎn)生的代理對(duì)象 Proxy4$
// 參數(shù)2 將來需要調(diào)用到的目標(biāo)對(duì)象里面真正的那個(gè)方法的鏡像
// 參數(shù)3 將來調(diào)用方法的時(shí)候所傳的參數(shù)
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
// 獲得將來所調(diào)用方法的名字
String methodName = m.getName();
// 用日志記錄輸出一下
logger.log(methodName + " is invoked...");
// 用反射的方式去調(diào)用將來需要真正調(diào)用的方法.
Object o = m.invoke(target, args);
return o;
}
get/set
....
}
main:
//目標(biāo)對(duì)象
IStudentService service = new StudentServiceImpl();
//service是我們的目標(biāo)對(duì)象。
//我們要給目標(biāo)對(duì)象產(chǎn)生代理對(duì)象次伶。
//目標(biāo)對(duì)象service只能單獨(dú)執(zhí)行delete方法痴昧。
//但是我們需要的是:先執(zhí)行l(wèi)og日志方法再執(zhí)行delete方法。
//目標(biāo)對(duì)象service做不到這個(gè)要求,所以我們要給目標(biāo)對(duì)象service
//生成一個(gè)代理對(duì)象去完成這倆個(gè)操作.
//怎么給目標(biāo)對(duì)象生成代理對(duì)象:
//JDK動(dòng)態(tài)代理的方式
//獲得目標(biāo)對(duì)象的Class對(duì)象
Class c = service.getClass();
//獲得目標(biāo)對(duì)象的類加載器對(duì)象
ClassLoader classLoader = c.getClassLoader();
//獲得目標(biāo)對(duì)象所實(shí)現(xiàn)的所有接口
Class[] interfaces = c.getInterfaces();
//獲得一個(gè)InvocationHandler接口的實(shí)現(xiàn)類對(duì)象,并把目標(biāo)對(duì)象傳進(jìn)去
InvocationHandler h =
new MyHandler(service);
//參數(shù)1 目標(biāo)對(duì)象的類加載器對(duì)象
//參數(shù)2 目標(biāo)對(duì)象所實(shí)現(xiàn)的所有接口. Class類型數(shù)組
//參數(shù)3 InvocationHandler接口的實(shí)現(xiàn)類對(duì)象
IStudentService proxy =
(IStudentService)Proxy.newProxyInstance
(classLoader, interfaces, h);
//這里的proxy是一個(gè)實(shí)現(xiàn)了IStudentService接口動(dòng)態(tài)生成的代理類的對(duì)象
proxy.delete();
CGLib代理
JDK實(shí)現(xiàn)動(dòng)態(tài)代理需要實(shí)現(xiàn)類通過接口定義業(yè)務(wù)方法冠王,對(duì)于沒有接口的類赶撰,如何實(shí)現(xiàn)動(dòng)態(tài)代理呢,這就需要CGLib了柱彻。CGLib采用了非常底層的字節(jié)碼技術(shù)豪娜,其原理是通過字節(jié)碼技術(shù)為目標(biāo)對(duì)象創(chuàng)建一個(gè)子類對(duì)象,并在子類對(duì)象中攔截所有父類方法的調(diào)用哟楷,然后在方法調(diào)用前后調(diào)用后都可以加入自己想要執(zhí)行的代碼瘤载。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理都是Spring AOP的采用的代理方式。
簡單的實(shí)現(xiàn):
這是一個(gè)需要被代理的類卖擅,也就是父類鸣奔,通過字節(jié)碼技術(shù)創(chuàng)建這個(gè)類的子類,實(shí)現(xiàn)動(dòng)態(tài)代理惩阶。
public class SayHello {
public void say(){
System.out.println("hello everyone");
}
}
注意:在cglib方式中,目標(biāo)對(duì)象作為父類,代理對(duì)象作為目標(biāo)對(duì)象動(dòng)態(tài)生成的子類對(duì)象
- 該類實(shí)現(xiàn)了創(chuàng)建一個(gè)類的子類的方法(cglib給一個(gè)類生成代理對(duì)象的方式)
- getProxy(SuperClass.class)方法通過參數(shù)即父類的class對(duì)象挎狸,創(chuàng)建出它的一個(gè)子類對(duì)象,也就是cglib方式的代理對(duì)象
- intercept()方法攔截所有目標(biāo)類方法的調(diào)用,
- obj表示將來生成的代理對(duì)象断楷,
- method為目標(biāo)類中方法的反射對(duì)象锨匆,args為方法的動(dòng)態(tài)入?yún)ⅲ?/li>
- mproxy為代理類(子類)中方法的反射對(duì)象。
- mproxy.invokeSuper(obj, args)通過代理類調(diào)用目標(biāo)對(duì)象(父類)中的方法冬筒。
public class CglibProxy implements MethodInterceptor{
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
//設(shè)置誰是父類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通過字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例
return enhancer.create();
}
//實(shí)現(xiàn)MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy mproxy) throws Throwable {
System.out.println("前置代理");
//通過代理類調(diào)用父類中的方法
Object result = mproxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
main:
CglibProxy proxy = new CglibProxy();
//通過生成子類的方式創(chuàng)建代理類
SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
proxyImp.say();
輸出結(jié)果:
前置代理
hello everyone
后置代理
Spring實(shí)現(xiàn)AOP(Aspect Oriented Programming)是依賴JDK動(dòng)態(tài)代理和CGLIB代理(不同情況spring會(huì)自己選擇一種方式)恐锣。
JDK動(dòng)態(tài)代理和CGLIB代理的對(duì)比:
JDK動(dòng)態(tài)代理:
其代理對(duì)象必須是某個(gè)接口的實(shí)現(xiàn)茅主,它是通過在運(yùn)行期間創(chuàng)建一個(gè)接口的實(shí)現(xiàn)類來完成對(duì)目標(biāo)對(duì)象的代理。
CGLIB代理:
實(shí)現(xiàn)原理類似于JDK動(dòng)態(tài)代理侥蒙,只是它在運(yùn)行期間生成的代理對(duì)象是針對(duì)目標(biāo)類擴(kuò)展的子類暗膜。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的java字節(jié)碼編輯類庫)操作字節(jié)碼實(shí)現(xiàn)的鞭衩。
所以spring會(huì)有以下倆種選擇動(dòng)態(tài)代理實(shí)現(xiàn)方式的情況:
- 如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口学搜,默認(rèn)情況下會(huì)采用JDK的動(dòng)態(tài)代理實(shí)現(xiàn)AOP
- 如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)了接口,必須采用CGLIB庫论衍,spring會(huì)自動(dòng)在JDK動(dòng)態(tài)代理和CGLIB之間自動(dòng)選擇;
認(rèn)識(shí)AOP中的一些基本概念,然后在一個(gè)一個(gè)的例子中,不斷的加強(qiáng)對(duì)這些概念的理解同時(shí)要能自己表述出每個(gè)概念的含義
AOP 面向切面編程
aspect 切面/切面類
joinPoint 連接點(diǎn)
在spring的aop中只有 類中的方法 可以做連接點(diǎn),每一個(gè)方法都可以是一個(gè)連接點(diǎn).
pointCut 切入點(diǎn)
一組連接點(diǎn)的集合
advice 通知/攔截器
用來控制切面類將來到底是織入到切入點(diǎn)的前面瑞佩、后面或者是拋異常的時(shí)候。
adivsor 增強(qiáng)器
用來篩選類中的哪些方法是我們的連接點(diǎn)(哪些方法需要被攔截).
target 目標(biāo)對(duì)象
proxy 代理對(duì)象
wave 織入
前置通知(Before advice):
在某連接點(diǎn)(join point)之前執(zhí)行的通知返回后通知(After returning advice):
在某連接點(diǎn)(join point)正常完成后執(zhí)行的通知:例如坯台,一個(gè)方法沒有拋出任何異常炬丸,正常返回。拋出異常后通知(After throwing advice):
在方法拋出異常退出時(shí)執(zhí)行的通知蜒蕾。后通知(After (finally) advice):
當(dāng)某連接點(diǎn)退出的時(shí)候執(zhí)行的通知環(huán)繞通知(Around Advice):
包圍一個(gè)連接點(diǎn)(join point)的通知稠炬,例如事務(wù)的處理,就需要這樣的通知,因?yàn)槭聞?wù)需要在方法前開啟,在方法后提交
在Spring中,Advice是由spring中的幾個(gè)接口來指定(就像action類由struts2中的action接口來指定一樣)咪啡,主要有以下幾種:
Before Advice
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
例如:
//有一下幾個(gè)類或者接口:
Account.java
private int id;
private String name;
private double balance;//余額
get/set
...
AccountDao.java
//取款 賬號(hào)減去多少錢
void withdraw(Account acc,double amt);
//存款 賬號(hào)加上多少錢
void deposit(Account acc,double amt);
AccountDaoImpl.java
//簡單的實(shí)現(xiàn)接口中的抽象方式
IAccountService.java
//銀行賬號(hào)的一個(gè)操作:例如轉(zhuǎn)賬
void bankAction();
AccountServiceImpl.java
private AccountDao accountDao;
private Account account;
//轉(zhuǎn)賬
public void bankAction(){
accountDao.withdraw(account, 100);
accountDao.deposit(account, 100);
}
get/set
...
//切面類
public class MyLogger {
public void log(String msg){
System.out.println("log:"+msg);
}
}
我們要做的事情:在轉(zhuǎn)賬方法(bankAction)執(zhí)行之前進(jìn)行一個(gè)日志輸出
//前置通知:作用Spring會(huì)在目標(biāo)方法執(zhí)行之前調(diào)用這個(gè)before方法
public class BeforeAdvice implements MethodBeforeAdvice {
//切面類
private MyLogger logger;
// 參數(shù)1 將來我們需要調(diào)用的目標(biāo)對(duì)象中的方法鏡像
// 參數(shù)2 將來調(diào)用方法的時(shí)候所傳過來的參數(shù)
// 參數(shù)3 目標(biāo)對(duì)象
//將來在調(diào)用目標(biāo)對(duì)象方法之前,會(huì)先執(zhí)行這個(gè)before方法
//在此方法只需要去寫首启,代理對(duì)象要新增的功能呢=,不需要手動(dòng)調(diào)用目標(biāo)對(duì)象
//所執(zhí)行的方法撤摸。
public void before(Method m, Object[] args, Object target) throws Throwable {
logger.log(m.getName() + " is invoked..");
/*
* 注意:這里一定不要自己手動(dòng)的用反射去 調(diào)用這個(gè)目標(biāo)對(duì)象中的方法,
* 因?yàn)閟pring 會(huì)幫我們?nèi)フ{(diào)用的,如果我們這個(gè)再去調(diào)用這個(gè)方法,
* 那么這這個(gè)方法會(huì)被調(diào)用倆次.
*
* m.invoke(target,args);
*
*/
}
get/set
}
配置xml文件: 注意ProxyFactoryBean的配置,htmlsingle中搜索即可
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對(duì)象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用的是spring的一個(gè)代理對(duì)象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對(duì)象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對(duì)象所實(shí)現(xiàn)的接口 可以有多個(gè)接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個(gè) -->
<property name="interceptorNames">
<list>
<value>beforeAdvice</value>
</list>
</property>
</bean>
After advice
public interface AfterReturningAdvice extends Advice {
void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable;
}
例如:
public class AfterAdvice implements AfterReturningAdvice {
private MyLogger logger;
//參數(shù)1 目標(biāo)對(duì)象中的方法執(zhí)行完返回值
//參數(shù)2 所執(zhí)行方法的鏡像對(duì)象
//參數(shù)3 執(zhí)行方法時(shí)候所傳的參數(shù)
//參數(shù)4 目標(biāo)對(duì)象
//將來調(diào)用目標(biāo)對(duì)象的方法之后會(huì)執(zhí)行這個(gè)afterReturning方法
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
logger.log("after returning " + " target=" + target
+ " method Name=" + method.getName() + " args are:" + args
+ " returnValue=" + returnValue);
}
get/set
}
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="afterAdvice"
class="com.briup.aop.after.AfterAdvice">
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對(duì)象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl" />
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用的是spring的一個(gè)代理對(duì)象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對(duì)象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對(duì)象所實(shí)現(xiàn)的接口 可以有多個(gè)接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個(gè) -->
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
</list>
</property>
</bean>
注意:另外一個(gè)返回后通知接口:AfterReturningAdvice的使用方式和這個(gè)是類似的,但是需要注意它們倆個(gè)之間的區(qū)別
環(huán)繞Advice:
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
例如:
public class AroundAdvice implements MethodInterceptor {
private MyLogger logger;
public Object invoke(MethodInvocation mi) throws Throwable {
// mi.getMethod()獲得將來要調(diào)用的方法的鏡像
//在目標(biāo)方法執(zhí)行之前做日志
logger.log(mi.getMethod().getName() + " is start...");
// 這個(gè)方法就是用來調(diào)用目標(biāo)對(duì)象中的方法的
Object returnValue = mi.proceed();
//在目標(biāo)方法執(zhí)行之后做日志
logger.log(mi.getMethod().getName() + " is end...");
return returnValue;
}
get/set
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="aroundAdvice"
class="com.briup.aop.around.AroundAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對(duì)象 -->
<bean name="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用的是spring的一個(gè)代理對(duì)象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對(duì)象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對(duì)象所實(shí)現(xiàn)的接口 可以有多個(gè)接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個(gè) -->
<property name="interceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</bean>
Throws Advice
//ThrowsAdvice 是一個(gè)空接口毅桃,起標(biāo)識(shí)作用
public interface ThrowsAdvice extends Advice {
}
例如:
public class ThrowingAdvice implements ThrowsAdvice {
private MyLogger logger;
public MyLogger getLogger() {
return logger;
}
public void setLogger(MyLogger logger) {
this.logger = logger;
}
//這里這個(gè)方法的名字一定要叫afterThrowing
//參數(shù)可以是1個(gè)也可以是四個(gè)
//1個(gè)參數(shù)的時(shí)候只能是一個(gè)異常類型的參數(shù)
//如果是4個(gè)參數(shù)的話,參數(shù)的順序也一定要是下面的順序
public void afterThrowing(Method method, Object[] args, Object target,Exception e) {
logger.log(e.getMessage());
}
//下面這樣寫也可以
/*
public void afterThrowing(Exception e) {
logger.log(e.getMessage());
}
*/
get/set
}
配置xml文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="throwAdvice" class="com.briup.aop.throwException.ThrowingAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對(duì)象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用的是spring的一個(gè)代理對(duì)象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對(duì)象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對(duì)象所實(shí)現(xiàn)的接口 可以有多個(gè)接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個(gè) -->
<property name="interceptorNames">
<list>
<value>throwAdvice</value>
</list>
</property>
</bean>
advisor
作用:篩選要攔截(要代理)的方法,之前的advice是把目標(biāo)對(duì)象中的所有方法全部都進(jìn)行代理
指定為advisor的接口為:
public interface PointcutAdvisor {
Pointcut getPointcut();
Advice getAdvice();
}
spring中已經(jīng)給我們提供了實(shí)現(xiàn)類RegexpMethodPointcutAdvisor,在xml中直接配使用就可以了
xml配置文件:
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor 增強(qiáng)器-->
<!-- 作用:篩選要攔截(要代理)的方法 -->
<bean name="advisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增強(qiáng)器要在哪一個(gè)advice起作用-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被攔截的目標(biāo)對(duì)象中的方法(連接點(diǎn)) -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用的是spring的一個(gè)代理對(duì)象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對(duì)象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對(duì)象所實(shí)現(xiàn)的接口 可以有多個(gè)接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice/advisor 可以有多個(gè) -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
AutoProxy 自動(dòng)代理:DefaultAdvisorAutoProxyCreator類的使用
使用原因:在配置文件中我們往往需要給很多個(gè)目標(biāo)對(duì)象設(shè)置代理對(duì)象,那么上面例子的方式就需要每個(gè)目標(biāo)對(duì)象的代理對(duì)象都需要配置一套類似的標(biāo)簽
自動(dòng)代理:可以用很少的配置為xml文件中的目標(biāo)對(duì)象自動(dòng)的生成對(duì)應(yīng)的代理對(duì)象
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:篩選要攔截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被攔截的目標(biāo)對(duì)象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
<value>.*deposit</value>
<value>.*withdraw</value>
</list>
</property>
</bean>
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用自動(dòng)代理的方式 autoproxy -->
<!-- 注意:這不是一個(gè)工廠類,所以不能用過proxy來拿代理對(duì)象 -->
<bean name="proxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
使用自動(dòng)代理的時(shí)候需要注意的方面:
- 當(dāng)前的配置里面一定要有一個(gè)advisor的配置
- 不需要向自動(dòng)代理類中注入任何信息
- 不管目標(biāo)對(duì)象是否實(shí)現(xiàn)了一個(gè)或多接口,自動(dòng)代理的方式都能夠?yàn)樗a(chǎn)生代理對(duì)象(CGLib的方式).
- 從spring容器中拿代理對(duì)象的時(shí)候,需要通過目標(biāo)對(duì)象的名字來拿。
AutoProxyByName 通過名字進(jìn)行自動(dòng)代理:BeanNameAutoProxyCreator類的使用
使用原因:雖然自動(dòng)代理可以很方便的給xml文件中的目標(biāo)對(duì)象設(shè)置對(duì)應(yīng)的代理對(duì)象,但是并不是xml文件中的所有對(duì)象都是我們的目標(biāo)對(duì)象,我們更想希望可以進(jìn)一步篩選出某幾個(gè)對(duì)象為我們的目標(biāo)對(duì)象
名字進(jìn)行自動(dòng)代理:解決了上面的問題,給我們提供了篩選目標(biāo)對(duì)象的配置方式
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對(duì)象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor -->
<!-- 作用:篩選要攔截的方法 -->
<bean name="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice -->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被攔截的目標(biāo)對(duì)象中的方法 -->
<property name="patterns">
<list>
<value>.*bankAction</value>
<value>.*deposit</value>
<value>.*withdraw</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對(duì)象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target2"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<bean name="target3"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對(duì)象 -->
<!-- 這里使用自動(dòng)代理的方式 autoproxybyname -->
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的對(duì)象名字 -->
<property name="beanNames">
<list>
<value>target</value>
<value>target2</value>
<value>dao</value>
</list>
</property>
<!-- 注入advice或者advisor -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
使用自動(dòng)代理的時(shí)候需要注意的方面:
- 當(dāng)前的配置里面有沒有advisor的配置都沒關(guān)系
- 需要向自動(dòng)代理類中注入被代理目標(biāo)對(duì)象的名字已經(jīng)advice或者advisor
- 不管目標(biāo)對(duì)象是否實(shí)現(xiàn)了一個(gè)或多接口,自動(dòng)代理的方式
都能夠?yàn)樗a(chǎn)生代理對(duì)象. - 從spring容器中拿代理對(duì)象的時(shí)候,需要通過目標(biāo)對(duì)象的
名字來拿准夷。