Spring框架-AOP詳細學習[轉載]

參考博客:http://www.javashuo.com/article/p-hqdlakuf-ct.htmlspring

--------------------- 
做者:huang-yang 
來源:CSDN 
原文:https://blog.csdn.net/qq_22583741/article/details/79589910 

express

這個大佬寫的太厲害了, 索性直接轉了編程

 

一. AOP概念緩存

   AOP(Aspect Oriented Programming , 面向切面編程), 經過預編譯和運行期動態代理實現程序功能的統一維護的一種技術. AOP是OOP的延續,是Spring框架的重要內容, 是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。安全

  • AOP採起橫向抽取機制,取代了傳統縱向繼承體系重複性代碼
  • 經典應用:事務管理、性能監視、安全檢查、緩存 、日誌等
  • Spring AOP使用純Java實現,不須要專門的編譯過程和類加載器,在運行期經過代理方式向目標類織入加強代碼
  • AspectJ是一個基於Java語言的AOP框架,Spring2.0開始,Spring AOP引入對Aspect的支持,AspectJ擴展了Java語言,提供了一個專門的編譯器,在編譯時提供橫向代碼的織入

 

二. AOP實現原理:app

  • aop底層將採用代理機制進行實現。
  • 接口 + 實現類 :spring採用 jdk 的動態代理Proxy。
  • 實現類:spring 採用 cglib字節碼加強。

 

三. AOP術語 [重點掌握]框架

1.target(目標類):      須要被代理的類。例如:UserService
2.Joinpoint(鏈接點):    所謂鏈接點是指那些可能被攔截到的方法。例如:全部的方法
3.PointCut 切入點:   已經被加強的鏈接點。例如:addUser()
4.advice 通知/加強:     加強代碼。例如:after、before
5. Weaving(織入):       是指把加強advice應用到目標對象target來建立新的代理對象proxy的過程.
6.proxy (代理類) 
7. Aspect(切面):          是切入點pointcut和通知advice的結合
一個線是一個特殊的面。
一個切入點和一個通知,組成一個特殊的面。 ide

也就是說,咱們最終會得到的, 是advice 和 pointcut 結合起來的proxy代理類
函數式編程

 

 

四. AOP 實現方式函數

             AOP實現方式包括  手動模式,  半自動模式,  全自動模式

    4.1.手動模式: 

         4.1.1JDK動態代理

 (1)目標類:  接口+實現類

public interface ProductService {
    public void addProduct();
    public void updateProduct();
    public void deleteProduct();
}

public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct() {
        System.out.println("add Product");
    }

    @Override
    public void updateProduct() {
        System.out.println("update Product");
    }

    @Override
    public void deleteProduct() {
        System.out.println("delete Product");
    }
}

(2)切面類:  用於實現通知/加強

public class LoggerAspect {
    public void before(){
        System.out.println("在添加商品前作些什麼");
    }
    public void after(){
        System.out.println("在添加商品後作些什麼");
    }
}

(3)工廠類:  編寫工廠生成代理

public class ProductLogFactory {

    public static ProductService createService(){
        //目標類
        final ProductService productService = new ProductServiceImpl();
        //切面類
        final LoggerAspect loggerAspect = new LoggerAspect();

        //代理類
        ProductService proxyService = (ProductService) Proxy.newProxyInstance(
                LoggerAspect.class.getClassLoader(),  //參數1
                productService.getClass().getInterfaces(),  //參數2
                new InvocationHandler() {                  //參數3
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        //前執行
                        loggerAspect.before();

                        //執行目標類方法
                        Object obj = method.invoke(productService,args);

                        //後執行
                        loggerAspect.after();

                        return obj;
                    }
                }
        );
        return proxyService;      //返回建立的代理類對象
    }
}

參數1:loader ,類加載器,動態代理類 運行時建立,任何類都須要類加載器將其加載到內存。
* 通常狀況:當前類.class.getClassLoader();
* 目標類實例.getClass().get...
---------------------

參數2:Class[] interfaces 代理類須要實現的全部接口
* 方式1:目標類實例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口
* 方式2:new Class[]{UserService.class}
* 例如:jdbc 驅動 --> DriverManager 得到接口 Connection
---------------------

參數3:InvocationHandler 處理類,接口,必須進行實現類,通常採用匿名內部
* 提供 invoke 方法,代理類的每個方法執行時,都將調用一次invoke
* 參數31:Object proxy :代理對象
* 參數32:Method method : 代理對象當前執行的方法的描述對象(反射)
* 執行方法名:method.getName()
* 執行方法:method.invoke(對象,實際參數)
* 參數33:Object[] args :方法實際參數

 

(4)測試:

使用被織入了切面方法的代理類的方法

    @Test
    public void test3(){
        ProductService ps = ProductLogFactory.createService();
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

 

          4.1.1CGLIB字節碼加強

  • 沒有接口,只有實現類。
  • 採用字節碼加強框架 cglib,在運行時 建立目標類的子類,從而對目標類進行加強。
public class ProductLogFactory1 {

    public static ProductServiceImpl createService(){
        //目標類
        final ProductServiceImpl productService = new ProductServiceImpl();
        //切面類
        final LoggerAspect loggerAspect = new LoggerAspect();
        //代理類, 採用cglib, 底層建立目標類的子類
        //核心類
        Enhancer enhancer = new Enhancer();
        //設置父類爲目標類
        enhancer.setSuperclass(productService.getClass());
        //設置回調函數
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

                //前執行
                loggerAspect.before();

                //執行目標類的方法
                Object obj = method.invoke(productService,args);
                //執行代理類的父類  目標類是代理類的父類
                methodProxy.invokeSuper(proxy,args);

                //後執行
                loggerAspect.after();

                return obj;
            }
        });
        //建立代理
        ProductServiceImpl proxyService = (ProductServiceImpl)enhancer.create();
        
        return proxyService;
    }
}

  用法和上面相似,得到代理類對象後, 使用其中已經織入切面類方法的方法

 

    4.2.半自動方式:    讓Spring建立代理對象, 從Spring容器中手動得到代理對象

