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

Spring?AOP注解配置與XML配置雙實戰(zhàn)教程

 更新時間:2025年11月10日 16:45:57   作者:程序員三明治  
本文詳細(xì)介紹了面向切面編程(AOP)的概念、快速入門、核心概念、切點確定、通知分類、獲取被增強(qiáng)方法相關(guān)信息、應(yīng)用案例、XML配置、多切面順序問題以及AOP原理中的動態(tài)代理,通過實例展示了如何在Spring框架中使用AOP進(jìn)行方法增強(qiáng),感興趣的朋跟隨小編一起看看吧

1. AOP

1.1 概念

? AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程。他是一種可以在不修改原來的核心代碼的情況下給程序動態(tài)統(tǒng)一進(jìn)行增強(qiáng)的一種技術(shù)。

? SpringAOP: 批量對Spring容器中bean的方法做增強(qiáng),并且這種增強(qiáng)不會與原來方法中的代碼耦合。

AOP:面向切面編程。簡單說,就是把一些業(yè)務(wù)邏輯中的相同的代碼抽取到一個獨立的模塊中,讓業(yè)務(wù)邏輯更加清爽。

1.2 快速入門

1.2.1 需求

? 要求讓_08_SpringAOP模塊中service包下所有類的所有方法在調(diào)用前都輸出:方法被調(diào)用了。

1.2.2 準(zhǔn)備工作

①添加依賴

需要添加SpringIOC相關(guān)依賴和AOP相關(guān)依賴。

        <!--SpringIOC相關(guān)依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--AOP相關(guān)依賴-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

②相關(guān)bean要注入容器中

開啟組件掃描

<context:component-scan base-package="com.sangeng"></context:component-scan>

加@Service注解

@Service
public class PhoneService {
    public void deleteAll(){
        System.out.println("PhoneService中deleteAll的核心代碼");
    }
}
@Service
public class UserService {
    public void deleteAll(){
        System.out.println("UserService中deleteAll的核心代碼");
    }
}

1.2.3 實現(xiàn)AOP

①開啟AOP注解支持

使用aop:aspectj-autoproxy標(biāo)簽

<?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:aop="http://www.springframework.org/schema/aop"
       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/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--開啟組件掃描-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--開啟aop注解支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

②創(chuàng)建切面類

創(chuàng)建一個類,在類上加上@Component和@Aspect

使用@Pointcut注解來指定要被增強(qiáng)的方法

使用@Before注解來給我們的增強(qiáng)代碼所在的方法進(jìn)行標(biāo)識,并且指定了增強(qiáng)代碼是在被增強(qiáng)方法執(zhí)行之前執(zhí)行的。

@Component
@Aspect
public class MyAspect {
//    用Pointcut注解中的屬性來指定對哪些方法進(jìn)行增強(qiáng)
    @Pointcut("execution(* com.sangeng.service.*.*(..))")
    public void pt(){}
    /*
        用@Before注解來指定該方法中是增強(qiáng)的代碼,并且是在被增強(qiáng)方法執(zhí)行前執(zhí)行的
        @Before的屬性寫上加了@Pointcut注解的方法: 方法名()
    */
    @Before("pt()")
    public void methodbefore(){
        System.out.println("方法被調(diào)用了");
    }
}

1.2.4 測試

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        PhoneService phoneService = applicationContext.getBean(PhoneService.class);
        UserService userService = applicationContext.getBean(UserService.class);
        phoneService.deleteAll();

    }

1.3 AOP核心概念

  • Joinpoint(連接點):所謂連接點是指那些可以被增強(qiáng)到的點。在spring中,這些點指的是方法,因為spring只支持方法類型的連接點
  • Pointcut(切入點):所謂切入點是指被增強(qiáng)的連接點(方法)
  • Advice(通知/ 增強(qiáng)):所謂通知是指具體增強(qiáng)的代碼
  • Target(目標(biāo)對象):被增強(qiáng)的對象就是目標(biāo)對象
  • Aspect(切面):是切入點和通知(引介)的結(jié)合
  • Proxy (代理):一個類被 AOP 增強(qiáng)后,就產(chǎn)生一個結(jié)果代理類

1.4 切點確定

1.4.1 切點表達(dá)式

? 可以使用切點表達(dá)式來表示要對哪些方法進(jìn)行增強(qiáng)。

