欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

1. AOP

1.1 概念

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

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

AOP:面向切面編程。簡(jiǎn)單說(shuō),就是把一些業(yè)務(wù)邏輯中的相同的代碼抽取到一個(gè)獨(dú)立的模塊中,讓業(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要注入容器中

開(kāi)啟組件掃描

<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 實(shí)現(xiàn)AOP

①開(kāi)啟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">
    <!--開(kāi)啟組件掃描-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--開(kāi)啟aop注解支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

②創(chuàng)建切面類

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

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

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

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

1.2.4 測(cè)試

    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(連接點(diǎn)):所謂連接點(diǎn)是指那些可以被增強(qiáng)到的點(diǎn)。在spring中,這些點(diǎn)指的是方法,因?yàn)閟pring只支持方法類型的連接點(diǎn)
  • Pointcut(切入點(diǎn)):所謂切入點(diǎn)是指被增強(qiáng)的連接點(diǎn)(方法)
  • Advice(通知/ 增強(qiáng)):所謂通知是指具體增強(qiáng)的代碼
  • Target(目標(biāo)對(duì)象):被增強(qiáng)的對(duì)象就是目標(biāo)對(duì)象
  • Aspect(切面):是切入點(diǎn)和通知(引介)的結(jié)合
  • Proxy (代理):一個(gè)類被 AOP 增強(qiáng)后,就產(chǎn)生一個(gè)結(jié)果代理類

1.4 切點(diǎn)確定

