Github源碼下載地址:https://github.com/chenxingxing6/sourcecode/tree/master/code-springaop
一、前言
AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,通過預編譯方
式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,通過對這些行爲的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行爲的時候不影響業務邏輯的代碼。
Aop相關專業術語:
- Aspect(切面): Aspect 聲明類似於 Java 中的類聲明,在 Aspect 中會包含着一些 Pointcut 以及相應的Advice。
- Jointpoint(連接點):表示在程序中明確定義的點,典型的包括方法調用,對類成員的訪問以及異常處理程序塊的執行等等,它自身還可以嵌套其它joint point。
- Pointcut(切點):表示一組 joint point,這些 joint point或是通過邏輯關係組合起來,或是通過通配、正則表達式等方式集中起來,它定義了相應的 Advice 將要發生的地方。
- Advice(增強):Advice 定義了在 Pointcut 裏面定義的程序點具體要做的操作,它通before、after 和around 來區別是在每個 joint point 之前、之後還是代替執行的代碼。 Target(目標對象):織入 Advice的目標對象.。
- Weaving(織入):將 Aspect 和其他對象連接起來, 並創建 Adviced object 的過程。
AOP底層使用動態代理實現。包括兩種方式:
使用JDK動態代理實現。
使用cglib來實現
通知類型
前置通知:在方法之前執行
後置通知:在方法之後執行
異常通知:方法出現異常執行
最終通知:在後置之後執行
環繞通知:在方法之前和之後執行
二、自己實現AOP代理(jdk代理)
2.1需求
1.前置通知,後置返回通知,環繞通知,後置通知,異常通知
2.自己實現一個簡單版IOC容器
3.通過動態代理 + 通知註解類實現
4.定義切入點,正則表達式 @pointcut
2.2實現思路
服務啓動時,將項目中的bean注入到容器裏面,對應有切面進行代理的bean,重新生成代理對象,替換原理的對象。
①被代理類;
②被代理類要實現的接口;
③代理類;
④動態創建“代理類的對象”的類;
⑤註解類:
a. 切面註解類,註解在類上:@Aspect
b. 各種通知註解,註解在方法上:
__@Before
__@AfterReturning
__@After
__@AfterThrowing
__@Around
⑥IOC容器:BeanFactory
2.3項目結構
2.4如何配置,測試項目
本項目配置切面,只支持@MyPointcut方式,支持正則表達式;然後將pointcut放到對應切面通知上。
1.編寫Aspect切面
2.測試
只支持jdk動態代理
package com.demo1;
import com.aop.annotation.*;
import com.aop.aspect.AbstractAspect;
import com.aop.aspect.JoinPoint;
import com.ioc.annotation.MyComponent;
/**
* @Author: cxx
* @Date: 2019/10/3 11:25
* 日誌切面
*/
@MyAspect
@MyComponent
public class LogAspect extends AbstractAspect{
@MyPointcut("com.demo1.*")
private void myPointcut(){
}
@MyAround(value = "myPointcut()")
public Object around(JoinPoint joinPoint){
Long start = System.currentTimeMillis();
System.out.println("環繞通知start....");
Object obj= joinPoint.proceed();
System.out.println("環繞通知end....");
Long end = System.currentTimeMillis();
System.out.println("執行方法耗時:" + String.valueOf(end - start));
return obj;
}
}
2.5 核心代碼AopBeanContext
package com.aop.core;
import com.aop.aspect.AspectHandler;
import com.aop.aspect.CheckResult;
import com.aop.aspect.IAspectHandler;
import com.ioc.core.MyIoc;
import javafx.util.Pair;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: cxx
* @Date: 2019/10/3 13:45
*/
public class AopBeanContext {
private static IAspectHandler aspectHandler = null;
static {
init();
}
public static Object getObject(Class clz){
Object target = MyIoc.getObject(clz);
if (target == null){
throw new RuntimeException("容器中獲取不到實例");
}
Pair<Object, CheckResult> aspectResult = aspectHandler.getAspectInstance(clz.getTypeName());
// 沒有切面
if (aspectResult == null){
return target;
}
// 創建代理類
return ProxyBeanFactory.newProxyBean(target, aspectResult);
}
private static void init(){
createProxyBeanContext(MyIoc.getBeanFactory());
}
// 根據切點,創建有代理類的bean容器
public static void createProxyBeanContext(Map<String, Object> iocMap){
aspectHandler = new AspectHandler(iocMap);
}
}
2.6 核心代碼BeanInvocationHandler
package com.aop.core;
import com.aop.annotation.AdviceEnum;
import com.aop.aspect.AbstractAspect;
import com.aop.aspect.CheckResult;
import com.aop.aspect.JoinPoint;
import javafx.util.Pair;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author: cxx
* @Date: 2019/10/3 15:35
* @Desc: 環繞通知先不實現
*/
public class BeanInvocationHandler implements InvocationHandler {
// 目標類
private Object target;
// 切面相關信息
private Pair<Object, CheckResult> acpectResult;
public BeanInvocationHandler(Object target, Pair<Object, CheckResult> acpectResult) {
this.target = target;
this.acpectResult = acpectResult;
}
/**
* 內部類實現環繞通知
*/
class MyJoinPoint implements JoinPoint{
private Method method;
private Object[] args;
public MyJoinPoint(Method method, Object[] args) {
this.method = method;
this.args = args;
}
@Override
public Object proceed() {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 切面類
AbstractAspect aspcet = (AbstractAspect) acpectResult.getKey();
// 通知方式
CheckResult runType = acpectResult.getValue();
boolean isAround = runType.isRunAround();
JoinPoint joinPoint = null;
if (isAround){
joinPoint = new MyJoinPoint(method, args);
}
Object result = null;
try {
// 1.前置通知
if (runType.isRunBefore()){
runAspectInstance(AdviceEnum.BEFORE, aspcet, args);
}
// 2.環繞通知
if (isAround){
result = aspcet.around(joinPoint);
}else {
result = method.invoke(target, args);
}
// 3.返回通知
if (runType.isRunAfterReturning()){
runAspectInstance(AdviceEnum.AFTER_RETURNING, aspcet, args, result);
}
return result;
}catch (Exception e){
// 4.異常通知
if (runType.isRunAfterThrowing()){
runAspectInstance(AdviceEnum.AFTER_THROWING, aspcet, args, e);
}
}finally {
// 5.後置通知
if (runType.isRunAfter()){
runAspectInstance(AdviceEnum.AFTER, aspcet, args);
}
}
return result;
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args){
this.runAspectInstance(adviceEnum, aspect, args, null, null);
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Object result){
this.runAspectInstance(adviceEnum, aspect, args, null, result);
}
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Throwable e){
this.runAspectInstance(adviceEnum, aspect, args, e, null);
}
/**
* 執行切面實例
* @param adviceEnum
* @param aspect
* @param args
*/
private void runAspectInstance(AdviceEnum adviceEnum, AbstractAspect aspect, Object[] args, Throwable e, Object result){
try {
switch (adviceEnum){
case BEFORE:{
aspect.before();
break;
}
case AFTER_RETURNING:{
aspect.afterReturning(result);
break;
}
case AFTER_THROWING:{
aspect.afterThrowable(e);
break;
}
case AFTER:{
aspect.after();
break;
}
default:{
break;
}
}
}catch (Exception ee){
ee.printStackTrace();
}
}
}
三、測試結果
package com.aop;
import com.aop.core.AopBeanContext;
import com.demo1.ILogService;
import com.demo1.LogService;
/**
* @Author: cxx
* @Date: 2019/10/3 11:34
*/
public class MainTest {
public static void main(String[] args) {
// 01.測試 修改ioc.properties scan.package=com.demo
//IUserService userService = (IUserService) AopBeanContext.getObject(UserService.class);
//userService.delete("100");
// 02.環繞通知測試 修改ioc.properties scan.package=com.demo1
ILogService logService = (ILogService) AopBeanContext.getObject(LogService.class);
logService.printLog("test....");
}
}