Spring配置切面的幾種方式


1、實現MethodBeforeAdvice等接口

pom.xml添加spring核心依賴:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>

在Spring中,org.springframework.aop包下有四個接口,分別是MethodBeforeAdvice(前置通知)、AfterReturningAdvice(后置通知)、MethodInterceptor(環繞通知)、ThrowsAdvice(異常通知),其中,前三個接口都有對應的實現方法,分別實現后就可以在對應的通知方法中添加功能,但是ThrowsAdvice異常通知沒有實現方法,所以需要自定義一個方法,不過對方法名有規定,必須寫成afterThrowing,代碼如下:

定義一個切面類UserServiceAdvice:

public class UserServiceAspect implements MethodBeforeAdvice, AfterReturningAdvice,
        MethodInterceptor, ThrowsAdvice {

    /**
     * 后置通知
     * @param o
     * @param method
     * @param objects
     * @param o1
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知");
    }

    /**
     * 前置通知
     * @param method
     * @param objects
     * @param o
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知。。。");
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("環繞通知前。。。");
        Object returnVal = methodInvocation.proceed();
        System.out.println("環繞通知后。。。");
        return returnVal;
    }

    /**
     * 異常通知,參照MethodBeforeAdvice,
     * 該方法的方法名必須叫做afterThrowing,
     * method,args,target這三個參數可以省略,要么全部聲明
     * Exception必須保留
     * @param e
     */
    public void afterThrowing(Exception e){
        System.out.println("產生了異常:"+e.getMessage());
    }
}

在resources目錄下創建一個Spring配置文件applicationContext.xml:

在這個配置文件中主要是做對bean的配置和對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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目標對象 -->
    <bean id="userService" class="edu.nf.ch12.service.impl.UserServiceImpl" />
    <!-- 切面 -->
    <bean id="userServiceAspect" class="edu.nf.ch12.service.aspect.UserServiceAspect" />
    <!-- aop配置, -->
    <aop:config proxy-target-class="false">
        <!-- 配置切入點,使用aspectJ的切入點表達式,
         表達式語法:
         1、execution(訪問修飾符 方法返回值 包名.類名.方法名(參數類型))
            execution是切入到方法級別的
         2、within(訪問修飾符 方法返回值 包名.類名)
            within是切入到類級別的
         說明:訪問修飾符可以省略,
         方法返回值、包名、類名、方法名、可以使用*號進行統配,
         方法參數可以指定參數的類型,也可以使用".."來標識任意類型和個數的參數
         例如:execution(* edu.nf.ch12.service.impl.*.*(..))
         表示edu.nf.ch12.service.impl包下的所有類,以及任意返回值類和任意參數類型和個數的方法都會匹配-->
        <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch12.service.impl.UserServiceImpl.say(..))"/>
        <!-- 配置通知器(Advisor),其實就是切面
         advice-ref引用上面定義的切面的id
         pointcut-ref引用上面定義的切入點的id-->
        <aop:advisor advice-ref="userServiceAspect" pointcut-ref="myPointcut"/>
        <!-- 當有多個切面,但是又不想共用一個切入點表達式的時候,那么使用pointcut屬性來重新制定切入點表達式 -->
        <aop:advisor advice-ref="demoAspect" pointcut="execution(* edu.nf.ch12.service.impl.UserServiceImpl.run())"/>
    </aop:config>
</beans>

2、使用AspectJ配置,不實現任何接口

(1)使用配置文件配置切面

當使用AspectJ配置時,不需要實現任何接口,因為對這些通知的方法都是在配置文件中配置和綁定的。

創建一個切面類:

public class UserServiceAspect {

    /**
     * 前置通知
     * @param jp 連接點,通過這個連接點可以獲取目標方法
     */
    public void before(JoinPoint jp){
        System.out.println("前置通知,目標方法參數:" + jp.getArgs()[0]);
    }

    /**
     * 環繞通知
     * @param pjp 連接點,可以獲取目標方法參數以及方法信息以及調用目標方法等等
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("環繞通知前。。。");
        //獲取目標方法的的Method對象
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        Method method = ms.getMethod();
        System.out.println("當前調用的目標方法:" + method.getName());
        //調用目標方法
        Object returnVal = pjp.proceed();
        System.out.println("環繞通知后。。。");
        return returnVal;
    }

    /**
     * 后置通知
     * @param returnVal 目標方法的返回值
     */
    public void afterReturning(String returnVal){
        System.out.println("后置通知,返回參數:" + returnVal);
    }