1.4.1 切點(diǎn)表達(dá)式

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

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

  • 訪問(wèn)修飾符可以省略,大部分情況下省略
  • 返回值類型、包名、類名、方法名可以使用星號(hào)* 代表任意
  • 包名與類名之間一個(gè)點(diǎn) . 代表當(dāng)前包下的類,兩個(gè)點(diǎn) … 表示當(dāng)前包及其子包下的類
  • 參數(shù)列表可以使用兩個(gè)點(diǎn) … 表示任意個(gè)數(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開(kāi)頭

1.4.2 切點(diǎn)函數(shù)@annotation

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

寫(xiě)法:@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來(lái)確定要增強(qiáng)的方法

@Component
@Aspect
public class MyAspect {
//    用Pointcut注解中的屬性來(lái)指定對(duì)哪些方法進(jìn)行增強(qiáng)
    @Pointcut("@annotation(com.sangeng.aspect.InvokeLog)")
    public void pt(){}
    /*
        用@Before注解來(lái)指定該方法中是增強(qiáng)的代碼,并且是在被增強(qiáng)方法執(zhí)行前執(zhí)行的
        @Before的屬性寫(xiě)上加了@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)異常不會(huì)執(zhí)行
  • @After:后置通知,在目標(biāo)方法之后執(zhí)行,無(wú)論是否出現(xiàn)異常都會(huì)執(zhí)行
  • @AfterThrowing:異常通知,在目標(biāo)方法拋出異常后執(zhí)行
  • @Around:環(huán)繞通知,圍繞著目標(biāo)方法執(zhí)行

理解不同通知執(zhí)行時(shí)機(jī)。(下面的偽代碼是用來(lái)理解單個(gè)通知的執(zhí)行時(shí)機(jī)的,不能用來(lái)理解多個(gè)通知情況下的執(zhí)行順序。如果需要配置多個(gè)通知我們會(huì)選擇使用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)繞通知非常特殊,它可以對(duì)目標(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)信息

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

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

例如:

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

案例:

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

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

? 如果需要獲取被增強(qiáng)方法中的異常對(duì)象或者返回值則需要在方法參數(shù)上增加一個(gè)對(duì)應(yīng)類型的參數(shù),并且使用注解的屬性進(jìn)行配置。這樣Spring會(huì)把你想獲取的數(shù)據(jù)賦值給對(duì)應(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)的異常對(duì)象賦值給下面方法的參數(shù)t
    public void AfterThrowing(JoinPoint jp,Throwable t){
        System.out.println("AfterReturning方法被調(diào)用了");
    }

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

? 直接在環(huán)繞通知方法中增加一個(gè)ProceedingJoinPoint類型的參數(shù)。這個(gè)參數(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í)傳入的參數(shù)
        Object target = pjp.getTarget();//被代理對(duì)象
        MethodSignature signature = (MethodSignature) pjp.getSignature();//獲取被被增強(qiáng)方法簽名封裝的對(duì)象
        Object ret = null;
        try {
            ret = pjp.proceed();//ret就是目標(biāo)方法執(zhí)行后的返回值
        } catch (Throwable throwable) {
            throwable.printStackTrace();//throwable就是出現(xiàn)異常時(shí)的異常對(duì)象
        }
        return ret;
    }

1.7 AOP應(yīng)用案例

1.7.1 需求

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

public class AIController {
    //AI自動(dòng)回答
    public String getAnswer(String question){
        //AI核心代碼 價(jià)值10個(gè)億
        String str = question.replace("嗎", "");
        str = str.replace("?","!");
        return str;
    }
    //AI算
    public String fortuneTelling(String name){
        //AI算核心代碼
        String[] strs = {"女犯?jìng)侔逊蚩耍档厣徎ㄔ圆换?,不是吃上兩家飯,也要刷上三家鍋?,"一朵鮮花頭上戴,一年四季也不開(kāi),一心想要花開(kāi)時(shí),采花之人沒(méi)到來(lái)。","此命生來(lái)脾氣暴,上來(lái)一陣雙腳跳,對(duì)你脾氣啥都好,經(jīng)常與人吵和鬧。"};
        int index = name.hashCode() % 3;
        return strs[index];
    }
}

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

②開(kāi)啟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>
    <!--啟動(dòng)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)對(duì)象一定要記得注入Spring容器中

@Controller
public class AIController {
	//....
    //AI算命
    @Crypt
    public String fortuneTelling(String name){
        System.out.println(name);
              //AI算命核心代碼
        String[] strs = {"女犯?jìng)侔逊蚩?,旱地蓮花栽不活,不是吃上兩家飯,也要刷上三家鍋?,"一朵鮮花頭上戴,一年四季也不開(kāi),一心想要花開(kāi)時(shí),采花之人沒(méi)到來(lái)。","此命生來(lái)脾氣暴,上來(lái)一陣雙腳跳,對(duì)你脾氣啥都好,經(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 {
    //確定切點(diǎn)
    @Pointcut("@annotation(com.sangeng.aspect.Crypt)")
    public void pt(){
    }
    //定義通知
    @Around("pt()")
    public Object crypt(ProceedingJoinPoint pjp) {
        //獲取去目標(biāo)方法調(diào)用時(shí)的參數(shù)
        Object[] args = pjp.getArgs();
        //對(duì)參數(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;
            //對(duì)返回值加密后進(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)類上加是對(duì)應(yīng)的注解。注入如果是使用注解的方式注入容器要記得開(kāi)啟組件掃描。

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

@Component//把切面類注入容器
public class MyAspect {
	//..。省略無(wú)關(guān)代碼
}
@Service//把目標(biāo)類注入容器
public class UserService {
	//..。省略無(wú)關(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">
    <!--開(kāi)啟組件掃描-->
    <context:component-scan base-package="com.sangeng"></context:component-scan>
    <!--配置AOP-->
    <aop:config>
        <!--定義切點(diǎn)-->
        <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 多切面順序問(wèn)題

? 在實(shí)際項(xiàng)目中我們可能會(huì)存在配置了多個(gè)切面的情況。這種情況下我們很可能需要控制切面的順序。

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

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

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

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

例如:

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

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

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

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

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

1.10.1 JDK動(dòng)態(tài)代理

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

public static void main(String[] args) {
        AIControllerImpl aiController = new AIControllerImpl();
        //使用動(dòng)態(tài)代理增強(qiáng)getAnswer方法
        //1.JDK動(dòng)態(tài)代理
        //獲取類加載器
        ClassLoader cl = Demo.class.getClassLoader();
        //被代理類所實(shí)現(xiàn)接口的字節(jié)碼對(duì)象數(shù)組
        Class<?>[] interfaces = AIControllerImpl.class.getInterfaces();
        AIController proxy = (AIController) Proxy.newProxyInstance(cl, interfaces, new InvocationHandler() {
            //使用代理對(duì)象的方法時(shí) 會(huì)調(diào)用到invoke
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //proxy   是代理對(duì)象
                //method 是當(dāng)前被調(diào)用的方法封裝的Method對(duì)象
                //args   是調(diào)用方法時(shí)傳入的參數(shù)
                //調(diào)用被代理對(duì)象的對(duì)應(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動(dòng)態(tài)代理

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

public class CglibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //設(shè)置父類的字節(jié)碼對(duì)象
        enhancer.setSuperclass(AIControllerImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            //使用代理對(duì)象執(zhí)行方法是都會(huì)調(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)用父類中對(duì)應(yīng)的方法
                Object ret = methodProxy.invokeSuper(o, objects);
                return ret;
            }
        });
        //生成代理對(duì)象
        AIControllerImpl proxy = (AIControllerImpl) enhancer.create();
//        System.out.println(proxy.getAnswer("你好嗎?"));
        System.out.println(proxy.fortuneTelling("你好嗎?"));
    }
}

1.10.3 總結(jié)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

相關(guān)文章

  • 基于springboot攔截器HandlerInterceptor的注入問(wèn)題

    基于springboot攔截器HandlerInterceptor的注入問(wèn)題

    這篇文章主要介紹了springboot攔截器HandlerInterceptor的注入問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • IDEA maven項(xiàng)目中刷新依賴的兩種方法小結(jié)

    IDEA maven項(xiàng)目中刷新依賴的兩種方法小結(jié)

    這篇文章主要介紹了IDEA maven項(xiàng)目中刷新依賴的兩種方法小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • httpclient重定向之后獲取網(wǎng)址信息示例

    httpclient重定向之后獲取網(wǎng)址信息示例

    使用HttpClient進(jìn)行127.0.0.1:8080地址的問(wèn)題,然后該地址自動(dòng)重定向127.0.0.1:8080/mobserver,如何獲取該地址呢?使用HttpContext,下面是示例
    2014-02-02
  • java判斷http地址是否連通(示例代碼)

    java判斷http地址是否連通(示例代碼)

    這篇文章通過(guò)實(shí)例代碼給大家介紹了java判斷http地址是否連通,文末給大家補(bǔ)充知識(shí)點(diǎn)url.openconnection() 設(shè)置超時(shí)時(shí)間的方法,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • SpringBoot實(shí)現(xiàn)單點(diǎn)登錄(SSO)的四種方案

    SpringBoot實(shí)現(xiàn)單點(diǎn)登錄(SSO)的四種方案

    單點(diǎn)登錄(Single?Sign-On,SSO)是企業(yè)應(yīng)用系統(tǒng)中常見(jiàn)的用戶認(rèn)證方案,它允許用戶使用一組憑證訪問(wèn)多個(gè)相關(guān)但獨(dú)立的系統(tǒng),無(wú)需重復(fù)登錄,本文給大家介紹了SpringBoot實(shí)現(xiàn)單點(diǎn)登錄(SSO)的四種方案,需要的朋友可以參考下
    2025-04-04
  • struts1登錄示例代碼_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    struts1登錄示例代碼_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了struts1登錄示例代碼,需要的朋友可以參考下
    2017-08-08
  • Java自定義equals產(chǎn)生的問(wèn)題分析

    Java自定義equals產(chǎn)生的問(wèn)題分析

    這篇文章主要介紹了Java自定義equals時(shí)super.equals帶來(lái)的問(wèn)題分析,總的來(lái)說(shuō)這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過(guò)程。希望通過(guò)這道題能給你帶來(lái)一種解題優(yōu)化的思路
    2023-01-01
  • springboot實(shí)現(xiàn)啟動(dòng)直接訪問(wèn)項(xiàng)目地址

    springboot實(shí)現(xiàn)啟動(dòng)直接訪問(wèn)項(xiàng)目地址

    這篇文章主要介紹了springboot實(shí)現(xiàn)啟動(dòng)直接訪問(wèn)項(xiàng)目地址,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • SpringBoot引入swagger報(bào)錯(cuò)處理的解決方法

    SpringBoot引入swagger報(bào)錯(cuò)處理的解決方法

    這篇文章主要給大家介紹SpringBoot引入swagger是會(huì)出現(xiàn)報(bào)錯(cuò)的處理解決方法,文中有詳細(xì)的解決過(guò)程,感興趣的小伙伴可以跟著小編一起來(lái)學(xué)習(xí)吧
    2023-06-06
  • mybatis 忽略實(shí)體對(duì)象的某個(gè)屬性(2種方式)

    mybatis 忽略實(shí)體對(duì)象的某個(gè)屬性(2種方式)

    這篇文章主要介紹了mybatis 忽略實(shí)體對(duì)象的某個(gè)屬性方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06

最新評(píng)論