寫法:execution([修飾符] 返回值類型 包名.類名.方法名(參數(shù)))

  • 訪問修飾符可以省略,大部分情況下省略
  • 返回值類型、包名、類名、方法名可以使用星號* 代表任意
  • 包名與類名之間一個點 . 代表當(dāng)前包下的類,兩個點 … 表示當(dāng)前包及其子包下的類
  • 參數(shù)列表可以使用兩個點 … 表示任意個數(shù),任意類型的參數(shù)列表

例如:

execution(* com.sangeng.service.*.*(..))   表示com.sangeng.service包下任意類,方法名任意,參數(shù)列表任意,返回值類型任意
execution(* com.sangeng.service..*.*(..))   表示com.sangeng.service包及其子包下任意類,方法名任意,參數(shù)列表任意,返回值類型任意
execution(* com.sangeng.service.*.*())     表示com.sangeng.service包下任意類,方法名任意,要求方法不能有參數(shù),返回值類型任意
execution(* com.sangeng.service.*.delete*(..))     表示com.sangeng.service包下任意類,要求方法不能有參數(shù),返回值類型任意,方法名要求已delete開頭

1.4.2 切點函數(shù)@annotation

? 我們也可以在要增強(qiáng)的方法上加上注解。然后使用@annotation來表示對加了什么注解的方法進(jìn)行增強(qiáng)。

寫法:@annotation(注解的全類名)

例如:

定義注解如下

@Target({ElementType.METHOD})//該注解可以加在方法上
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokeLog {
}

給需要增強(qiáng)的方法增加注解

@Service
public class PhoneService {
    @InvokeLog  
    public void deleteAll(){
        System.out.println("PhoneService中deleteAll的核心代碼");
    }
}

切面類中使用@annotation來確定要增強(qiáng)的方法

@Component
@Aspect
public class MyAspect {
//    用Pointcut注解中的屬性來指定對哪些方法進(jìn)行增強(qiáng)
    @Pointcut("@annotation(com.sangeng.aspect.InvokeLog)")
    public void pt(){}
    /*
        用@Before注解來指定該方法中是增強(qiáng)的代碼,并且是在被增強(qiáng)方法執(zhí)行前執(zhí)行的
        @Before的屬性寫上加了@Pointcut注解的方法: 方法名()
    */
    @Before("pt()")
    public void methodbefore(){
        System.out.println("方法被調(diào)用了");
    }
}

1.5 通知分類

  • @Before:前置通知,在目標(biāo)方法執(zhí)行前執(zhí)行
  • @AfterReturning: 返回后通知,在目標(biāo)方法執(zhí)行后執(zhí)行,如果出現(xiàn)異常不會執(zhí)行
  • @After:后置通知,在目標(biāo)方法之后執(zhí)行,無論是否出現(xiàn)異常都會執(zhí)行
  • @AfterThrowing:異常通知,在目標(biāo)方法拋出異常后執(zhí)行
  • @Around:環(huán)繞通知,圍繞著目標(biāo)方法執(zhí)行

