結合代碼徹底理解Spring AOP的術語

每本Spring的書在介紹AOP之前,總能有一籮筐的術語,看半天似懂非懂,也就沒興趣再繼續學習下去,其實並沒有這麼複雜。

難懂的定義我們就不貼了,就說說咱們通俗的解釋,下面讓我們結合代碼來理清楚各個術語的含義

一、Advice(通知,叫增強更貼切低啊,也更好理解)

增強可以這麼理解:爲某些方法增加某些功能,這些功能可以安全、事物、日誌等;

增強分爲前置增強(Before Advice):對方法執行前進行的增強

                後置增強(After Advice):對方法執行後進行的增強

                環繞增強(Around Advice):可以理解爲前置增強跟後置增強的合體

                拋出增強(Throw Advice):方法拋出異常後退出執行的增強

                引入增強(Introduction Advice):動態的讓被增強的類實現一個接口

代碼伺候:

先看前置增強、後置增強以及環繞增強,下面是jdk動態代理的代理類

package com.bckj.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class Invo implements InvocationHandler {
    private Object target;

    public Invo(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }
    
    private void before(){
        System.out.println("------method start------");
    }
    private void after(){
        System.out.println("------method end------");
    }
    
}
上面例子中的invoke方法中before方法就叫前置加強,像after這樣的方法就叫後置加強,before跟after兼具就叫環繞加強,下面看Spring Aop的代碼

接口:

package test;

/**
 * Created by DoodleJump on 2017/5/16.
 */
public interface Greeting {
    void sayHello(String name);
}

實現類:

package test;

import org.springframework.stereotype.Component;

/**
 * Created by DoodleJump on 2017/5/16.
 */
@Component
public class GreetingImpl implements Greeting {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello! "+name);
    }
}
前置增強類(實現org.springframework.aop.MethodBeforeAdvice接口):

package test;

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
 * 前置增強
 */
@Component
public class GreetingBeforeAdvice implements MethodBeforeAdvice{

    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(method.getName()+" start!");
    }
}
後置增強類(實現org.springframework.aop.AfterReturningAdvice接口):

package test;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
 * 後置增強
 */
@Component
public class GreetingAfterAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(method.getName()+" end!");
    }
}
環繞增強類(實現org.aopalliance.intercept.MethodInterceptor接口):

package test;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
/**
 * 環繞增強
 */
@Component
public class GreetingAroundAdvice implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        before(methodInvocation);
        Object result = methodInvocation.proceed();
        after(methodInvocation);
        return result;
    }

    private void before(MethodInvocation methodInvocation){
        System.out.println(methodInvocation.getMethod().getName()+" start!");
    }

    private void after(MethodInvocation methodInvocation){
        System.out.println(methodInvocation.getMethod().getName()+" end!");
    }
}
就以環繞增強類爲例,spring配置文件的配置如下

<context:component-scan base-package="test" />

     <bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
          <!--需要代理的接口-->
          <property name="proxyInterfaces" value="test.Greeting"/>
          <!--接口實現類-->
          <property name="target" ref="greetingImpl"/>
          <!--攔截器名稱(也就是增強類名稱)-->
          <property name="interceptorNames">
               <list>
                    <value>greetingAroundAdvice</value>
               </list>
          </property>
     </bean>
測試類:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Created by DoodleJump on 2017/5/16.
 */
public class Client {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/ApplicationContext.xml");
        Greeting greeting = (Greeting)context.getBean("greetingProxy");
        greeting.sayHello("Jack");
    }
}
測試結果:



下面來看拋出增強:

拋出增強類(實現org.springframe.aop.ThrowAdvice接口,該接口僅僅爲標識接口,沒有抽象方法,但是實現類必須得有afterThrowing這個方法,不然spring在反射的時候會拋出java.lang.IllegalArgumentException: At least one handler method must be found in class [class test.GreetingThrowAdvice] 這個異常)

package test;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
 * Created by DoodleJump on 2017/5/16.
 */
@Component
public class GreetingThrowAdvice implements ThrowsAdvice {

    public void afterThrowing(Method method,Object[] args,Object target,Exception e){
        System.out.println("----------Throw Exception----------");
        System.out.println("Target Class: "+target.getClass().getName());
        System.out.println("Method Name: "+method.getName());
        System.out.println("Exception Message: "+e.getMessage());
        System.out.println("-----------------------------------");
    }
}
讓實現類的方法拋出一個異常:

@Component
public class GreetingImpl implements Greeting {
    @Override
    public void sayHello(String name) {
        System.out.println("Hello! "+name);
        throw new RuntimeException("Error");
    }
}
配置文件的話就把增強類名稱改爲greetingThrowAdvice,測試類不變,測試結果:



引入增強:

動態實現的接口:

package test;

/**
 * Created by DoodleJump on 2017/5/16.
 */
public interface Apology {
    void saySorry(String name);
}
引入增強類(繼承org.springframework.aop.support.DelegationIntroductionInterceptor並實現動態接口):

package test;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.stereotype.Component;

/**
 * Created by DoodleJump on 2017/5/16.
 */
@Component
public class GreetingIntroAdvice extends DelegatingIntroductionInterceptor implements Apology {
    @Override
    public void saySorry(String name) {
        System.out.println("Sorry! "+name);
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return super.invoke(invocation);
    }
}
spring配置文件:

<bean id="greetingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
          <!--需要代理的接口-->
          <property name="proxyInterfaces" value="test.Apology"/>
          <!--接口實現類-->
          <property name="target" ref="greetingImpl"/>
          <!--攔截器名稱(也就是增強類名稱)-->
          <property name="interceptorNames">
               <list>
                    <value>greetingIntroAdvice</value>
               </list>
          </property>
          <!--代理目標類(false:jdk代理,true:cglib代理)-->
          <property name="proxyTargetClass" value="true"/>
     </bean>
測試類:

package test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Created by DoodleJump on 2017/5/16.
 */
public class Client {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/ApplicationContext.xml");
        GreetingImpl greetingImpl = (GreetingImpl)context.getBean("greetingProxy");
        greetingImpl.sayHello("Jack");
        Apology apology = (Apology) greetingImpl;
        apology.saySorry("Jack");
    }
}
測試結果:

可以看到生成的代理類動態的實現了Apology這個接口。

二、Weaving(織入):對方法的增強,比如上面所說的前置增強、後置增強以及環繞增強都是織入的表現

根據不同的實現技術,AOP有三種織入的方式:
    a、編譯期織入,這要求使用特殊的Java編譯器。
    b、類裝載期織入,這要求使用特殊的類裝載器。
    c、動態代理織入,在運行期爲目標類添加增強生成子類的方式。
    Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。

三、Introduction(引入):對類的增強,比如上面所說的引入增強


四、Pointcut(切):切入的連接點,即織入或引入發生的那個方法或類


五、Aspect(切面):增強+切點,配置如下



六、Join Point(連接點):所有可以切入的點,切點只是你選擇的部分連接點












發佈了27 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章