最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring超詳細講解AOP面向切面

 更新時間:2022年10月17日 10:08:01   作者:一根頭發(fā)學一年  
面向?qū)ο缶幊淌且环N編程方式,此編程方式的落地需要使用“類”和 “對象”來實現(xiàn),所以,面向?qū)ο缶幊唐鋵嵕褪菍?nbsp;“類”和“對象” 的使用,面向切面編程,簡單的說,就是動態(tài)地將代碼切入到類的指定方法、指定位置上的編程思想就是面向切面的編程

說明:基于atguigu學習筆記。

簡介

AOP(Aspect Oriented Programming)是一種面向切面的編程思想。不同于面向?qū)ο罄锏睦^承思想,當需要為多個不具有繼承關系的對象引人同一個公共行為時,也就是把程序橫向看,尋找切面,插入公共行為。

AOP目的是為了些把影響了多個類的公共行為抽取到一個可重用模塊里,不通過修改源代碼方式,在主干功能里面添加新功能,降低模塊間的耦合度,增強代碼的可操作性和可維護性。

例如,每次用戶請求我們的服務接口,都要進行權限認證,看看是否登錄,就可以在不改變原來接口代碼的情況下,假如認證這個新功能。

Spring AOP底層使用了代理模式。下面具體了解一下。

AOP底層原理

代理概念

所謂代理,也就是讓我們的代理對象持有原對象,在執(zhí)行原對象目標方法的前后可以執(zhí)行額外的增強代碼。

代理對象需要是原對象接口的實現(xiàn)或原對象的子類,這樣就可以在對象引用處直接替換原對象。

代理方式分靜態(tài)代理和動態(tài)代理,區(qū)別在于代理對象生成方式不同

靜態(tài)代理:在編譯期增強,生成可見的代理class,使用代理類替換原有類進行調(diào)用。

動態(tài)代理:在運行期增強,內(nèi)存中動態(tài)生成代理類,使用反射動態(tài)調(diào)用原對象方法。

在spring中使用的是JDK、CGLIB動態(tài)代理對象。

JDK動態(tài)代理:必須基于接口,即生成的代理對象是對原對象接口的實現(xiàn),相當于替換了實現(xiàn)類,面向?qū)ο笾薪涌诳梢蕴鎿Q實現(xiàn)類。

CGLIB動態(tài)代:理基于繼承,即生成的代理對象是原對象的子類,面向?qū)ο笾凶宇惪梢蕴鎿Q父類。

JDK動態(tài)代理實現(xiàn)

使用 JDK 動態(tài)代理,使用反射包里 java.lang.refelft.Proxy 類的 newProxyInstance 方法創(chuàng)建代理對象。

源碼如下

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    Objects.requireNonNull(h);
    final Class<?> caller = System.getSecurityManager() == null
                                ? null
                                : Reflection.getCallerClass();
    /*
     * Look up or generate the designated proxy class and its constructor.
     */
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

方法有三個參數(shù):

第一參數(shù),類加載器

第二參數(shù),增強方法所在的類,這個類實現(xiàn)的接口,支持多個接口

第三參數(shù),實現(xiàn)這個接口 InvocationHandler,創(chuàng)建代理對象,寫增強的部分

下面以JDK動態(tài)代理為例,具體步驟。

1.創(chuàng)建接口,定義方法

public interface UserDao {
	public int add(int a,int b);
	public String update(String id);
}

2.創(chuàng)建接口實現(xiàn)類,實現(xiàn)方法

public class UserDaoImpl implements UserDao {
	@Override
	public int add(int a, int b) {
		return a+b;
	}
	@Override
	public String update(String id) {
		return id;
	} 
}

3.使用 Proxy 類創(chuàng)建接口代理對象

public class JDKProxy {
	public static void main(String[] args) {
		//創(chuàng)建接口實現(xiàn)類代理對象
		Class[] interfaces = {UserDao.class};
		// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
		// @Override
		// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// return null;
		// }
		// });
		UserDaoImpl userDao = new UserDaoImpl();
		UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
		int result = dao.add(1, 2);
		System.out.println("result:"+result);
		} 
	}
	//創(chuàng)建代理對象代碼
	class UserDaoProxy implements InvocationHandler {
		//1 把創(chuàng)建的是誰的代理對象,把誰傳遞過來
		//有參數(shù)構造傳遞
		private Object obj;
		public UserDaoProxy(Object obj) {
		this.obj = obj;
		}
		//增強的邏輯
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//方法之前
		System.out.println("方法之前執(zhí)行...."+method.getName()+" :傳遞的參數(shù)..."+ Arrays.toString(args));
		//被增強的方法執(zhí)行
		Object res = method.invoke(obj, args);
		//方法之后
		System.out.println("方法之后執(zhí)行...."+obj);
		return res;
	} 
}

Spring中的AOP

相關術語

1.連接點(Join point): 類里面可以被增強的方法。

2.切入點:真正被增強的方法。

3.通知:實際增強處理的邏輯。