理解不同通知執(zhí)行時機(jī)。(下面的偽代碼是用來理解單個通知的執(zhí)行時機(jī)的,不能用來理解多個通知情況下的執(zhí)行順序。如果需要配置多個通知我們會選擇使用Around通知,更加的清晰并且好用

    public Object test() {
        before();//@Before 前置通知
        try {
            Object ret = 目標(biāo)方法();//目標(biāo)方法調(diào)用
            afterReturing();//@AfterReturning 返回后通知
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            afterThrowing();//@AfterThrowing 異常通知通知
        }finally {
            after();//@After 后置通知
        }
        return ret;
    }

環(huán)繞通知非常特殊,它可以對目標(biāo)方法進(jìn)行全方位的增強(qiáng)。

例如:

  	@Around("pt()")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("目標(biāo)方法前");
        try {
            pjp.proceed();//目標(biāo)方法執(zhí)行
            System.out.println("目標(biāo)方法后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("目標(biāo)方法出現(xiàn)異常");
        }finally {
            System.out.println("finally中進(jìn)行增強(qiáng)");
        }
    }

1.6 獲取被增強(qiáng)方法相關(guān)信息

? 我們實際對方法進(jìn)行增強(qiáng)時往往還需要獲取到被增強(qiáng)代碼的相關(guān)信息,比如方法名,參數(shù),返回值,異常對象等。

? 我們可以在除了環(huán)繞通知外的所有通知方法中增加一個JoinPoint類型的參數(shù)。這個參數(shù)封裝了被增強(qiáng)方法的相關(guān)信息。我們可以通過這個參數(shù)獲取到除了異常對象和返回值之外的所有信息。

例如:

    @Before("pt()")
    public void methodbefore(JoinPoint jp){
        Object[] args = jp.getArgs();//方法調(diào)用時傳入的參數(shù)
        Object target = jp.getTarget();//被代理對象
        MethodSignature signature = (MethodSignature) jp.getSignature();//獲取被被增強(qiáng)方法簽名封裝的對象
        System.out.println("Before方法被調(diào)用了");
    }

案例:

需求:要求讓所有service包下類的所有方法被調(diào)用前都輸出全類名,方法名,以及調(diào)用時傳入的參數(shù)

@Component
@Aspect
public class PrintLogAspect {
    //對哪些方法增強(qiáng)
    @Pointcut("execution(* com.sangeng.service..*.*(..))")
    public void pt(){}
    //怎么增強(qiáng)
    @Before("pt()")
    public void printLog(JoinPoint joinPoint){
        //輸出 被增強(qiáng)的方法所在的類名 方法名 調(diào)用時傳入的參數(shù)   joinPoint.getSignature().getName()  joinPoint.getArgs()
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //類名
        String className = signature.getDeclaringTypeName();
        //方法名
        String methodName = signature.getName();
        //調(diào)用時傳入的參數(shù)
        Object[] args = joinPoint.getArgs();
        System.out.println(className+"=="+methodName+"======"+ Arrays.toString(args));
    }
}

? 如果需要獲取被增強(qiáng)方法中的異常對象或者返回值則需要在方法參數(shù)上增加一個對應(yīng)類型的參數(shù),并且使用注解的屬性進(jìn)行配置。這樣Spring會把你想獲取的數(shù)據(jù)賦值給對應(yīng)的方法參數(shù)。

例如:

    @AfterReturning(value = "pt()",returning = "ret")//使用returning屬性指定了把目標(biāo)方法返回值賦值給下面方法的參數(shù)ret
    public void AfterReturning(JoinPoint jp,Object ret){
        System.out.println("AfterReturning方法被調(diào)用了");
    }
    @AfterThrowing(value = "pt()",throwing = "t")//使用throwing屬性指定了把出現(xiàn)的異常對象賦值給下面方法的參數(shù)t
    public void AfterThrowing(JoinPoint jp,Throwable t){
        System.out.println("AfterReturning方法被調(diào)用了");
    }

? 相信你肯定覺得上面的獲取方式特別的麻煩難以理解。就可以使用下面這種萬能的方法。

? 直接在環(huán)繞通知方法中增加一個ProceedingJoinPoint類型的參數(shù)。這個參數(shù)封裝了被增強(qiáng)方法的相關(guān)信息。

該參數(shù)的proceed()方法被調(diào)用相當(dāng)于被增強(qiáng)方法被執(zhí)行,調(diào)用后的返回值就相當(dāng)于被增強(qiáng)方法的返回值。

例如:

    @Around(value = "pt()")
    public Object around(ProceedingJoinPoint pjp) {
        Object[] args = pjp.getArgs();//方法調(diào)用時傳入的參數(shù)
        Object target = pjp.getTarget();//被代理對象
        MethodSignature signature = (MethodSignature) pjp.getSignature();//獲取被被增強(qiáng)方法簽名封裝的對象
        Object ret = null;
        try {
            ret = pjp.proceed();//ret就是目標(biāo)方法執(zhí)行后的返回值
        } catch (Throwable throwable) {
            throwable.printStackTrace();//throwable就是出現(xiàn)異常時的異常對象
        }
        return ret;
    }

1.7 AOP應(yīng)用案例

1.7.1 需求

現(xiàn)有AI核心功能代碼如下:

public class AIController {
    //AI自動回答
    public String getAnswer(String question){
        //AI核心代碼 價值10個億
        String str = question.replace("嗎", "");
        str = str.replace("?","!");
        return str;
    }
    //AI算
    public String fortuneTelling(String name){
        //AI算核心代碼
        String[] strs = {"女犯傷官把夫克,旱地蓮花栽不活,不是吃上兩家飯,也要刷上三家鍋。","一朵鮮花頭上戴,一年四季也不開,一心想要花開時,采花之人沒到來。","此命生來脾氣暴,上來一陣雙腳跳,對你脾氣啥都好,經(jīng)常與人吵和鬧。"};
        int index = name.hashCode() % 3;
        return strs[index];
    }
}

? 現(xiàn)在為了保證數(shù)據(jù)的安全性,要求調(diào)用方法時fortuneTelling傳入的姓名是經(jīng)過加密的。我們需要對傳入的參數(shù)進(jìn)行解密后才能使用。并且要對該方法的返回值進(jìn)行加密后返回。

? PS:后期也可能讓其他方法進(jìn)行相應(yīng)的加密處理。

字符串加密解密直接使用下面的工具類即可:

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
public class CryptUtil {
    private static final String AES = "AES";
    private static int keysizeAES = 128;
    private static String charset = "utf-8";
    public static String parseByte2HexStr(final byte buf[]) {
        final StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
    public static byte[] parseHexStr2Byte(final String hexStr) {
        if (hexStr.length() < 1)
            return null;
        final byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0;i< hexStr.length()/2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
    private static String keyGeneratorES(final String res, final String algorithm, final String key, final Integer keysize, final Boolean bEncode) {
        try {
            final KeyGenerator g = KeyGenerator.getInstance(algorithm);
            if (keysize == 0) {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                g.init(new SecureRandom(keyBytes));
            } else if (key == null) {
                g.init(keysize);
            } else {
                byte[] keyBytes = charset == null ? key.getBytes() : key.getBytes(charset);
                SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
                random.setSeed(keyBytes);
                g.init(keysize, random);
            }
            final SecretKey sk = g.generateKey();
            final SecretKeySpec sks = new SecretKeySpec(sk.getEncoded(), algorithm);
            final Cipher cipher = Cipher.getInstance(algorithm);
            if (bEncode) {
                cipher.init(Cipher.ENCRYPT_MODE, sks);
                final byte[] resBytes = charset == null? res.getBytes() : res.getBytes(charset);
                return parseByte2HexStr(cipher.doFinal(resBytes));
            } else {
                cipher.init(Cipher.DECRYPT_MODE, sks);
                return new String(cipher.doFinal(parseHexStr2Byte(res)));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String AESencode(final String res) {
        return keyGeneratorES(res, AES, "aA11*-%", keysizeAES, true);
    }
    public static String AESdecode(final String res) {
        return keyGeneratorES(res, AES, "aA11*-%", keysizeAES, false);
    }
    public static void main(String[] args) {
        System.out.println(
                "加密后:" + AESencode("將要加密的明文")
        );
        System.out.println(
                "解密后:" + AESdecode("730CAE52D85B372FB161B39D0A908B8CC6EF6DA2F7D4E595D35402134C3E18AB")
        );
    }
}

1.7.2 實現(xiàn)

①導(dǎo)入依賴

        <!--SpringIOC相關(guān)依賴-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--AOP相關(guān)依賴-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

②開啟AOP注解支持

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置組件掃描-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--啟動AOP注解支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

③自定義注解

package com.sangeng.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Crypt {
}

④在目標(biāo)方法上增加注解

注意:目標(biāo)對象一定要記得注入Spring容器中

@Controller
public class AIController {
	//....
    //AI算命
    @Crypt
    public String fortuneTelling(String name){
        System.out.println(name);
              //AI算命核心代碼
        String[] strs = {"女犯傷官把夫克,旱地蓮花栽不活,不是吃上兩家飯,也要刷上三家鍋。","一朵鮮花頭上戴,一年四季也不開,一心想要花開時,采花之人沒到來。","此命生來脾氣暴,上來一陣雙腳跳,對你脾氣啥都好,經(jīng)常與人吵和鬧。"};
        int index = name.hashCode() % 3;
        return strs[index];
    }
}

⑤定義切面類

package com.sangeng.aspect;
import com.sangeng.util.CryptUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class CryptAspect {
    //確定切點
    @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
    public void pt(){
    }
    //定義通知
    @Around("pt()")
    public Object crypt(ProceedingJoinPoint pjp) {
        //獲取去目標(biāo)方法調(diào)用時的參數(shù)
        Object[] args = pjp.getArgs();
        //對參數(shù)進(jìn)行解密  解密后傳入目標(biāo)方法執(zhí)行
        String arg = (String) args[0];
        String s = CryptUtil.AESdecode(arg);//解密
        args[0] = s;
        Object proceed = null;
        String ret = null;
        try {
            proceed = pjp.proceed(args);//目標(biāo)方法調(diào)用
            //目標(biāo)方法執(zhí)行后需要獲取到返回值
            ret = (String) proceed;
            //對返回值加密后進(jìn)行真正的返回
            ret = CryptUtil.AESencode(ret);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return ret;
    }
}

1.8 xml配置AOP

①定義切面類

public class MyAspect {
    public void before(JoinPoint joinPoint){
        System.out.println("before");
    }
//    @AfterReturning(value = "pt()",returning = "ret")
    public void afterReturning(JoinPoint joinPoint,Object ret){
        System.out.println("afterReturning:"+ret);
    }
//    @After("pt()")
    public void after(JoinPoint joinPoint){
        System.out.println("after");
    }
//    @AfterThrowing(value = "pt()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Throwable e){
        String message = e.getMessage();
        System.out.println("afterThrowing:"+message);
    }
    public Object around(ProceedingJoinPoint pjp){
        //獲取參數(shù)
        Object[] args = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Object target = pjp.getTarget();
        Object ret = null;
        try {
            ret = pjp.proceed();//目標(biāo)方法的執(zhí)行
            //ret就是被增強(qiáng)方法的返回值
            System.out.println(ret);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println(throwable.getMessage());
        }
//        System.out.println(pjp);
        return ret;
    }
}

②目標(biāo)類和切面類注入容器

在切面類和目標(biāo)類上加是對應(yīng)的注解。注入如果是使用注解的方式注入容器要記得開啟組件掃描。

當(dāng)然你也可以在xml中使用bean標(biāo)簽的方式注入容器。

@Component//把切面類注入容器
public class MyAspect {
	//..。省略無關(guān)代碼
}
@Service//把目標(biāo)類注入容器
public class UserService {
	//..。省略無關(guān)代碼
}

③配置AOP

<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--開啟組件掃描-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--配置AOP-->
    <aop:config>
        <!--定義切點-->
        <aop:pointcut id="pt1" expression="execution(* com.sangeng.service..*.*(..))"></aop:pointcut>
        <aop:pointcut id="pt2" expression="@annotation(com.sangeng.aspect.InvokeLog)"></aop:pointcut>
        <!--配置切面-->
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut-ref="pt1"></aop:before>
            <aop:after method="after" pointcut-ref="pt1"></aop:after>
            <aop:after-returning method="afterReturning" pointcut-ref="pt1" returning="ret"></aop:after-returning>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pt2" throwing="e"></aop:after-throwing>
        </aop:aspect>
    </aop:config>
</beans>

1.9 多切面順序問題

? 在實際項目中我們可能會存在配置了多個切面的情況。這種情況下我們很可能需要控制切面的順序。

? 我們在默認(rèn)情況下Spring有它自己的排序規(guī)則。(按照類名排序)

? 默認(rèn)排序規(guī)則往往不符合我們的要求,我們需要進(jìn)行特殊控制。

? 如果是注解方式配置的AOP可以在切面類上加**@Order注解來控制順序。@Order中的屬性越小優(yōu)先級越高。**

? 如果是XML方式配置的AOP,可以通過調(diào)整配置順序來控制。

例如:

下面這種配置方式就會先使用CryptAspect里面的增強(qiáng),在使用APrintLogAspect里的增強(qiáng)

@Component
@Aspect
@Order(2)
public class APrintLogAspect {
    //省略無關(guān)代碼
}
@Component
@Aspect
@Order(1)
public class CryptAspect {
    //省略無關(guān)代碼
}

1.10 AOP原理-動態(tài)代理

? 實際上Spring的AOP其實底層就是使用動態(tài)代理來完成的。并且使用了兩種動態(tài)代理分別是JDK的動態(tài)代理和Cglib動態(tài)代理。

? 所以我們接下去來學(xué)習(xí)下這兩種動態(tài)代理,理解下它們的不同點。

1.10.1 JDK動態(tài)代理

? JDK的動態(tài)代理使用的java.lang.reflect.Proxy這個類來進(jìn)行實現(xiàn)的。要求被代理(被增強(qiáng))的類需要實現(xiàn)了接口。并且JDK動態(tài)代理也只能對接口中的方法進(jìn)行增強(qiáng)。

public static void main(String[] args) {
        AIControllerImpl aiController = new AIControllerImpl();
        //使用動態(tài)代理增強(qiáng)getAnswer方法
        //1.JDK動態(tài)代理
        //獲取類加載器
        ClassLoader cl = Demo.class.getClassLoader();
        //被代理類所實現(xiàn)接口的字節(jié)碼對象數(shù)組
        Class<?>[] interfaces = AIControllerImpl.class.getInterfaces();
        AIController proxy = (AIController) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
            //使用代理對象的方法時 會調(diào)用到invoke
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //proxy   是代理對象
                //method 是當(dāng)前被調(diào)用的方法封裝的Method對象
                //args   是調(diào)用方法時傳入的參數(shù)
                //調(diào)用被代理對象的對應(yīng)方法
                //判斷 當(dāng)前調(diào)用的是否是getAnswer方法
                if(method.getName().equals("getAnswer")){
                    System.out.println("增強(qiáng)");
                }
                Object ret = method.invoke(aiController, args);
                return ret;
            }
        });
        String answer = proxy.getAnswer("三連了嗎?");
		System.out.println(answer);
    }

1.10.2 Cglib動態(tài)代理

? 使用的是org.springframework.cglib.proxy.Enhancer類進(jìn)行實現(xiàn)的。

public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //設(shè)置父類的字節(jié)碼對象
        enhancer.setSuperclass(AIControllerImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            //使用代理對象執(zhí)行方法是都會調(diào)用到intercept方法
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //判斷當(dāng)前調(diào)用的方法是不是getAnswer方法 如果是進(jìn)行增強(qiáng)
                if ("getAnswer".equals(method.getName())){
                    System.out.println("被增強(qiáng)了");
                }
                //調(diào)用父類中對應(yīng)的方法
                Object ret = methodProxy.invokeSuper(o, objects);
                return ret;
            }
        });
        //生成代理對象
        AIControllerImpl proxy = (AIControllerImpl) enhancer.create();
//        System.out.println(proxy.getAnswer("你好嗎?"));
        System.out.println(proxy.fortuneTelling("你好嗎?"));
    }
}

