04-SpringMVC 編程方式實現 AOP

相關

  • Spring支持5種類型的增強或通知(advice)
    • before、around、after、afterReturnning

前提

  • Intellij
  • 創建Maven archtype webapp
  • SpringMVC 項目 而非 spring boot
  • 添加 spring-webmvn dependency

案例

service 業務邏輯層
public interface Service {

    void print(String str);
    //void say(String str);
}

public class ServiceImpl implements Service {

    public void print(String str) {
        System.out.println("我是業務方法"+str);
    }
    //public void say(String str) {
    //   System.out.println("222"+str);
    //}
}

aop 定義增強業務

** before 增強 **

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * Created by weixin:javajidi_com.
 * 方法執行前的邏輯,稱前置通知,
 * 通過編程實現接口
 */
public class BeforeAdvice implements MethodBeforeAdvice{
    /**
    * method: target 方法
    * objects: target 方法需要的參數
    * o:目的所要增強的 target 對象
    **/
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("BeforeAdvice方法執行前");
        System.out.println(method.getName()+";"+o.getClass());
    }
}

round 環繞增強

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 實現接口為 MethodInterceptor extends Advice
 */
public class RoundAdvice implements MethodInterceptor {

    //注意參數為:MethodInvocation
    //通過 它可以拿到參數信息
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("Roundadvice方法執行前");
        System.out.println(methodInvocation.getArguments()[0]);//可以獲取目標方法的參數值
        Object result=methodInvocation.proceed();//調用目標對象的方法
        System.out.println("RoundAdvice方法執行完成了");
        return result;
    }
}

afterreturnning 增強

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

//實現 接口
public class AfterAdvice implements AfterReturningAdvice {

    /**
    * o: target 方法執行返回結果
    * method: target 方法
    * objects: target 方法參數
    * o1: 要增強 的 target 對象
    **/
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("AfterAdvice方法執行完成了");
        System.out.println(method.getName()+";"+o1.getClass());
    }
}

Test1

import org.springframework.aop.framework.ProxyFactory;
import spring4.Service;
import spring4.ServiceImpl;

/**
 * test1: 不通過 spring bean 配置文件
 */
public class Test {

    public static void main(String[] arg){
        
        //非 Spring上下文 控制 bean, 全部自定義(new)
        
        Service service=new ServiceImpl();//
        //使用代理工廠為目標對象創建代理,
        //我們自己的advice邏輯
        ProxyFactory proxyFactoryBean=new ProxyFactory();
        proxyFactoryBean.setTarget(service);//設置目標對象
        
        BeforeAdvice beforeAdvice = new BeforeAdvice();
        proxyFactoryBean.addAdvice(beforeAdvice);//為目標對象織入增強
        
        AfterAdvice afterAdvice = new AfterAdvice();
        proxyFactoryBean.addAdvice(afterAdvice);
        
        
        RoundAdvice roundAdvice = new RoundAdvice();
        proxyFactoryBean.addAdvice(roundAdvice);
        
        Service proxy=(Service)proxyFactoryBean.getProxy();
        proxy.print("test");
    }
}

classpath 路徑下配置 spring.xml 實現

//在resources 目錄 創建spring.xml文件


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 由Spring 上下文 創建并裝配 所有的對象-->
    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">
            <list>
                <value>beforeAdvice</value>

                <value>afterAdvice</value>

                <value>roundAdvice</value>
            </list>
        </property>
        <property name="target" ref="service"></property>
    </bean>

</beans>

test2 通過bean 配置文件和Spring上下文

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工廠為目標對象創建代理,并織入我們自己的advice邏輯
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//設置目標對象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//為目標對象織入增強
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");
        
        //獲取上下文中的bean
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        Service service=applicationContext.getBean("serviceProxy",Service.class);
        service.print("test");
    }
}

**運行結果-增強被織入到目標類的所有方法中 **

BeforeAdvice方法執行前
print;class spring4.ServiceImpl
Roundadvice方法執行前
test
我是業務方法test
RoundAdvice方法執行完成了
AfterAdvice方法執行完成了
print;class spring4.ServiceImpl

更細粒度-指定不同方法的增強
  • spring 通過org.springframework.aop.Pointcut接口描述切點,Pointcut由ClassFilter和MethodMatcher構成。
  • 通過ClassFilter定位到某些特定類上,通過MethodMatcher定位到某些特定方法上
  • 這樣Pointcut就擁有了描述某些類的某些特定方法的能力。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>

    <!--通過正則表達定義增強具體的執行位置-->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <!--正則表達式用來表示增加添加到哪些類的哪些方法-->
        <property name="pattern" value="spring4\..*.print.*"/><!--表示應用到spring4包下所有類中的所有print開頭的方法上-->
    </bean>
    <bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">
            <list>
                <value>advisor</value><!--連接到具體執行位置-->

            </list>
        </property>
        <property name="target" ref="service"></property>
    </bean>

</beans>

test3
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

/**
 * Created by weixin:javajidi_com.
 */
public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工廠為目標對象創建代理,并織入我們自己的advice邏輯
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//設置目標對象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//為目標對象織入增強
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        //直接從上下文獲取 proxy
        Service service=applicationContext.getBean("serviceProxy",Service.class);
        service.print("print");
        //添加多余驗證代碼,驗證其不會被增強
        service.say("say");
    }
}

結果

BeforeAdvice方法執行前
print;class spring4.ServiceImpl
我是業務方法print
say:say

動態生成 proxy

  • 當有很多 目的target 類需要被增強時,需要手動配置很多 proxy
  • Spring 提供自動代理機制
  • 在內部Spring 使用 BeanPostProcessor 自動完成此任務
  • BeanPostProcessor 的實現類: 根據 某些規則 自動在容器實例化 bean時為符合規則條件的bean 生成代理
    • BeanNameAutoProxyCreator
      • 允許為一組特定配置名的Bean自動創建代理
    • DefaultAdvisorAutoProxyCreator
      • 它會對容器中所有的Advisor進行掃描,自動將這些切面應用到匹配的Bean中(即為目標Bean創建代理實例)
    • AnnotationAwareAspectjAutoProxyCreator
      • 為包含AspectJ注解的Bean自動創建代理實例
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="beforeAdvice" class="spring4.aop.BeforeAdvice"/>
    <bean id="afterAdvice" class="spring4.aop.AfterAdvice"/>
    <bean id="roundAdvice" class="spring4.aop.RoundAdvice"/>
    <bean id="service" class="spring4.ServiceImpl"/>

    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="advice" ref="beforeAdvice"/>
        <!--正則表達式用來表示增加添加到哪些類的哪些方法-->
        <property name="pattern" value="spring4\..*.print.*"/><!--表示應用到spring4包下所有類中的所有print開頭的方法上-->
    </bean>
    
    <!--會掃描所有容器中的advisor, 如上面定義的 advisor, 這個advisor 可以匹配到 若干個類
    若出現 advisor2, advisor3, advisor4 得寫 相應個數 proxyFactoryBean
    然后自動為這些advisor要應用的bean生成代理-->
    <!--bean 即要被增強的 target 類-->
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

</beans>

test 4

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import spring4.Service;

/**
 * Created by weixin:javajidi_com.
 */
public class Test {

    public static void main(String[] arg){
//        Service service=new ServiceImpl();
//        //使用代理工廠為目標對象創建代理,并織入我們自己的advice邏輯
//        ProxyFactory proxyFactoryBean=new ProxyFactory();
//        proxyFactoryBean.setTarget(service);//設置目標對象
//        proxyFactoryBean.addAdvice(new BeforeAdvice());//為目標對象織入增強
//        proxyFactoryBean.addAdvice(new AfterAdvice());
//        proxyFactoryBean.addAdvice(new RoundAdvice());
//        Service proxy=(Service)proxyFactoryBean.getProxy();
//        proxy.print("test");

        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:spring.xml");
        //要注意這里 bean name,可以debug,  
        //applicationContext.getBeanNamesForType(Service.class) 拿到 bean 名字
        Service service=applicationContext.getBean("service",Service.class);
        service.print("print");
        service.say("say");
    }
}

參考: http://www.lxweimin.com/p/1dd6a26c881b

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

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,954評論 6 342
  • 如下是整篇文章的結構,所需閱讀時間大約20min Spring簡介 Spring框架由Rod Johnson開發,...
    李序鍇閱讀 903評論 0 15
  • 什么是Spring Spring是一個開源的Java EE開發框架。Spring框架的核心功能可以應用在任何Jav...
    jemmm閱讀 16,550評論 1 133
  • 最近好像陷入一場遠距離戀愛,連笑容都很甜蜜。時間也隨著漸漸遠去,好像閑暇的時光稍縱即逝,原本存了好多電視和電影的...
    yumiruirui閱讀 160評論 0 0