AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。html
AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。不經過修改源代碼方式,在主幹功能裏面添加新功能。java
官方文檔:點我傳送spring
鏈接點: 類裏能夠被加強的方法稱爲鏈接點。express
切入點: 實際被加強的方法稱爲切入點。編程
通知(加強):加強的邏輯部分稱爲通知。api
切面: 通知應用到切入點的過程稱爲切面。markdown
AOP 底層使用動態代理,有兩種狀況動態代理:oracle
方式一: JDK動態代理 (有接口狀況)
方式二: CGLIB 動態代理 (沒有接口狀況)框架
JDK1.8官方文檔:點我傳送ide
public class Proxy extends Object implements Serializable{ /** * loader:類加載器 * interfaces:加強方法所在的類,這個類實現的接口,支持多個接口 * InvocationHandler:實現這個接口 InvocationHandler,建立代理對象,寫加強的部分 * * Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler. */ static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h); }
public interface UserDao { public int add(int a,int b); public String update(String str); }
public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) { return a+b; } @Override public String update(String str) { return str; } }
public class JDKProxy { public static void main(String[] args) { //建立 接口實現類代理對象 /** * @CallerSensitive * public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) * * public interface InvocationHandler {} * */ Class[] interfaces = {UserDao.class}; UserDaoImpl userDao = new UserDaoImpl(); UserDao proxyInstance = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int result = proxyInstance.add(1, 2); String abc = proxyInstance.update("abc"); } } //建立代理對象代碼 class UserDaoProxy implements InvocationHandler { //把須要建立的代理對象 傳遞過來 private Object obj; public UserDaoProxy(Object obj) { this.obj = obj; } //加強的邏輯(部分) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法以前 System.out.println("方法以前執行: " + method.getName() + " 傳遞的參數:" + Arrays.toString(args)); //加強的方法執行 Object invoke = null; if (method.getName().equals("add")) { System.out.println("UserDaoProxy add ..."); invoke = method.invoke(obj, args); } if(method.getName().equals("update")){ System.out.println("UserDaoProxy update ..."); invoke = method.invoke(obj, args); } //方法以後 System.out.println("方法以後執行: " + obj); return invoke; } }
AspectJ 不是 Spring 組成部分,獨立 AOP 框架,通常把 AspectJ 和 Spirng 框架一塊兒使用,進行 AOP 操做。
= 基於 AspectJ 實現 AOP 操做
- 基於 xml 配置文件實現 - 基於註解方式實現
com.springsource.net.sf.cglib-2.2.0.jar com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar commons-logging-1.1.1.jar druid-1.1.9.jar spring-aop-5.2.6.RELEASE.jar spring-aspects-5.2.6.RELEASE.jar spring-beans-5.2.6.RELEASE.jar spring-context-5.2.6.RELEASE.jar spring-core-5.2.6.RELEASE.jar spring-expression-5.2.6.RELEASE.jar
# 切入點表達式語法結構: execution([權限修飾符] [返回類型] [類全路徑] [方法名稱]([參數列表]) ) 舉例 1:對 com.hosystem.dao.BookDao 類裏面的 add 進行加強 execution(* [返回類型可省略] com.hosystem.dao.BookDao.add(..)) 舉例 2:對 com.hosystem.dao.BookDao 類裏面的全部的方法進行加強 execution(* com.hosystem.dao.BookDao.* (..)) 舉例 3:對 com.hosystem.dao 包裏面全部類,類裏面全部方法進行加強 execution(* com.hosystem.dao.*.* (..))
User.java
public class User { public void add(){ System.out.println("User add ..."); } }
UserProxy.java
//加強類 public class UserProxy { //前置通知 public void before(){ System.out.println("UserProxy before ... "); } }
在 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 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.hosystem.spring5.dao.aopannotation"></context:component-scan> </beans>
使用註解建立 User 和 UserProxy 對象
//加強類 @Component public class UserProxy {...} @Component public class User {...}
加強類上面添加註解 @Aspect
//加強類 @Component @Aspect //生成代理對象 public class UserProxy { //前置通知 public void before(){ System.out.println("UserProxy before ... "); } }
在 spring 配置文件中開啓生成代理對象
<!-- 開啓Aspect生成代理對象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
在加強類的裏面,在做爲通知方法上面添加通知類型註解,使用切入點表達式配置
//加強類 @Component @Aspect //生成代理對象 public class UserProxy { //前置通知 //@Before註解表示做爲前置通知 @Before(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void before() { System.out.println("UserProxy before ... "); } //最終通知 @After(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void after() { System.out.println("UserProxy after ... "); } //後置通知(返回通知) @AfterReturning(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void afterReturning() { System.out.println("UserProxy afterReturning ... "); } //異常通知 @AfterThrowing(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void afterThrowing() { System.out.println("UserProxy afterThrowing ... "); } //環繞通知 //public interface ProceedingJoinPoint extends JoinPoint{} @Around(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("UserProxy aroud 環繞以前... "); //被加強的方法執行 proceedingJoinPoint.proceed(); System.out.println("UserProxy aroud 環繞以後... "); } }
//加強類 @Component @Aspect //生成代理對象 public class UserProxy { //相同切入點抽取 @Pointcut(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void pointdemo(){} //前置通知 //@Before註解表示做爲前置通知 @Before(value = "pointdemo()") public void before() { System.out.println("UserProxy before ... "); } }
public class TestAop { /** * 正常執行流程: * UserProxy aroud 環繞以前... * UserProxy before ... * User add ... * UserProxy aroud 環繞以後... * UserProxy after ... * UserProxy afterReturning ... * * 有error錯誤流程: * UserProxy aroud 環繞以前... * UserProxy before ... * UserProxy after ... * UserProxy afterThrowing ... */ @Test public void testAopAnno(){ ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean1.xml"); User user = classPathXmlApplicationContext.getBean("user", User.class); user.add(); } }
有多個加強類多同一個方法進行加強,設置加強類優先級。
在加強類上面添加註解 @Order(數字類型值),數字類型值越小優先級越高
@Aspect @Component @Order(1) public class PersonProxy { //後置通知(返回通知) @Before(value = "execution(* com.hosystem.spring5.dao.aopannotation.User.add(..))") public void before() { System.out.println("PersonProxy before ... "); } }
建立配置類,不須要建立 xml 配置文件
@Configuration @ComponentScan(basePackages = {"com.hosystem"}) @EnableAspectJAutoProxy(proxyTargetClass = true) //<aop:aspectj-autoproxy></aop:aspectj-autoproxy> public class ConfigAop { }
建立兩個類,加強類和被加強類,建立方法
Book.java
public class Book { public void buy(){ System.out.println("book buy ..."); } }
BookProxy.java
public class BookProxy { public void befor(){ System.out.println("BookProxy before ..."); } }
<!-- 建立對象--> <bean id="book" class="com.hosystem.spring5.dao.aopxml.Book"></bean> <bean id="bookProxy" class="com.hosystem.spring5.dao.aopxml.BookProxy"></bean> <!-- 配置aop加強--> <aop:config> <!-- 切入點--> <aop:pointcut id="pcut" expression="execution(* com.hosystem.spring5.dao.aopxml.Book.buy(..))"/> <!-- 配置切面--> <aop:aspect ref="bookProxy"> <!-- 加強做用在具體的方法上--> <aop:before method="before" pointcut-ref="pcut"></aop:before> </aop:aspect> </aop:config>