1、什么是AOP?(是什么)
???Aspect OrientedProgramming的縮寫,意思就是:面向切面編程肥惭,通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)舅巷。這種在運行時套硼,動態(tài)地將代碼切入到類的指定方法垫毙、指定位置上的編程思想就是面向切面的編程霹疫。它是一種編程范式,一種編程思想综芥。AOP是Spring提供的關(guān)鍵特性之一丽蝎。對了,有人提到這個和攔截器有什么關(guān)系膀藐,注意AOP是一種編程思想屠阻,攔截器就是基于這種編程思想實現(xiàn)的。
基本概念:
切面(Aspect):一個關(guān)注點的模塊化额各,這個關(guān)注點可能會橫切多個對象国觉。
連接點(Joinpoint):程序執(zhí)行過程中的某個特定的點
通知(Advice):在切面的某個特定的連接點上執(zhí)行的動作。
切入點(Pointcut): 匹配連接點的斷言虾啦,在AOP中通知和一個切入點表達(dá)式的關(guān)聯(lián)麻诀。
引入(Introduction)在不修改類代碼的前提下,為類添加新的方法和屬性缸逃。
目標(biāo)對象(target Object)被一個或者多個切面所通知的對象
AOP代理(Aop proxy)AOP框架創(chuàng)建的對象针饥,用來實現(xiàn)切面契約(aspectcontract)包括通知放法執(zhí)行等功能。
織入(weaving) 把切面連接到其他的應(yīng)用程序類型或者對象上需频,并創(chuàng)建一個被通知的對象丁眼,分為編制時織入,類加載時織入昭殉,執(zhí)行時織入苞七。
2、aop編程思想的作用E捕(為什么)
???其實aop就是對OOP編程的有效補充蹂风。其實最終目的是實現(xiàn)程序的解耦。但是OOP一般允許開發(fā)者定義縱向的關(guān)系乾蓬,但并不適合定義橫向關(guān)系惠啄,使用AOP技術(shù)可以將一些系統(tǒng)性相關(guān)的編程工作、獨立提取出來任内,獨立實現(xiàn)撵渡,然后通過切面切入進(jìn)系統(tǒng)。從而避免了在業(yè)務(wù)邏輯的代碼中混入很多的系統(tǒng)相關(guān)的邏輯——比如權(quán)限管理死嗦,事物管理趋距,日志記錄等等。這些系統(tǒng)性的編程工作都可以獨立編碼實現(xiàn)越除,然后通過AOP技術(shù)切入進(jìn)系統(tǒng)即可节腐。從而達(dá)到了 將不同的關(guān)注點分離出來的效果外盯。
???如上圖,我們可以將日志以及權(quán)限這種分離出來翼雀,然后在需要用到的這些核心功能模塊中橫向的切入進(jìn)去饱苟,這樣對于核心模塊是無感的,這樣的話講大大降低系統(tǒng)的耦合性和復(fù)雜性锅纺,試想如果這些模塊散落在各個核心業(yè)務(wù)中掷空,如果出現(xiàn)大的改動,必然會產(chǎn)生極大的維護(hù)成本囤锉,需要每一處都進(jìn)行修改坦弟。
3、實現(xiàn)方式
** 方式一:**@AspectJ注解驅(qū)動的切面官地,這個是一種靜態(tài)代理的方式
???Java通過Java編譯器將.java源文件編譯成.class字節(jié)碼文件酿傍,這種.class文件是二進(jìn)制文件,內(nèi)容是只有JVM虛擬機能夠識別的機器碼驱入,JVM虛擬機讀取字節(jié)碼文件赤炒,取出二進(jìn)制數(shù)據(jù),加載到內(nèi)存中亏较,解析.class文件內(nèi)的信息莺褒,生成對應(yīng)的Class對象,進(jìn)而使Class對象創(chuàng)建類的具體實例來進(jìn)行調(diào)用實現(xiàn)具體的功能雪情。
???所以這種方式就是在java編譯成class的時候去實現(xiàn)代理遵岩。性能上很高效,但是沒有不太靈活巡通。
???這種方式我的理解就是在編譯器將切片插入到對應(yīng)的函數(shù)棧中(僅僅自己的理解)
** 方式二:**運行期動態(tài)代理尘执,(JDK動態(tài)代理,CGLib動態(tài)代理)
純jiava的實現(xiàn)宴凉,無需特殊的編譯過程誊锭,不需要控制類加載器層次。
目前只支持放法執(zhí)行連接點弥锄,(通知spring bean的方法的執(zhí)行)
不是提供一個最完整的AOP實現(xiàn)盡管它已經(jīng)非常強大丧靡,而是側(cè)重用于提供一種AOP實現(xiàn)和SpringIOC容器之間的整合,用于幫助解決企業(yè)應(yīng)用中常見的問題籽暇。
Spring AOP 不會和Aspect競爭温治,從而提供綜合全面的AOP解決方案。
???spring中AOP代理由Spring的IOC容器負(fù)責(zé)生成图仓,管理,其依賴關(guān)系也是由IOC容器負(fù)責(zé)管理的但绕,因此救崔,AOP代理可以直接使用容器中的其他bean實例作為目標(biāo)惶看,這種關(guān)系可由IOC容器的依賴注入提供,Spring創(chuàng)建代理的規(guī)則為:
1)六孵、默認(rèn)使用java動態(tài)代理來創(chuàng)建AOP代理纬黎,這樣就可以為任何接口實例創(chuàng)建代理了。
2)劫窒、當(dāng)需要代理的類不是代理接口的時候本今,Spring會切換為使用CGLB代理,也可強制使用CGLIB(CGLIB是一個強大的主巍、高性能的代碼生成庫)
4冠息、應(yīng)用場景
???其實這里有點多余,aop是一種編程思想孕索,你可以在很多場景中去使用逛艰,但是還是想給大家一個直觀的感受。
5搞旭、在sping中使用切面編程散怖。(怎么用?)
基于@AspectJ注解的AOP實現(xiàn)
???這里主要是采用這種方式來演示aop的實現(xiàn)過程肄渗,來直觀的體會aop實現(xiàn)的思想镇眷。
step1:引用依賴
<!--aop aspectj-->
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
Step2:創(chuàng)建測試類
IAopTest.java 接口
package com.mjcc.spring.aop;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:40 2019/7/28
* @Modified By:
*/
public interface IAopTest {
public int add(int a, int b);
public int sub(int a, int b);
}
AopTestImpl.java 實現(xiàn)類
package com.mjcc.spring.aop;import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:42 2019/7/28
* @Modified By:
*/
@Component("aopTest")
public class AopTestImpl implements IAopTest {
public int add(int a, int b) {
int res = a + b;
return res;
}
public int sub(int a, int b) {
int res = a - b;
return res;
}
}
LoggerAspect.java // 日志的切面編程
package com.mjcc.spring.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.List;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:59 2019/7/28
* @Modified By:
*/
@Aspect
// 告訴spring 這是一個切面類,就是橫切關(guān)注點的集合翎嫡,與核心業(yè)務(wù)代碼無關(guān)的日志欠动,權(quán)限,事務(wù)代碼 集中起來組成切面
@Componentpublic
class LoggerAspect {
private Logger logger = Logger.getLogger(this.getClass());
// 指定beforeMethod在哪個類中的哪個方法前切入執(zhí)行
// @Before的參數(shù)里钝的,寫一個切入點表達(dá)式翁垂,來告訴spring 要將下面的方法切入到哪個類的哪個方法前面執(zhí)行
@Before("execution(public int com.mjcc.spring.aop.AopTestImpl.add(int, int))")
@Before("execution(public int com.mjcc.spring.aop.AopTestImpl.*(int, int))")
public void beforeMethod(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName(); // 拿到切入點的的方法名
List<Object> args = Arrays.asList(joinPoint.getArgs());
logger.info(methodName + "方法運算執(zhí)行之前。硝桩。沿猜。。碗脊。啼肩。。" + args);
}
}
測試base類
package com.mjcc.spring.base;
import org.junit.After;
import org.junit.Before;
import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StringUtils;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 14:18 2019-04-13
* @Modified By:
*/
public class UnitTestBase {
private ClassPathXmlApplicationContext context;
private String springXmlPath;
public UnitTestBase () {
}
public UnitTestBase(String springXmlPath) {
this.springXmlPath = springXmlPath;
}
@Before // 初始化方法衙伶,執(zhí)行當(dāng)前測試類的每個測試方法前執(zhí)行
public void before () {
if (StringUtils.isEmpty(springXmlPath)) {
springXmlPath = "classpath*:spring-*.xml";
}
try {
context = new ClassPathXmlApplicationContext(springXmlPath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After //釋放資源祈坠,執(zhí)行當(dāng)前測試類的每個測試方法后執(zhí)行
public void after () {
context.destroy();
}
@SuppressWarnings("unchecked")
protected <T extends Object> T getBean (String beanId) {
try {
return (T)context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
protected <T extends Object> T getBean (Class<T> clazz) {
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
測試類:
package com.mjcc.spring.aop;
import com.mjcc.spring.base.UnitTestBase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
/**
* @Description:
* @Author: chengcheng
* @Date: Create in 8:47 2019/7/28
* @Modified By:
*/
@RunWith(BlockJUnit4ClassRunner.class)
public class AopTestImplTest extends UnitTestBase {
public AopTestImplTest () {
super("applicationContext.xml");
}
@Test
public void AopTest () {
IAopTest aopTest = (IAopTest) super.getBean("aopTest");
System.out.println("add : " + aopTest.add(1, 1));
System.out.println("sub : " + aopTest.sub(8, 5));
}
}
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: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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.mjcc.spring"></context:component-scan>
<!--自動生成aspecyj 動態(tài)代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
切入點:
- @Before和它注解的這個方法在AOP中叫做通知,在上面基礎(chǔ)概念中已經(jīng)提到了矢劲, before屬于前置通知赦拘。
還有下面這些通知,
@After 后置通知芬沉,在目標(biāo)方法執(zhí)行之后執(zhí)行
@AfterReturning 返回通知躺同,(在目標(biāo)方法返回結(jié)果之后執(zhí)行)
@AfterThrowing異步通知阁猜,在目標(biāo)方法拋出異常之后執(zhí)行
@Around 環(huán)繞通知
???對了,注意一個問題蹋艺,當(dāng)同一個目標(biāo)方法有多個切面的時候剃袍,到底是哪個先執(zhí)行,這個順序可以使用@Order注解來注解切入類捎谨,值越小民效,越先執(zhí)行。@Order(1) > @Order(2)
個人拙見涛救,望指教畏邢。
關(guān)注公眾號回復(fù)獲取“java入門就業(yè)”獲取一套個人收藏的java視頻教程 ,