Spring框架學(xué)習(xí)的第三天

代理模式

代理模式是常用的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ù)。

  1. 注意:
    委托類對象就是我們后面說到的 目標(biāo)對象(需要【被】代理的對象)
    代理類對象就是我們后面說到的 代理對象(目標(biāo)對象就是需要這個對象做為代理)
  2. 按照代理類的創(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)生成的子類對象

  1. 該類實(shí)現(xiàn)了創(chuàng)建一個類的子類的方法(cglib給一個類生成代理對象的方式)
  2. getProxy(SuperClass.class)方法通過參數(shù)即父類的class對象,創(chuàng)建出它的一個子類對象,也就是cglib方式的代理對象
  3. intercept()方法攔截所有目標(biāo)類方法的調(diào)用,
  4. obj表示將來生成的代理對象,
  5. method為目標(biāo)類中方法的反射對象,args為方法的動態(tài)入?yún)ⅲ?/li>
  6. mproxy為代理類(子類)中方法的反射對象。
  7. 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        織入
  1. 前置通知(Before advice):
    在某連接點(diǎn)(join point)之前執(zhí)行的通知

  2. 返回后通知(After returning advice):
    在某連接點(diǎn)(join point)正常完成后執(zhí)行的通知:例如,一個方法沒有拋出任何異常,正常返回。

  3. 拋出異常后通知(After throwing advice):
    在方法拋出異常退出時執(zhí)行的通知。

  4. 后通知(After (finally) advice):
    當(dāng)某連接點(diǎn)退出的時候執(zhí)行的通知

  5. 環(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>

使用自動代理的時候需要注意的方面:

  1. 當(dāng)前的配置里面一定要有一個advisor的配置
  2. 不需要向自動代理類中注入任何信息
  3. 不管目標(biāo)對象是否實(shí)現(xiàn)了一個或多接口,自動代理的方式都能夠為它產(chǎn)生代理對象(CGLib的方式).
  4. 從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>

使用自動代理的時候需要注意的方面:

  1. 當(dāng)前的配置里面有沒有advisor的配置都沒關(guān)系
  2. 需要向自動代理類中注入被代理目標(biāo)對象的名字已經(jīng)advice或者advisor
  3. 不管目標(biāo)對象是否實(shí)現(xiàn)了一個或多接口,自動代理的方式
    都能夠為它產(chǎn)生代理對象.
  4. 從spring容器中拿代理對象的時候,需要通過目標(biāo)對象的
    名字來拿。

Spring框架的學(xué)習(xí)第一天
Spring框架的學(xué)習(xí)第二天

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,948評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,958評論 6 342
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,555評論 1 133
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,367評論 11 349
  • 今晚兩部電影,都講愛情!前者小城青年愛上女明星,彼此執(zhí)著,終修正果。《王者之心》兩個相愛的陰差陽錯,崔斯坦落寞寡歡...
    超群老師閱讀 339評論 1 1