代理模式
代理模式是常用的Java 設(shè)計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系,一個代理類的對象與一個委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實(shí)現(xiàn)服務(wù),而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務(wù)。
-
注意:
委托類對象就是我們后面說到的 目標(biāo)對象(需要【被】代理的對象)
代理類對象就是我們后面說到的 代理對象(目標(biāo)對象就是需要這個對象做為代理) - 按照代理類的創(chuàng)建時期,代理類可分為兩種。
靜態(tài)代理類:
由程序員創(chuàng)建或由特定工具自動生成源代碼,再對其編譯。在程序運(yùn)行前,代理類的.class文件就已經(jīng)存在了。
動態(tài)代理類:在程序運(yùn)行時,運(yùn)用反射機(jī)制動態(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)用委托類對象的方法(也就是目標(biāo)對象方法/被代理對象方法)
//這個方法才是我們真正要執(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)用委托類對象的方法(也就是目標(biāo)對象方法/被代理對象方法)
//這個方法才是我們真正要執(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"));
動態(tài)代理
與靜態(tài)代理類對照的是動態(tài)代理類,動態(tài)代理類的字節(jié)碼在程序運(yùn)行時由Java反射機(jī)制動態(tài)生成,無需程序員手工編寫它的源代碼。動態(tài)代理類不僅簡化了編程工作,而且提高了軟件系統(tǒng)的可擴(kuò)展性,因為Java 反射機(jī)制可以生成任意類型的動態(tài)代理類。 java.lang.reflect 包下面的Proxy類和InvocationHandler 接口提供了生成動態(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的動態(tài)代理中需要使用
public class MyHandler implements InvocationHandler {
//目標(biāo)對象
private Object target;
private StudentLogger logger = new StudentLogger();
public MyHandler() {
}
public MyHandler(Object target) {
this.target = target;
}
// 參數(shù)1 將來所產(chǎn)生的代理對象 Proxy4$
// 參數(shù)2 將來需要調(diào)用到的目標(biāo)對象里面真正的那個方法的鏡像
// 參數(shù)3 將來調(diào)用方法的時候所傳的參數(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)對象
IStudentService service = new StudentServiceImpl();
//service是我們的目標(biāo)對象。
//我們要給目標(biāo)對象產(chǎn)生代理對象。
//目標(biāo)對象service只能單獨(dú)執(zhí)行delete方法。
//但是我們需要的是:先執(zhí)行l(wèi)og日志方法再執(zhí)行delete方法。
//目標(biāo)對象service做不到這個要求,所以我們要給目標(biāo)對象service
//生成一個代理對象去完成這倆個操作.
//怎么給目標(biāo)對象生成代理對象:
//JDK動態(tài)代理的方式
//獲得目標(biāo)對象的Class對象
Class c = service.getClass();
//獲得目標(biāo)對象的類加載器對象
ClassLoader classLoader = c.getClassLoader();
//獲得目標(biāo)對象所實(shí)現(xiàn)的所有接口
Class[] interfaces = c.getInterfaces();
//獲得一個InvocationHandler接口的實(shí)現(xiàn)類對象,并把目標(biāo)對象傳進(jìn)去
InvocationHandler h =
new MyHandler(service);
//參數(shù)1 目標(biāo)對象的類加載器對象
//參數(shù)2 目標(biāo)對象所實(shí)現(xiàn)的所有接口. Class類型數(shù)組
//參數(shù)3 InvocationHandler接口的實(shí)現(xiàn)類對象
IStudentService proxy =
(IStudentService)Proxy.newProxyInstance
(classLoader, interfaces, h);
//這里的proxy是一個實(shí)現(xiàn)了IStudentService接口動態(tài)生成的代理類的對象
proxy.delete();
CGLib代理
JDK實(shí)現(xiàn)動態(tài)代理需要實(shí)現(xiàn)類通過接口定義業(yè)務(wù)方法,對于沒有接口的類,如何實(shí)現(xiàn)動態(tài)代理呢,這就需要CGLib了。CGLib采用了非常底層的字節(jié)碼技術(shù),其原理是通過字節(jié)碼技術(shù)為目標(biāo)對象創(chuàng)建一個子類對象,并在子類對象中攔截所有父類方法的調(diào)用,然后在方法調(diào)用前后調(diào)用后都可以加入自己想要執(zhí)行的代碼。JDK動態(tài)代理與CGLib動態(tài)代理都是Spring AOP的采用的代理方式。
簡單的實(shí)現(xiàn):
這是一個需要被代理的類,也就是父類,通過字節(jié)碼技術(shù)創(chuàng)建這個類的子類,實(shí)現(xiàn)動態(tài)代理。
public class SayHello {
public void say(){
System.out.println("hello everyone");
}
}
注意:在cglib方式中,目標(biāo)對象作為父類,代理對象作為目標(biāo)對象動態(tài)生成的子類對象
- 該類實(shí)現(xiàn)了創(chuàng)建一個類的子類的方法(cglib給一個類生成代理對象的方式)
- getProxy(SuperClass.class)方法通過參數(shù)即父類的class對象,創(chuàng)建出它的一個子類對象,也就是cglib方式的代理對象
- intercept()方法攔截所有目標(biāo)類方法的調(diào)用,
- obj表示將來生成的代理對象,
- method為目標(biāo)類中方法的反射對象,args為方法的動態(tài)入?yún)ⅲ?/li>
- mproxy為代理類(子類)中方法的反射對象。
- mproxy.invokeSuper(obj, args)通過代理類調(diào)用目標(biāo)對象(父類)中的方法。
public class CglibProxy implements MethodInterceptor{
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
//設(shè)置誰是父類
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通過字節(jié)碼技術(shù)動態(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動態(tài)代理和CGLIB代理(不同情況spring會自己選擇一種方式)。
JDK動態(tài)代理和CGLIB代理的對比:
JDK動態(tài)代理:
其代理對象必須是某個接口的實(shí)現(xiàn),它是通過在運(yùn)行期間創(chuàng)建一個接口的實(shí)現(xiàn)類來完成對目標(biāo)對象的代理。
CGLIB代理:
實(shí)現(xiàn)原理類似于JDK動態(tài)代理,只是它在運(yùn)行期間生成的代理對象是針對目標(biāo)類擴(kuò)展的子類。CGLIB是高效的代碼生成包,底層是依靠ASM(開源的java字節(jié)碼編輯類庫)操作字節(jié)碼實(shí)現(xiàn)的。
所以spring會有以下倆種選擇動態(tài)代理實(shí)現(xiàn)方式的情況:
- 如果目標(biāo)對象實(shí)現(xiàn)了接口,默認(rèn)情況下會采用JDK的動態(tài)代理實(shí)現(xiàn)AOP
- 如果目標(biāo)對象沒有實(shí)現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間自動選擇;
認(rèn)識AOP中的一些基本概念,然后在一個一個的例子中,不斷的加強(qiáng)對這些概念的理解同時要能自己表述出每個概念的含義
AOP 面向切面編程
aspect 切面/切面類
joinPoint 連接點(diǎn)
在spring的aop中只有 類中的方法 可以做連接點(diǎn),每一個方法都可以是一個連接點(diǎn).
pointCut 切入點(diǎn)
一組連接點(diǎn)的集合
advice 通知/攔截器
用來控制切面類將來到底是織入到切入點(diǎn)的前面、后面或者是拋異常的時候。
adivsor 增強(qiáng)器
用來篩選類中的哪些方法是我們的連接點(diǎn)(哪些方法需要被攔截).
target 目標(biāo)對象
proxy 代理對象
wave 織入
前置通知(Before advice):
在某連接點(diǎn)(join point)之前執(zhí)行的通知返回后通知(After returning advice):
在某連接點(diǎn)(join point)正常完成后執(zhí)行的通知:例如,一個方法沒有拋出任何異常,正常返回。拋出異常后通知(After throwing advice):
在方法拋出異常退出時執(zhí)行的通知。后通知(After (finally) advice):
當(dāng)某連接點(diǎn)退出的時候執(zhí)行的通知環(huán)繞通知(Around Advice):
包圍一個連接點(diǎn)(join point)的通知,例如事務(wù)的處理,就需要這樣的通知,因為事務(wù)需要在方法前開啟,在方法后提交
在Spring中,Advice是由spring中的幾個接口來指定(就像action類由struts2中的action接口來指定一樣),主要有以下幾種:
Before Advice
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method m, Object[] args, Object target) throws Throwable;
}
例如:
//有一下幾個類或者接口:
Account.java
private int id;
private String name;
private double balance;//余額
get/set
...
AccountDao.java
//取款 賬號減去多少錢
void withdraw(Account acc,double amt);
//存款 賬號加上多少錢
void deposit(Account acc,double amt);
AccountDaoImpl.java
//簡單的實(shí)現(xiàn)接口中的抽象方式
IAccountService.java
//銀行賬號的一個操作:例如轉(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)行一個日志輸出
//前置通知:作用Spring會在目標(biāo)方法執(zhí)行之前調(diào)用這個before方法
public class BeforeAdvice implements MethodBeforeAdvice {
//切面類
private MyLogger logger;
// 參數(shù)1 將來我們需要調(diào)用的目標(biāo)對象中的方法鏡像
// 參數(shù)2 將來調(diào)用方法的時候所傳過來的參數(shù)
// 參數(shù)3 目標(biāo)對象
//將來在調(diào)用目標(biāo)對象方法之前,會先執(zhí)行這個before方法
//在此方法只需要去寫,代理對象要新增的功能呢=,不需要手動調(diào)用目標(biāo)對象
//所執(zhí)行的方法。
public void before(Method m, Object[] args, Object target) throws Throwable {
logger.log(m.getName() + " is invoked..");
/*
* 注意:這里一定不要自己手動的用反射去 調(diào)用這個目標(biāo)對象中的方法,
* 因為spring 會幫我們?nèi)フ{(diào)用的,如果我們這個再去調(diào)用這個方法,
* 那么這這個方法會被調(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">
<!-- 注入切面類對象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對象 -->
<!-- 這里使用的是spring的一個代理對象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對象所實(shí)現(xiàn)的接口 可以有多個接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個 -->
<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)對象中的方法執(zhí)行完返回值
//參數(shù)2 所執(zhí)行方法的鏡像對象
//參數(shù)3 執(zhí)行方法時候所傳的參數(shù)
//參數(shù)4 目標(biāo)對象
//將來調(diào)用目標(biāo)對象的方法之后會執(zhí)行這個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層對象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl" />
<!-- 配置目標(biāo)對象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對象 -->
<!-- 這里使用的是spring的一個代理對象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對象所實(shí)現(xiàn)的接口 可以有多個接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個 -->
<property name="interceptorNames">
<list>
<value>afterAdvice</value>
</list>
</property>
</bean>
注意:另外一個返回后通知接口:AfterReturningAdvice的使用方式和這個是類似的,但是需要注意它們倆個之間的區(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...");
// 這個方法就是用來調(diào)用目標(biāo)對象中的方法的
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">
<!-- 注入切面類對象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對象 -->
<bean name="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對象 -->
<!-- 這里使用的是spring的一個代理對象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對象所實(shí)現(xiàn)的接口 可以有多個接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個 -->
<property name="interceptorNames">
<list>
<value>aroundAdvice</value>
</list>
</property>
</bean>
Throws Advice
//ThrowsAdvice 是一個空接口,起標(biāo)識作用
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;
}
//這里這個方法的名字一定要叫afterThrowing
//參數(shù)可以是1個也可以是四個
//1個參數(shù)的時候只能是一個異常類型的參數(shù)
//如果是4個參數(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">
<!-- 注入切面類對象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置dao層對象 -->
<bean id="dao" class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對象 -->
<!-- 這里使用的是spring的一個代理對象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對象所實(shí)現(xiàn)的接口 可以有多個接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice 可以有多個 -->
<property name="interceptorNames">
<list>
<value>throwAdvice</value>
</list>
</property>
</bean>
advisor
作用:篩選要攔截(要代理)的方法,之前的advice是把目標(biāo)對象中的所有方法全部都進(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">
<!-- 注入切面類對象 -->
<property name="logger" ref="logger"></property>
</bean>
<!-- 配置advisor 增強(qiáng)器-->
<!-- 作用:篩選要攔截(要代理)的方法 -->
<bean name="advisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 注入advice 表示增強(qiáng)器要在哪一個advice起作用-->
<property name="advice" ref="beforeAdvice"></property>
<!-- 注入需要被攔截的目標(biāo)對象中的方法(連接點(diǎn)) -->
<property name="patterns">
<list>
<value>.*bankAction</value>
</list>
</property>
</bean>
<bean id="dao"
class="com.briup.aop.dao.AccountDaoImpl"/>
<!-- 配置目標(biāo)對象 -->
<bean name="target"
class="com.briup.aop.service.AccountServiceImpl">
<property name="accountDao" ref="dao"></property>
</bean>
<!-- 配置代理對象 -->
<!-- 這里使用的是spring的一個代理對象工廠類產(chǎn)生的 -->
<bean name="proxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 注入目標(biāo)對象 -->
<property name="target" ref="target"></property>
<!-- 注入目標(biāo)對象所實(shí)現(xiàn)的接口 可以有多個接口 -->
<property name="proxyInterfaces">
<list>
<value>com.briup.aop.service.IAccountService</value>
</list>
</property>
<!-- 注入advice/advisor 可以有多個 -->
<property name="interceptorNames">
<list>
<value>advisor</value>
</list>
</property>
</bean>
AutoProxy 自動代理:DefaultAdvisorAutoProxyCreator類的使用
使用原因:在配置文件中我們往往需要給很多個目標(biāo)對象設(shè)置代理對象,那么上面例子的方式就需要每個目標(biāo)對象的代理對象都需要配置一套類似的標(biāo)簽
自動代理:可以用很少的配置為xml文件中的目標(biāo)對象自動的生成對應(yīng)的代理對象
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對象 -->
<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)對象中的方法 -->
<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)對象 -->
<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>
<!-- 配置代理對象 -->
<!-- 這里使用自動代理的方式 autoproxy -->
<!-- 注意:這不是一個工廠類,所以不能用過proxy來拿代理對象 -->
<bean name="proxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
</bean>
使用自動代理的時候需要注意的方面:
- 當(dāng)前的配置里面一定要有一個advisor的配置
- 不需要向自動代理類中注入任何信息
- 不管目標(biāo)對象是否實(shí)現(xiàn)了一個或多接口,自動代理的方式都能夠為它產(chǎn)生代理對象(CGLib的方式).
- 從spring容器中拿代理對象的時候,需要通過目標(biāo)對象的名字來拿。
AutoProxyByName 通過名字進(jìn)行自動代理:BeanNameAutoProxyCreator類的使用
使用原因:雖然自動代理可以很方便的給xml文件中的目標(biāo)對象設(shè)置對應(yīng)的代理對象,但是并不是xml文件中的所有對象都是我們的目標(biāo)對象,我們更想希望可以進(jìn)一步篩選出某幾個對象為我們的目標(biāo)對象
名字進(jìn)行自動代理:解決了上面的問題,給我們提供了篩選目標(biāo)對象的配置方式
xml配置文件:
<!-- 配置切面類 -->
<bean name="logger" class="com.briup.aop.aspect.MyLogger"></bean>
<!-- 配置advice -->
<bean name="beforeAdvice"
class="com.briup.aop.before.BeforeAdvice">
<!-- 注入切面類對象 -->
<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)對象中的方法 -->
<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)對象 -->
<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>
<!-- 配置代理對象 -->
<!-- 這里使用自動代理的方式 autoproxybyname -->
<bean name="proxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 注入需要被代理的對象名字 -->
<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)前的配置里面有沒有advisor的配置都沒關(guān)系
- 需要向自動代理類中注入被代理目標(biāo)對象的名字已經(jīng)advice或者advisor
- 不管目標(biāo)對象是否實(shí)現(xiàn)了一個或多接口,自動代理的方式
都能夠為它產(chǎn)生代理對象. - 從spring容器中拿代理對象的時候,需要通過目標(biāo)對象的
名字來拿。