11、AOP
11.1、AOP
AOP(Aspect Oriented Programming)意爲: 面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型,利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務各部分之間的耦合度降低,提高程序的重用性,同時提高了開發的效率。
11.2、AOP在Spring中的作用
提供聲明式事務,允許用戶自定義切面
- 橫切關注點:跨越應用程序多個模塊的方法或功能,即是,與我們業務邏輯無關的,但是我們需要關注的部分,就是橫切關注點。如日誌,安全,緩存,事務等
- 切面(ASPECT):且沒關注點,被模塊化的特殊對象,即,它是一個類
- 通知(Advice):且沒必須要完成的工作,即,它是類中的一個方法
- 目標(Targer):被通知的對象
- 代理(Proxy):向目標對象應用通知之後創建的對象
- 切入點(PoinCut):切面通知執行的“地點”的定義
- 連接點(JoinPoint):與切面點匹配的執行點
SpringAOP中,通過Advice定義橫切邏輯,Spring中支持5鍾類型的Advice
即Aop在不改變原代碼的情況下,去增加新的功能
11.3、使用Spring實現AOP
【需要導入一個依賴包】
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring的API接口
package com.zjx.service;
/**
* 業務
* @author zjx
* @date 2020/4/9 21:46
*/
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.zjx.service;
/**
* 真實對象
* @author zjx
* @date 2020/4/9 21:47
*/
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一個用戶");
}
public void delete() {
System.out.println("刪除了一個用戶");
}
public void update() {
System.out.println("更新了一個用戶");
}
public void select() {
System.out.println("查詢了一個用戶");
}
}
package com.zjx.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* 代理Proxy
* @author zjx
* @date 2020/4/9 21:49
*/
public class Log implements MethodBeforeAdvice {
//method 要執行的目標對象的方法
//objects 參數
//o 目標對象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被執行了");
}
}
package com.zjx.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* 代理 Proxy
* @author zjx
* @date 2020/4/9 21:52
*/
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getName()+"的"+method.getName()+"執行完畢了,返回結果爲: "+o);
}
}
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--註冊bean-->
<bean id="userService" class="com.zjx.service.UserServiceImpl"/>
<bean id="log" class="com.zjx.log.Log"/>
<bean id="afterLog" class="com.zjx.log.AfterLog"/>
<!--方式一,使用原生的API接口-->
<!--配置AOP 需要導入AOP的約束-->
<aop:config>
<!--切入點 就是我們需要在哪個地方去執行我們Spring的方法-->
<!--expression(要執行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.zjx.service.UserServiceImpl.*(..))"/>
<!--執行環繞增強-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
測試
import com.zjx.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author zjx
* @date 2020/4/9 22:02
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//動態代理的是接口
UserService service = context.getBean("userService", UserService.class);
service.add();
service.delete();
service.select();
service.update();
}
}
結果
com.zjx.service.UserServiceImpl的add被執行了
增加了一個用戶
com.zjx.service.UserServiceImpl的add執行完畢了,返回結果爲: null
com.zjx.service.UserServiceImpl的delete被執行了
刪除了一個用戶
com.zjx.service.UserServiceImpl的delete執行完畢了,返回結果爲: null
com.zjx.service.UserServiceImpl的select被執行了
查詢了一個用戶
com.zjx.service.UserServiceImpl的select執行完畢了,返回結果爲: null
com.zjx.service.UserServiceImpl的update被執行了
更新了一個用戶
com.zjx.service.UserServiceImpl的update執行完畢了,返回結果爲: null
方式二:自定義類來實現AOP
package com.zjx.diy;
/**
* 自定義切入點類
* @author zjx
* @date 2020/4/9 22:12
*/
public class DivPointCut {
public void before(){
System.out.println("=======方法執行前=======");
}
public void after(){
System.out.println("=======方法執行後=======");
}
}
<!--註冊bean-->
<bean id="userService" class="com.zjx.service.UserServiceImpl"/>
<bean id="log" class="com.zjx.log.Log"/>
<bean id="afterLog" class="com.zjx.log.AfterLog"/>
<!--方式二 自定義類-->
<bean id="diy" class="com.zjx.diy.DivPointCut"/>
<aop:config>
<!--自定義切面 ref要引用的類-->
<aop:aspect ref="diy">
<!--切入點-->
<aop:pointcut id="point" expression="execution(* com.zjx.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
測試結果
=======方法執行前=======
增加了一個用戶
=======方法執行後=======
=======方法執行前=======
刪除了一個用戶
=======方法執行後=======
=======方法執行前=======
查詢了一個用戶
=======方法執行後=======
=======方法執行前=======
更新了一個用戶
=======方法執行後=======
方式三:使用註解實現AOP
<!--註冊bean-->
<bean id="userService" class="com.zjx.service.UserServiceImpl"/>
<bean id="log" class="com.zjx.log.Log"/>
<bean id="afterLog" class="com.zjx.log.AfterLog"/>
<!--方式三-->
<bean id="annotationPointCut" class="com.zjx.diy.AnnotationPoinCut"/>
<!--開啓註解支持-->
<aop:aspectj-autoproxy/>
package com.zjx.diy;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 方式三:使用註解方式實現AOP
* @author zjx
* @date 2020/4/9 22:22
*/
@Aspect //標註這個類是一個切面
public class AnnotationPoinCut {
@Before("execution(* com.zjx.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法執行前");
}
@After("execution(* com.zjx.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法執行後");
}
}
結果
方法執行前
增加了一個用戶
方法執行後
方法執行前
刪除了一個用戶
方法執行後
方法執行前
查詢了一個用戶
方法執行後
方法執行前
更新了一個用戶
方法執行後