    /**
     * 異常通知
     * @param e 目標方法產生的異常對象
     */
    public void afterThrowing(Throwable e){
        System.out.println("異常通知,異常信息:" + e.getMessage());
    }

    /**
     * 最終通知
     */
    public void after(){
        System.out.println("最終通知");
    }
}

在resources目錄中創建一個Spring配置文件applicationContext.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="edu.nf.ch13.service.impl.UserServiceImpl"/>
    <!-- 定義切面 -->
    <bean id="userServiceAspect" class="edu.nf.ch13.service.aspect.UserServiceAspect"/>

    <!-- 配置AOP,基於AspectJ -->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch13.service.impl.UserServiceImpl.*(..))"/>
        <!-- 裝配切面,ref引用上面配置的切面的id -->
        <aop:aspect ref="userServiceAspect">
            <!-- 裝配通知,method對應通知的方法名,pointcut-ref引用上面定義的切入點的id
             如果不同的通知想使用不同的切入點,那么使用pointcut屬性進行自定義 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="myPointcut"/>
            <!-- 環繞通知 -->
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <!-- 后置通知,returning屬性指定后置通知方法的參數名(參數名稱要一致) -->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
            <!-- 異常通知,throwing屬性指定異常通知方法的參數名(名稱要一致) -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>
            <!-- 最終通知 -->
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

(2)使用注解和配置類配置切面

在上面的applicationContext.xml配置文件中,對基於AspectJ的aop配置完全可以使用注解來配置,

在切面類中:

/**
 * AspectJ提供了所有通知注解,這些注解都有一個value屬性,
 * 屬性指定的是@Pointcut注解所標注的方法名,即pointcut(),
 * 如:@Around("pointcut()")
 * 如果不同通知想使用不同的切入點那么可以直接在value屬性中自定義
 * 如:@Before("execution(* edu.nf.ch14.service.impl.UserServiceImpl.say())")
 */
@Component
/**
 * @Aspect注解標識當前類為一個切面類
 */
@Aspect
public class UserServiceAspect extends AbstractAsPect {

    /**
     * 聲明一個切入點,@Pointcut聲明在一個方法上,在這里將這個方法放在了AbstractAspect這個類中
     */
//    @Pointcut("execution(* edu.nf.ch15.service.impl.UserServiceImpl.*(..))")
//    public void pointcut(){
//
//    }

    /**
     * 前置通知
     * @param jp 連接點,通過這個連接點可以獲取目標方法
     * pointcut()指定的是切入點注解所標注的方法名
     * @Before注解的value屬性指定的是切入點注解所標注的方法名,
     * 結果不同通知想使用不同的切入點
     */
    @Before("execution(* edu.nf.ch15.service.impl.UserServiceImpl.say())")
    public void before(JoinPoint jp){
        System.out.println("前置通知,目標方法參數:" + jp.getArgs()[0]);
    }

    /**
     * 環繞通知
     * @param pjp 連接點,可以獲取目標方法參數以及方法信息以及調用目標方法等等
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("環繞通知前。。。");
        //獲取目標方法的的Method對象
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        Method method = ms.getMethod();
        System.out.println("當前調用的目標方法:" + method.getName());
        //調用目標方法
        Object returnVal = pjp.proceed();
        System.out.println("環繞通知后。。。");
        return returnVal;
    }

    /**
     * 后置通知
     * @param returnVal 目標方法的返回值
     */
    @AfterReturning(value = "pointcut()", returning = "returnVal")
    public void afterReturning(String returnVal){
        System.out.println("后置通知,返回參數:" + returnVal);
    }

    /**
     * 異常通知
     * @param e 目標方法產生的異常對象
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("異常通知,異常信息:" + e.getMessage());
    }

    /**
     * 最終通知
     */
    @After("pointcut()")
    public void after(){
        System.out.println("最終通知");
    }
}

創建一個配置類,可以做到0配置,

SpringConfig:

@Configuration
@ComponentScan("edu.nf.ch15")
/**
 * 啟用AspectJ注解處理器,等同於xml中的<aop:aspectj-autoproxy/>
 */
@EnableAspectJAutoProxy
public class SpringConfig {
}

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM