spring學習總結013 --- AOP面向切面程式設計
阿新 • • 發佈:2020-07-16
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代理方式