1.10.3 總結(jié)

? JDK動態(tài)代理要求被代理(被增強(qiáng))的類必須要實現(xiàn)接口,生成的代理對象相當(dāng)于是被代理對象的兄弟。

? Cglib的動態(tài)代理不要求被代理(被增強(qiáng))的類要實現(xiàn)接口,生成的代理對象相當(dāng)于被代理對象的子類對象。

? Spring的AOP默認(rèn)情況下優(yōu)先使用的是JDK的動態(tài)代理,如果使用不了JDK的動態(tài)代理才會使用Cglib的動態(tài)代理。

1.11 切換默認(rèn)動態(tài)代理方式

? 有的時候我們需要修改AOP的代理方式。

? 我們可以使用以下方式修改:

如果我們是采用注解方式配置AOP的話:

設(shè)置aop:aspectj-autoproxy標(biāo)簽的proxy-target-class屬性為true,代理方式就會修改成Cglib

<aop:aspectj-autoproxy proxy-target-class="true"/>

動態(tài)代理和靜態(tài)代理詳解可以看我這一篇文章: SpringAop實現(xiàn)原理及代理模式詳解

如果我們是采用xml方式配置AOP的話:

設(shè)置aop:config標(biāo)簽的proxy-target-class屬性為true,代理方式就會修改成Cglib

