Spring2(瞭解Spring的AOP切面編程,必須看的一篇博文!!)

Spring的AOP

(一)什麼是AOP切面編程?

  • AOP(Aspect Oriented Programming)爲:面向切面編程,通過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。AOP是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
  • 舉個例子:
    你想給你的網站加上鑑權,對於一些url,你認爲不需要鑑權就可以訪問;但對於另一些url,你認爲需要有特定權限的用戶才能訪問,如果你沒有使用AOP,單純的面向對象,那你只能在那些url對應的Controller代碼裏面,一個一個寫上鑑權的代碼,如果你使用了AOP,對原有代碼就毫無入侵性了,這就是AOP的好處了,把和主業務無關的事情,放到代碼外面去做。

(二)AOP在Spring中的作用

提供聲明式事務;允許用戶自定義切面

  • 橫切關注點:跨越應用程序多個模塊的方法或功能。即與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日誌 , 安全 , 緩存 , 事務等;
  • 切面(ASPECT):橫切關注點被模塊化 的特殊對象,它就是一個類。
  • 通知(Advice):切面必須要完成的工作,它就是類中的一個方法。
  • 目標(Target):被通知對象。
  • 代理(Proxy):向目標對象應用通知之後創建的對象。
  • 切入點(PointCut):切面通知執行的“地點”的定義。
  • 連接點(JointPoint):與切入點匹配的執行點。
    在這裏插入圖片描述

(三)使用Spring實現AOP

SpringAOP中,通過Advice定義橫切邏輯,Spring中支持5種類型的Advice:

通知類型 連接點 實現接口
前置通知 方法前 org.springframework.aop.MethodBeforeAdvice
後置通知 方法後 org.springframework.aop.AfterReturningAdvice
環繞通知 方法前後 org.aopalliance.intercept.MethodInterceptor
異常拋出通知 方法拋出異常 org.springframework.aop.ThrowsAdvice
引介通知 類中增新的方法屬性 org.springframework.aop.IntroductionInterceptor

1.使用Spring的 API 接口實現AOP

1)用AOP需要導入的依賴:

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

2)定義一個接口,接口裏面定義四個方法(增刪改查):

public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void search();
}

3)定義一個實體類,實現上面的接口,我們在add()方法前後實現操作:

public class UserServiceImpl implements UserService {
    //這裏執行前置操作
    public void add(){
        System.out.println("增加用戶");
    }
    //這裏執行後置操作

    public void delete(){
        System.out.println("刪除用戶");
    }

    public void update() {
        System.out.println("更新用戶");
    }

    public void search(){
        System.out.println("查詢用戶");
    }
}

4)通過Advice定義橫切邏輯,實現AOP:

//前置通知
public class Log implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的" + method.getName() + "方法被執行了");
    }
}
//後置通知
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object o, Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "的" + method.getName() + "方法,返回了" + o);
    }
}

5)編寫配置類,注入bean、編寫增強配置類:
(這裏的expression表達式即類名的全稱)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://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">
        
    <bean id="userService" class="com.chen.service.impl.UserServiceImpl"/>
    <bean id="log" class="com.chen.config.Log"/>
    <bean id="afterlog" class="com.chen.config.AfterLog"/>

<aop:config>
        <aop:pointcut id="pc-userservice" expression="execution(* com.chen.service.impl.UserServiceImpl.*(..))"/>
        <!-- 配置環繞 -->
        <aop:advisor advice-ref="log" pointcut-ref="pc-userservice"/>
        <aop:advisor advice-ref="afterlog" pointcut-ref="pc-userservice"/>
</aop:config>

</beans>

6)測試類,我們調用add()方法:

@Test
public void test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userservice = (UserService) context.getBean("userService");
    userservice.add();
}
/*結果:
com.chen.service.impl.UserServiceImpl的add方法被執行了
增加用戶
com.chen.service.impl.UserServiceImpl的add方法,返回了null
*/

我們可以發現切面編程的切入效果,在add()方法執行的前後實現了切面。

2.自定義切面實現AOP

1)自定義一個切面,類中定義兩個方法;

public class DiyPointCut {

    public void before(){
        System.out.println("方法執行前");
    }
    public void after(){
        System.out.println("方法執行後");
    }
}

2)編寫配置類:

<bean id="diy" class="com.chen.config.DiyPointCut"/>
<!--配置切面:下面的方法來自於切面-->
<aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut id="pc-userservice" expression="execution(* com.chen.service.impl.UserServiceImpl.*(..))"/>
            <aop:after method="after" pointcut-ref="pc-userservice"/>
            <aop:before method="before" pointcut-ref="pc-userservice"/>
        </aop:aspect>
    </aop:config>

3)結果:

/*
方法執行前
增加用戶
方法執行後
*/

3.使用註解實現AOP

需在配置中增加<aop:aspectj-autoproxy/>標籤,讓spring識別註解;
1)註解切面,@Before 前置註解 @After 後置註解 @Around 環繞註解,在註解後面編寫execution表達式:

@Aspect
public class AnnotationPointCut {

    @Before("execution(* com.chen.service.impl.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法執行前");
    }

    @After("execution(* com.chen.service.impl.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法執行後");
    }

    @Around("execution(* com.chen.service.impl.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("環繞前");
        //要執行的方法是哪一個
        Signature signature = joinPoint.getSignature();
        System.out.println(signature);
        //執行方法
        Object proceed = joinPoint.proceed(); 
        System.out.println("環繞後");
        System.out.println(proceed);
    }
}

3)結果:

/*
環繞前
void com.chen.service.UserService.add()
方法執行前
增加用戶
環繞後
null
方法執行後
*/

我們可以發現通過AOP可以實現我們需要的操作,在方法執行前後實現自己的操作。

//下篇再見…謝謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章