(1)目標類:  接口+實現類

public interface ProductService {
    public void addProduct();
    public void updateProduct();
    public void deleteProduct();
}

public class ProductServiceImpl implements ProductService {
    @Override
    public void addProduct() {
        System.out.println("add Product");
    }

    @Override
    public void updateProduct() {
        System.out.println("update Product");
    }

    @Override
    public void deleteProduct() {
        System.out.println("delete Product");
    }
}

(2)切面類:  與前面的切面類不一樣, 這個切面類實現了MethodInterceptor接口, 環繞通知

public class LoggerAspect implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        //前執行
        System.out.println("在操做商品前作些什麼");
        
        //手動執行目標方法
        Object obj = methodInvocation.proceed();
        
        //後執行
        System.out.println("在操做商品後作些什麼");
        return obj;
    }
}

(3) Spring配置:  要在applicationContext.xml文件中聲明目標類,切面類  並讓Spring幫忙生成代理類

    <!--目標類 -->
    <bean id="productServiceId" class="service.ProductServiceImpl"></bean>

    <!-- 切面類-->
    <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

   <!-- 代理類 -->
    <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="interfaces" value="service.ProductService"></property>
        <property name="target" ref="productServiceId"></property>
        <property name="interceptorNames" value="loggerAspectId"></property>
    </bean>

 

* 經過使用工廠bean FactoryBean ,底層調用 getObject() 返回特殊bean
* ProxyFactoryBean 用於建立代理工廠bean,生成特殊代理對象
                          參數1: interfaces : 肯定接口們
                                                        經過<array>能夠設置多個值
                                                        只有一個值時,value=""
                          參數2: target : 肯定目標類
                          參數3:  interceptorNames : 通知 切面類的名稱,類型String[],若是設置一個值 value=""
                          可選參數4:  optimize :強制使用cglib
                                             <property name="optimize" value="true"></property>
底層機制
若是目標類有接口,採用jdk動態代理
若是沒有接口,採用cglib 字節碼加強
若是聲明 optimize = true ,不管是否有接口,都採用cglib

 

(4)測試

    @Test
    public void test3(){
        String xmlPath = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
        ProductService ps = (ProductService)context.getBean("proxyServiceId");
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

 

     4.3.全自動方式:   從Spring容器得到目標類, 經過配置aop, 讓Spring自動生成代理

(1)目標類: 接口+實現類 這裏就不放代碼了

(2)切面類:  與半自動方式類似

(3)xml配置:

    <aop:config>
        <aop:pointcut id="productPointCut" expression="execution(* service.ProductServiceImpl.*(..))"/>
        <aop:advisor advice-ref="loggerAspectId" pointcut-ref="productPointCut"/>
    </aop:config>

aop編程 :
             3.1 導入命名空間
             3.2 使用 <aop:config>進行配置
                                    proxy-target-class="true" 聲明時使用cglib代理
                                    <aop:pointcut> 切入點 ,從目標對象得到具體方法
                                    <aop:advisor> 特殊的切面,只有一個通知 和 一個切入點
                                     advice-ref 通知引用
                                     pointcut-ref 切入點引用
             3.3 切入點表達式
                                      execution(* com.itheima.c_spring_aop.*.*(..))
                                     選擇方法 返回值任意 包 類名任意 方法名任意 參數任意

(4)測試:

    @Test
    public void test3(){
        String xmlPath = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath);
        ProductService ps = (ProductService)context.getBean("productServiceId");
        ps.addProduct();
        ps.deleteProduct();
        ps.updateProduct();
    }

注意這裏,再也不是獲取由Spring容器建立的代理類了, 而是直接獲取ProductService的bean, 生成代理的定義幾乎徹底不可視了

 

4.4. 另外一種全自動方式: 

(1)目標類: 接口+實現類

(2)切面類:  切面類再也不實現某個接口, 將獲取切入點的操做交給了Spring

public class LoggerAspect {

    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {

        //前執行
        System.out.println("在操做商品前作些什麼");

        //手動執行目標方法
        Object obj = joinPoint.proceed();

        //後執行
        System.out.println("在操做商品後作些什麼");
        return obj;
    }
}

(3)xml配置:  指定切面類的bean, 指定切入點是ProductService的全部方法, 指定切面方法爲log

    <!-- Aspect -->
    <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean>

    <!-- aop -->
    <aop:config>
        <aop:pointcut id="loggerCutpoint"
                      expression="execution(* service.ProductService.*(..))"/>
        <aop:aspect id="logAspect" ref="loggerAspectId">
            <aop:around pointcut-ref="loggerCutpoint" method="log"/>
        </aop:aspect>
    </aop:config>
相關文章
相關標籤/搜索