1. 程式人生 > 實用技巧 >spring學習總結013 --- AOP面向切面程式設計

spring學習總結013 --- AOP面向切面程式設計

AOP能夠在應用程式指定位置、指定時機,植入指定功能,並且不影響原程式的功能;

最簡單的而舉例:在已有應用程式的所有方法呼叫前後分別新增日誌;我們肯定不能修改程式,在每個方法前後新增日誌,這樣做工作量大,容易遺漏,更重要的是違背了“開閉原則”

AOP面向切面程式設計不是spring框架獨有的,其他比較著名的框架是AspectJ;我們常說的spring AOP是spring整合和AspectJ,當然spring自身也實現了AOP,不過比較厚重(在spring事務中有應用)

一些基本概念

  • 通知(Advice): AOP 框架中的增強處理。通知描述了切面何時執行以及如何執行增強處理。
  • 連線點(join point): 連線點表示應用執行過程中能夠插入切面的一個點,這個點可以是方法的呼叫、異常的丟擲。在 Spring AOP 中,連線點總是方法的呼叫。
  • 切點(PointCut): 可以插入增強處理的連線點。
  • 切面(Aspect): 切面是通知和切點的結合。
  • 引入(Introduction):引入允許我們向現有的類新增新的方法或者屬性。
  • 織入(Weaving): 將增強處理新增到目標物件中,並建立一個被增強的物件,這個過程就是織

這些概念比較抽象,需要在實際應用中理解

spring AOP例項(註解方式)

1、新增maven依賴:spring-aop、spring-aspects

、spring-context

2、建立spring配置類

@Configuration // 宣告為spring配置類
@ComponentScan(basePackages = {"com.demo"}) // 掃描com.demo包下的bean定義
@EnableAspectJAutoProxy // 為切面類建立代理, 植入增強
public class AopConfig {
}

3、定義切面

@Slf4j
@Aspect // 宣告為切面
@Component
public class LogAspect {

    // 定義切點及切點表示式, 即宣告在哪些方法織入切面
    @Pointcut("execution(public void com.demo.service.*.*(*))")
    
public void pointcut() { } @Before("pointcut()") // 織入前置通知 public void before(JoinPoint joinPoint) { log.info("Before invoke method:{}", joinPoint.getSignature().getName()); } }

4、業務程式碼,spring會使用CGLIB為該類生成代理類

@Service
public class AopService {

    public void func1(String name) {
        log.info("================Invoke func1, name:{}================", name);
    }
}

5、測試程式碼

    @Test
    public void test_cglib_proxy() {
        AopService aopService = applicationContext.getBean(AopService.class);
        aopService.func1("bale");
        log.info("AopService class:{}", aopService.getClass().getName());
    }

執行結果:

前面的業務程式碼採用CGLIB建立代理類,如果想採用JDK方式建立代理類,那麼需要業務方法為介面實現類,舉例如下:

介面:

public interface AopServiceIntf {
    public void printSomething(String name);
}

實現類:

@Slf4j
@Component
public class AopServiceIntfImpl implements AopServiceIntf {

    @Override
    public void printSomething(String name) {
        log.info("=====================Invoke printSomething, name:{}====================", name);
    }
}

測試類:

    @Test
    public void test_jdk_proxy() {
        AopServiceIntf aopServiceIntf1 = (AopServiceIntf) applicationContext.getBean("aopServiceIntfImpl");
        aopServiceIntf1.printSomething("bale1");
        log.info("AopServiceIntf1 class:{}", aopServiceIntf1.getClass().getName());
    }

執行結果:

spring AOP例項(XML配置方式)

1、spring配置檔案

<?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.demo" />

    <aop:aspectj-autoproxy />

    <bean class="com.demo.aop.LogXmlAspect" id="logXmlAspect" />

    <aop:config>
        <aop:aspect id="logAspect" ref="logXmlAspect">
            <aop:pointcut id="logPointcut" expression="execution(public void com.demo.service.*.*(*))"/>
            <aop:before method="before" pointcut-ref="logPointcut" />
        </aop:aspect>
    </aop:config>
</beans>

2、切面類

@Slf4j
public class LogXmlAspect {

    public void before(JoinPoint joinPoint) {
        log.info("Before invoke method:{}", joinPoint.getSignature().getName());
    }
}

3、同樣的測試類和業務類,執行結果:

指定代理方式

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

proxy-target-class="true"表示使用CGLIB代理方式

proxy-target-class="false"為預設值,如果委託類為介面實現類則使用JDK代理方式,否則採用CGLIB代理方式