AOP框架匯總通知分為以下幾種:

  • 前置通知@Before
  • 后置通知@AfterReturning
  • 環(huán)繞通知@Around
  • 異常通知@AfterThrowing
  • 最終通知@After

4.切面:把通知應用到切入點的過程,是一個動作。

AspectJ

AspectJ 不是 Spring 組成部分,獨立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,進行 AOP 操作。

基于 AspectJ 實現(xiàn) AOP 操作可以有兩種方式:基于xml配置文件、基于注解。

要使用AspectJ,首先要引入相關依賴:

	 <dependency>
         <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
      </dependency>

使用AspectJ時,會尋找切入點,這時候會用到切入點表示,為了知道對哪個類里面的哪個方法進行增強。

語法結構: execution([權限修飾符] [返回類型] [類全路徑] [方法名稱]([參數(shù)列表]) )

舉例 1:對 com.example.dao.BookDao 類里面的 add 進行增強
execution(* com.example.dao.BookDao.add(..))

舉例 2:對 com.example.dao.BookDao 類里面的所有的方法進行增強
execution(* com.example.dao.BookDao.* (..))

舉例 3:對 com.example.dao 包里面所有類,類里面所有方法進行增強
execution(* com.example.dao.*.* (..))

實現(xiàn)AOP

1.創(chuàng)建項目,引入依賴

依賴如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>spring-demo02</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>

2.創(chuàng)建類

創(chuàng)建一個自己的類,寫一個要增強的方法,并使用注解管理bean

package com.example;
import org.springframework.stereotype.Component;
@Component
public class User {
    public void add () {
        System.out.println("user add method...");
    }
}

3.創(chuàng)建代理增強類

創(chuàng)建增強類,使用@Aspect注解。

在增強類里面,創(chuàng)建方法,讓不同方法代表不同通知類型,此例創(chuàng)建前置通知使用@Before

package com.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    @Before(value = "execution(* com.example.User.add())")
    public void before () {
        System.out.println("proxy before...");
    }
}

4.xml配置

開啟注解掃描和Aspect 生成代理對象

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 開啟注解掃描 -->
    <context:component-scan base-package="com.example"></context:component-scan>
    <!-- 開啟 Aspect 生成代理對象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5.測試類

package com.example;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ap = new ClassPathXmlApplicationContext("bean1.xml");
        User user = ap.getBean("user", User.class);
        user.add();
    }
}

結果先輸出proxy before…,再輸出user add method…。說明我們的前置通知確實再被增強方法之前執(zhí)行成功。

不同通知類型實現(xiàn)

下面把五種通知都實現(xiàn)看一下順序,修改我們的代理類如下:

package com.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    /**
     * 前置通知
     */
    @Before(value = "execution(* com.example.User.add())")
    public void before () {
        System.out.println("proxy before...");
    }
    /**
     * 后置通知
     */
    @AfterReturning(value = "execution(* com.example.User.add())")
    public void afterReturning() {
        System.out.println("proxy afterReturning...");
    }
    /**
     * 最終通知
     */
    @After(value = "execution(* com.example.User.add())")
    public void after() {
        System.out.println("proxy after...");
    }
    /**
     * 異常通知
     */
    @AfterThrowing(value = "execution(* com.example.User.add())")
    public void afterThrowing() {
        System.out.println("proxy afterThrowing...");
    }
    /**
     * 環(huán)繞通知
     */
    @Around(value = "execution(* com.example.User.add())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 環(huán)繞之前
        System.out.println("proxy around before...");
        proceedingJoinPoint.proceed();
        // 環(huán)繞之后
        System.out.println("proxy around after...");
    }
}

執(zhí)行結果如下:

proxy around before...
proxy before...
user add method...
proxy around after...
proxy after...
proxy afterReturning...

相同的切入點抽取

上面代碼可以看到我們的通知value是相同的,這時候可以抽取出來公用,改寫代理類如下代碼如下:

package com.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    @Pointcut(value = "execution(* com.example.User.add())")
    public void pointDemo() {}
    /**
     * 前置通知
     */
    @Before(value = "pointDemo()")
    public void before () {
        System.out.println("proxy before...");
    }
    /**
     * 后置通知
     */
    @AfterReturning(value = "pointDemo()")
    public void afterReturning() {
        System.out.println("proxy afterReturning...");
    }
    /**
     * 最終通知
     */
    @After(value = "pointDemo()")
    public void after() {
        System.out.println("proxy after...");
    }
    /**
     * 異常通知
     */
    @AfterThrowing(value = "pointDemo()")
    public void afterThrowing() {
        System.out.println("proxy afterThrowing...");
    }
    /**
     * 環(huán)繞通知
     */
    @Around(value = "pointDemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 環(huán)繞之前
        System.out.println("proxy around before...");
        proceedingJoinPoint.proceed();
        // 環(huán)繞之后
        System.out.println("proxy around after...");
    }
}

增強類優(yōu)先級

有多個增強類多同一個方法進行增強,設置增強類優(yōu)先級。

在增強類上面添加注解 @Order(數(shù)字類型值),數(shù)字類型值越小優(yōu)先級越高。