<aop:config proxy-target-class="true">
</aop:config>

到此這篇關(guān)于Spring AOP注解配置與XML配置雙實戰(zhàn)教程的文章就介紹到這了,更多相關(guān)spring aop注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java基礎(chǔ)第四篇 封裝與接口

    Java基礎(chǔ)第四篇 封裝與接口

    本文將對介紹Java 的封裝與接口進(jìn)行介紹,將要封裝(encapsulation)對象的成員(成員包括數(shù)據(jù)成員和方法),從而只允許從外部調(diào)用部分的成員。利用封裝,我們可以提高對象的易用性和安全性。想具體了解的小伙伴請參考下面文章的內(nèi)容
    2021-09-09
  • Java響應(yīng)式編程Mono接口詳解

    Java響應(yīng)式編程Mono接口詳解

    這篇文章主要介紹了Java中的Mono接口,Mono是Project?Reactor響應(yīng)式編程庫中的核心接口之一,代表一個異步的、可能包含零個或一個元素的流,在響應(yīng)式編程范式中,Mono專門用于處理那些預(yù)期最多只有一個結(jié)果的異步操作,需要的朋友可以參考下
    2026-02-02
  • 詳解Java基礎(chǔ)篇--面向?qū)ο?(構(gòu)造方法,static、this關(guān)鍵字)

    詳解Java基礎(chǔ)篇--面向?qū)ο?(構(gòu)造方法,static、this關(guān)鍵字)

    這篇文章主要介紹了Java基礎(chǔ)篇--面向?qū)ο?(構(gòu)造方法,static、this關(guān)鍵字),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 解決Mybatis中mapper.xml文件update,delete及insert返回值問題

    解決Mybatis中mapper.xml文件update,delete及insert返回值問題

    這篇文章主要介紹了解決Mybatis中mapper.xml文件update,delete及insert返回值問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • spring boot切面execution表達(dá)式添加多個包路徑問題及解決方案

    spring boot切面execution表達(dá)式添加多個包路徑問題及解決方案

    在Spring Boot中,如果你想為多個包中的方法創(chuàng)建一個切面,你可以在@Pointcut注解中使用||操作符來指定多個包,下面給大家分享spring boot切面execution表達(dá)式添加多個包路徑問題及解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • SpringBoot?main方法結(jié)束程序不停止的原因分析及解決方法

    SpringBoot?main方法結(jié)束程序不停止的原因分析及解決方法

    Spring?Boot啟動內(nèi)嵌Tomcat時,main方法啟動非daemon線程執(zhí)行await()死循環(huán),使JVM保持運(yùn)行,通過發(fā)送關(guān)機(jī)指令可終止程序,下面通過本文給大家介紹SpringBoot?main方法結(jié)束程序不停止的原因分析及解決方法,感興趣的朋友一起看看吧
    2025-07-07
  • SpringBoot熱部署啟動關(guān)閉流程詳解

    SpringBoot熱部署啟動關(guān)閉流程詳解

    Spring?Boot啟動熱部署是一種技術(shù),它能讓開發(fā)者在不重啟應(yīng)用程序的情況下實時更新代碼。這樣可以提高開發(fā)效率,避免頻繁重啟應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-04-04
  • Hibernate中的多表查詢及抓取策略

    Hibernate中的多表查詢及抓取策略

    本文主要介紹了Hibernate中的多表查詢及抓取策略,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • Spring?Boot?實現(xiàn)字段唯一校驗功能(實例代碼)

    Spring?Boot?實現(xiàn)字段唯一校驗功能(實例代碼)

    這篇文章主要介紹了Spring?Boot?實現(xiàn)字段唯一校驗,實現(xiàn)代碼很簡單,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • CommandLineRunner最佳實踐小結(jié)

    CommandLineRunner最佳實踐小結(jié)

    CommandLineRunner是Spring Boot框架提供的一個功能接口,用于Spring Boot應(yīng)用啟動完成后立即執(zhí)行特定的代碼邏輯,允許開發(fā)者在應(yīng)用程序完全啟動并準(zhǔn)備好接收請求之前執(zhí)行一些初始化任務(wù),下面通過實例代碼講解什么是CommandLineRunner以及CommandLineRunner用法,一起看看吧
    2025-08-08

最新評論

长阳| 扶风县| 江孜县| 富宁县| 富民县| 饶河县| 五河县| 衡阳县| 武鸣县| 宁南县| 通山县| 三原县| 云阳县| 泰宁县| 诸暨市| 阿克| 丹东市| 新民市| 天峻县| 台前县| 广灵县| 双鸭山市| 肇州县| 梁河县| 庆安县| 工布江达县| 松潘县| 桦南县| 河源市| 沧源| 青神县| 平陆县| 沈丘县| 鸡泽县| 泉州市| 双流县| 高碑店市| 胶州市| 盐边县| 定结县| 彭水|