什么是AOP及其作用
1攒暇、AOP被稱之為面向切面(方面)編程(Aspect Oriented Programming)簡寫
2铺纽、AOP可以對業(yè)務(wù)邏輯各個方面進行隔離趟咆,從而降低代碼的耦合性
呵扛,提高代碼的可復(fù)用性
通俗講就是在不通過修改源碼的情況下實現(xiàn)主干功功能上增加新的功能
AOP中常用術(shù)語
1电湘、連接點:類中需要被增強的方法都稱之為連接點
2隔节、切入點:實際被增強的方法被稱之切入點
3、通知(增強):實際增強的邏輯被稱之為通知
4胡桨、切面:切面是一個過程
官帘,將通知應(yīng)用到切入點的過程被稱之為切面
通知的類型
1瞬雹、前置通知:切入點之前執(zhí)行的邏輯
2昧谊、后置通知:切入點之后執(zhí)行的邏輯
3、環(huán)繞通知:切入點前后都執(zhí)行邏輯
4酗捌、異常通知:當切入點發(fā)生異常后的邏輯
5呢诬、返回通知:當返回結(jié)果是執(zhí)行的邏輯
上述通知代碼
1、pom.xml
<dependencies>
<!-- junit測試包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- spring-context 包含Spring-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- aspectj -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- aspectj -->
</dependencies>
2胖缤、BookDao需要被增強的類
package work.chenc.spring.dao;
import org.springframework.stereotype.Component;
// 需要增強類
@Component
public class BookDao {
// 可以被增強的方法-連接點
public void insert() {
int A = 10 / 0;
System.out.println("執(zhí)行了insert().........:" + A);
}
// 需要被增強的方法-切入點
public void add() {
System.out.println("執(zhí)行了add().........");
}
}
3尚镰、BookProxy增強類—在什么位置對BookDao類中的方法進行增強邏輯
package work.chenc.spring.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
// 增強類
@Component
@Aspect
public class BookProxy {
// 相同切入點的抽取
@Pointcut(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void pointcutDemo() { }
// 前置通知
// @Before(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
// 自定義公共切入點的使用
@Before(value = "pointcutDemo()")
public void before() {
System.out.println("前置通知——執(zhí)行了before()方法");
}
// 后置通知 - 在方法執(zhí)行之后執(zhí)行
@After(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void after() {
System.out.println("后置通知——執(zhí)行了after()方法");
}
// 返回通知-在方法返回后執(zhí)行
@AfterReturning(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void afterReturning() {
System.out.println("后置通知——執(zhí)行了afterReturning()方法");
}
// 環(huán)繞通知
@Around(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("環(huán)繞通知之前-----------");
proceedingJoinPoint.proceed();
System.out.println("環(huán)繞通知之前-----------");
}
// 異常通知
@AfterThrowing(value = "execution(* work.chenc.spring.dao.BookDao.insert(..))")
public void afterThrowing() {
System.out.println("異常通知........");
}
}
4、bean.xml—開啟注解掃描 開啟Aspectj生成代理對象
<?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">
<!--
這里新增了一個aop的名名稱空間
xmlns:aop="http://www.springframework.org/schema/aop"
-->
<!-- 開啟注解掃描 -->
<context:component-scan base-package="work.chenc.spring.*"></context:component-scan>
<!--
開啟Aspect注解生成代理對象
到開啟注解掃描的類里面尋找有@Aspect注解的類哪廓,并聲稱對應(yīng)的代理對象
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
5狗唉、測試類
@Test
public void aspectTest() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:bean.xml");
BookDao bookDao = applicationContext.getBean("bookDao", BookDao.class);
// 測試非異常通知
// bookDao.add();
// 測試異常通知
bookDao.insert();
}
AOP部分解釋說明
1)什么是AspectJ
AspectJ不是Spring的組成部分,是獨立的AOP框架涡真,一般把AspectJ和Spring框架一起使用分俯,進行AOP操作
2)切入點表達式
execution([權(quán)限修飾符][返回類型][類的全路徑]方法名稱)
execution(* work.chenc.spring.dao.BookDao.add(..))
[權(quán)限修飾符]:public default protected private *表示所有
[返回類型]:Java中的類如String int 等自定義類型,空格表示void
3)相同切入點的抽取前置通知中有使用
@Pointcut(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void pointcutDemo() {}
AOP存在多個增強類對同一個方法增強時哆料,設(shè)置增強類的優(yōu)先級
目標
存在BookProxy和BookOtherProxy兩個增強類對通一個方法進行增強缸剪,BookOtherProxy中的增強方法先執(zhí)行
代碼如下
1)新增一個增強類BookOtherProxy
package work.chenc.spring.proxy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Order(1)
public class BookOtherProxy {
@Pointcut(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void pointcut() {
}
@Before(value = "pointcut()")
public void before() {
System.out.println("BookOtherProxy........");
}
}
2)在原有的BookProxy中新增一個注解@Order(2)
package work.chenc.spring.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
// 增強類
@Component
@Aspect
@Order(2)
public class BookProxy {
// 相同切入點的抽取
@Pointcut(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void pointcutDemo() {}
// 前置通知
// @Before(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
// 自定義公共切入點的使用
@Before(value = "pointcutDemo()")
public void before() {
System.out.println("前置通知——執(zhí)行了before()方法");
}
// 后置通知 - 在方法執(zhí)行之后執(zhí)行
@After(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void after() {
System.out.println("后置通知——執(zhí)行了after()方法");
}
// 返回通知-在方法返回后執(zhí)行
@AfterReturning(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void afterReturning() {
System.out.println("后置通知——執(zhí)行了afterReturning()方法");
}
// 環(huán)繞通知
@Around(value = "execution(* work.chenc.spring.dao.BookDao.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("環(huán)繞通知之前-----------");
proceedingJoinPoint.proceed();
System.out.println("環(huán)繞通知之前-----------");
}
// 異常通知
@AfterThrowing(value = "execution(* work.chenc.spring.dao.BookDao.insert(..))")
public void afterThrowing() {
System.out.println("異常通知........");
}
}
@Order()解釋
Order這里的注解是對增強方法進行優(yōu)先級的區(qū)分括號中的填入數(shù)字,從0
開始东亦,數(shù)字越大杏节,優(yōu)先級越低
通過上述的代碼先執(zhí)行的是BookOtherProxy中的前置通知before(),然后再執(zhí)行BookProxy中的前置通知