@Component
@Aspect
@Order(1)
public class UserProxy

完全使用注解開發(fā)

創(chuàng)建配置類,不需要創(chuàng)建 xml 配置文件。

package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

測試類:

package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AopTest {
    public static void main(String[] args) {
        ApplicationContext ap = new AnnotationConfigApplicationContext(AopConfig.class);
        User user = ap.getBean(User.class);
        user.add();
    }
}

到此這篇關于Spring超詳細講解AOP面向切面的文章就介紹到這了,更多相關Spring面向切面內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • SpringBoot如何自動生成API文檔詳解

    SpringBoot如何自動生成API文檔詳解

    網(wǎng)絡程序正朝著移動設備的方向發(fā)展,前后端分離、APP,最好的交互交互方式莫過于通過API接口實現(xiàn),這篇文章主要給大家介紹了關于SpringBoot如何自動生成API文檔的相關資料,需要的朋友可以參考下
    2021-07-07
  • Java源碼重讀之ConcurrentHashMap詳解

    Java源碼重讀之ConcurrentHashMap詳解

    ConcurrentHashMap(CHM)是日常開發(fā)中使用頻率非常高的一種數(shù)據(jù)結構。本文將從源碼角度帶大家深入了解一下ConcurrentHashMap的使用,需要的可以收藏一下
    2023-05-05
  • SpringMVC的組件之HandlerExceptionResolver詳解

    SpringMVC的組件之HandlerExceptionResolver詳解

    這篇文章主要介紹了SpringMVC的組件之HandlerExceptionResolver詳解,不管是在處理請求映射(HandlerMapping),還是在請求被處理(Handler)時拋出的異常,DispatcherServlet都會委托給HandlerExceptionResolver進行異常處理,該接口只有一個方法,需要的朋友可以參考下
    2023-10-10
  • SpringBoot多種生產(chǎn)打包方式詳解

    SpringBoot多種生產(chǎn)打包方式詳解

    生產(chǎn)上發(fā)布?Spring?Boot?項目時,流程頗為繁瑣且低效,但凡代碼有一丁點改動,就得把整個項目重新打包部署,耗時費力不說,生成的?JAR?包還特別臃腫,體積龐大,本文給大家介紹了SpringBoot多種生產(chǎn)打包方式,需要的朋友可以參考下
    2025-01-01
  • SpringBoot中自動配置原理解析

    SpringBoot中自動配置原理解析

    SpringBoost是基于Spring框架開發(fā)出來的功能更強大的Java程序開發(fā)框架,本文將以廣角視覺來剖析SpringBoot自動配置的原理,涉及部分Spring、SpringBoot源碼,需要的可以參考下
    2023-11-11
  • Java數(shù)據(jù)結構之棧與綜合計算器的實現(xiàn)

    Java數(shù)據(jù)結構之棧與綜合計算器的實現(xiàn)

    這篇文章主要為大家詳細介紹了Java數(shù)據(jù)結構中棧與綜合計算器的實現(xiàn),文中的示例代碼講解詳細,具有一定的學習價值,感興趣的小伙伴可以了解一下
    2022-10-10
  • Java 8中如何獲取參數(shù)名稱的方法示例

    Java 8中如何獲取參數(shù)名稱的方法示例

    這篇文章主要給大家介紹了在Java 8中如何獲取參數(shù)名稱的方法,文中給出了詳細的介紹和方法示例,相信對大家的理解和學習具有一定的參考借鑒價值,有需要的朋友可以參考學習,下面來一起看看吧。
    2017-01-01
  • Java中實現(xiàn)接口與繼承的區(qū)別及說明

    Java中實現(xiàn)接口與繼承的區(qū)別及說明

    這篇文章主要介紹了Java中實現(xiàn)接口與繼承的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Java多線程程序中synchronized修飾方法的使用實例

    Java多線程程序中synchronized修飾方法的使用實例

    synchronized關鍵字主要北用來進行線程同步,這里我們主要來演示Java多線程程序中synchronized修飾方法的使用實例,需要的朋友可以參考下:
    2016-06-06
  • Idea之沒有網(wǎng)絡的情況下創(chuàng)建SpringBoot項目的方法實現(xiàn)

    Idea之沒有網(wǎng)絡的情況下創(chuàng)建SpringBoot項目的方法實現(xiàn)

    本文主要介紹了Idea之沒有網(wǎng)絡的情況下創(chuàng)建SpringBoot項目的方法實現(xiàn),文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-09-09

最新評論

东乡族自治县| 汶川县| 达日县| 无极县| 兴山县| 毕节市| 共和县| 陇西县| 闽侯县| 武隆县| 寿阳县| 马边| 台南市| 苍南县| 安丘市| 平南县| 孟连| 沿河| 额敏县| 乐亭县| 南充市| 平塘县| 婺源县| 兴安盟| 墨脱县| 阿勒泰市| 桃园县| 西昌市| 淅川县| 突泉县| 梓潼县| 连江县| 施甸县| 清流县| 阿坝| 汶上县| 黄龙县| 轮台县| 林甸县| 兰州市| 平度市|