Spring Aop

AOP 是什麼?

面向切面編程(Aspect Oriented Programing)

應用場景

適用於具備橫切邏輯的場合,如性能檢測、訪問控制、事務管理及日誌記。編程

AOP

兩種攔截方式

  • 基於註解
  • 基於方法

鏈接點 JoinPoint

Spring 近支持方法的鏈接點,能在方法調用前、方法調用後、方法拋出異常時及方法調用先後這些執行點織入加強bash

鏈接點是客觀存在的方法ide

切點 Pointcut

指定在哪些類的哪些方法上織入橫切邏輯
一個切點能夠匹配多個鏈接點
對應鏈接點表達式性能

加強 Advice

加強是織入目標類鏈接點上的橫切邏輯代碼,包含定位鏈接點的方位信息ui

  • 前置加強:方法執行前加強 @Before("logPointCut()")
  • 後置加強:方法執行後加強 @After("logPointCut()")
  • 環繞加強:方法執行先後加強 @Around("logPointCut()")
  • 異常拋出加強:方法拋出異常後加強 @AfterThrowing()
  • 引介加強:在目標類添加新的方法和屬性

切面 Aspect

切面由切點和加強組成。對應一個類this

織入

織入是將加強添加到目標類的具體鏈接點上的過程。
Spring採用動態代理織入,AspectJ採用編譯器織入和類裝載期織入。spa

實例

@Slf4j
@Aspect
@Component
public class LogAspect {
	
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Pointcut("execution(public * com.pengtech.school.*.controller..*.*(..))")
    public void logPointCut(){
    }
    
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    	// 接收到請求,記錄請求內容
    	ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    	HttpServletRequest request = attributes.getRequest();
    	startTime.set(System.currentTimeMillis());
    	log.info("########請求地址:" + request.getRequestURL());
    	log.info("########開始時間:" + LocalDateTime.now());
    	log.info("########請求方法:" + joinPoint.getSignature().getDeclaringTypeName() +
    		"." + joinPoint.getSignature().getName());
    	Object[] args = joinPoint.getArgs();
    	for (Object param : args) {
    		log.info("########方法參數:" + param);
    	}
    }
    
    @After("logPointCut()")
    public void doAfter() throws Throwable {
    	log.info("#######執行間隔:" + (System.currentTimeMillis() - startTime.get())
    			+ "ms");
    	log.info("#######完成時間:" + LocalDateTime.now());
    	
    }
}
複製代碼

AOP 面向切面編程

概述

適合具備橫切邏輯的應用場合,如性能監測、訪問控制、事務管理和日誌記錄。

Spring AOP使用動態代理技術在運行期向目標類織入加強的代碼。
複製代碼

Spring AOP 兩種代理機制

  • 基於JDK的動態代理
  • 基於CGLib的動態代理
JDK 動態代理
在運行期建立接口的代理實例
限制:只能爲接口建立代理實例

// InvocationHandler 實現該接口定義橫切邏輯,並經過反射機制調用目標類的代碼,動態的將橫切邏輯和業務邏輯編織在一塊兒。
public class PerformanceHandler implements InvocationHandler {
    // 目標業務類
	private Object target;
	
	public PerformanceHandler(Object target){
		this.target = target;
	}
	
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		// 橫切代碼
		PerformanceMonitor.begin(target.getClass().getName()+"."+ method.getName());
		// 反射調用目標類的目標方法
		Object object = method.invoke(target, args);
		// 橫切代碼
		PerformanceMonitor.end();
		return object;
	}
}
Proxy 利用InvocationHandler動態建立一個符合某一接口的實例,生成目標類的代理對象

// 使用JDK動態代理

    // 被代理的目標業務類
	ForumService target = new ForumServiceImpl();
	// 將目標業務類和橫切代碼編織到一塊兒
	PerformanceHandler handler = new PerformanceHandler(target);
	// 建立代理實例
	ForumService proxy = (ForumService) Proxy.newProxyInstance(target
			.getClass().getClassLoader(),
			target.getClass().getInterfaces(), handler);
	// 調用代理方法		
	proxy.removeForum(10);
	proxy.removeTopic(1012);
複製代碼

CGLib 動態代理

動態建立子類的方式生成代理對象
不能對final或private方法進行代理

//實現MethodInterceptor接口,並重寫intercept方法
public class CglibProxy implements MethodInterceptor {
	private Enhancer enhancer = new Enhancer();

    //建立動態代理對象
	public Object getProxy(Class clazz) {
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		return enhancer.create();
	}

    // 攔截全部目標類方法的調用
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
	                        MethodProxy proxy) throws Throwable {
		PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
		Object result=proxy.invokeSuper(obj, args);
		PerformanceMonitor.end();
		return result;
	}
	
	//動態生成子類的方式建立代理類
	CglibProxy cglibProxy = new CglibProxy();
	ForumService forumService = (ForumService)cglibProxy.getProxy(ForumServiceImpl.class);
	forumService.removeForum(10);
	forumService.removeTopic(1023);
}
複製代碼
相關文章
相關標籤/搜索