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

代理模式

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

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

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

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

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

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

  5. 環(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í)候需要注意的方面:

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末钥飞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衫嵌,更是在濱河造成了極大的恐慌读宙,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渐扮,死亡現(xiàn)場離奇詭異论悴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)墓律,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門膀估,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耻讽,你說我怎么就攤上這事察纯。” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵饼记,是天一觀的道長香伴。 經(jīng)常有香客問我,道長具则,這世上最難降的妖魔是什么即纲? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮博肋,結(jié)果婚禮上低斋,老公的妹妹穿的比我還像新娘。我一直安慰自己匪凡,他們只是感情好膊畴,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著病游,像睡著了一般唇跨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衬衬,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天买猖,我揣著相機(jī)與錄音,去河邊找鬼滋尉。 笑死政勃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兼砖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼既棺,長吁一口氣:“原來是場噩夢啊……” “哼讽挟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起丸冕,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤耽梅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胖烛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體眼姐,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年佩番,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了众旗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡趟畏,死狀恐怖贡歧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤利朵,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布律想,位于F島的核電站,受9級(jí)特大地震影響绍弟,放射性物質(zhì)發(fā)生泄漏技即。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一樟遣、第九天 我趴在偏房一處隱蔽的房頂上張望而叼。 院中可真熱鬧,春花似錦年碘、人聲如沸澈歉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽埃难。三九已至,卻和暖如春涤久,著一層夾襖步出監(jiān)牢的瞬間涡尘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工响迂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留考抄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓蔗彤,卻偏偏與公主長得像川梅,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子然遏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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

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