SpringAOP原理分析

Spring核心知識

Spring是一個開源框架,Spring是於2003年興起的一個輕量級的Java開發框架,由Rod Johnson在其著做Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來。它是爲了解決企業應用開發的複雜性而建立的。框架的主要優點之一就是其分層架構,分層架構容許使用者選擇使用哪個組件,同時爲J2EE應用程序開發提供集成的框架。Spring使用基本的JavaBean來完成之前只可能由EJB完成的事情。然而,Spring的用途不只限於服務器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何Java應用均可以從Spring中受益。Spring的核心是控制反轉(IoC)和麪向切面(AOP)。簡單來講,Spring是一個分層的JavaSE/EEfull-stack(一站式)輕量級開源框架。java

爲何說Spring是一個一站式的輕量級開源框架呢?EE開發可分紅三層架構,針對JavaEE的三層結構,每一層Spring都提供了不一樣的解決技術。程序員

• WEB層:SpringMVCspring

• 業務層:Spring的IoCexpress

• 持久層:Spring的JDBCTemplate(Spring的JDBC模板,ORM模板用於整合其餘的持久層框架)編程

從上面的簡要介紹中,咱們要知道Spring的核心有兩部分:設計模式

• IoC:控制反轉。安全

舉例來講,在以前的操做中,比方說有一個類,咱們想要調用類裏面的方法(不是靜態方法),就要建立類的對象,使用對象調用方法實現。對於Spring來講,Spring建立對象的過程,不是在代碼裏面實現的,而是交給Spring來進行配置實現的。bash

AOP:面向切面編程。服務器

SpringAOP原理

AOP編程技術

什麼是AOP編程

AOP: Aspect Oriented Programming 面向切面編程。   面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP),是目前軟件開發中的一個熱點。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。   AOP是OOP的延續,是(Aspect Oriented Programming)的縮寫,意思是面向切面(方面)編程。   主要的功能是:日誌記錄,性能統計,安全控制,事務處理,異常處理等等。   主要的意圖是:將日誌記錄,性能統計,安全控制,事務處理,異常處理等代碼從業務邏輯代碼中劃分出來,經過對這些行爲的分離,咱們但願能夠將它們獨立到非指導業務邏輯的方法中,進而改 變這些行爲的時候不影響業務邏輯的代碼。架構

  能夠經過預編譯方式和運行期動態代理實如今不修改源代碼的狀況下給程序動態統一添加功能的一種技術。AOP實際是GoF設計模式的延續,設計模式孜孜不倦追求的是調用者和被調用者之間的解耦,AOP能夠說也是這種目標的一種實現。

假設把應用程序想成一個立體結構的話,OOP的利刃是縱向切入系統,把系統劃分爲不少個模塊(如:用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統,提取各個模塊可能都要重複操做的部分(如:權限檢查,日誌記錄等等)。因而可知,AOP是OOP的一個有效補充。

注意:AOP不是一種技術,其實是編程思想。凡是符合AOP思想的技術,均可以當作是AOP的實現。

Aop, aspect object programming 面向切面編程

​ 功能: 讓關注點代碼與業務代碼分離!

關注點

​ 關注點,重複代碼就叫作關注點;

切面

​ 關注點造成的類,就叫切面(類)!

​ 面向切面編程,就是指 對不少功能都有的重複的代碼抽取,再在運行的時候網業務方法上動態植入「切面類代碼」。

切入點

​ 執行目標對象方法,動態植入切面代碼。

​ 能夠經過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。

AOP底層實現原理

代理設計模式

什麼是代理模式

經過代理控制對象的訪問,能夠詳細訪問某個對象的方法,在這個方法調用處理,或調用後處理。既(AOP微實現) ,AOP核心技術面向切面編程。

代理模式應用場景

SpringAOP、事物原理、日誌打印、權限控制、遠程調用、安全代理 能夠隱蔽真實角色

代理的分類

靜態代理(靜態定義代理類)

動態代理(動態生成代理類)

Jdk自帶動態代理

Cglib 、javaassist(字節碼操做庫)

靜態代理
什麼是靜態代理

由程序員建立或工具生成代理類的源碼,再編譯代理類。所謂靜態也就是在程序運行前就已經存在代理類的字節碼文件,代理類和委託類的關係在運行前就肯定了。

靜態代理代碼
public interface IUserDao {
	void save();
}
public class UserDao implements IUserDao {
	public void save() {
		System.out.println("已經保存數據...");
	}
}
代理類
public class UserDaoProxy implements IUserDao {
	private IUserDao target;

	public UserDaoProxy(IUserDao iuserDao) {
		this.target = iuserDao;
	}

	public void save() {
		System.out.println("開啓事物...");
		target.save();
		System.out.println("關閉事物...");
	}

}

複製代碼
動態代理
什麼是動態代理

1.代理對象,不須要實現接口

2.代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象(須要咱們指定建立代理對象/目標對象實現的接口的類型)

3.動態代理也叫作:JDK代理,接口代理

JDK動態代理

1)原理:是根據類加載器和接口建立代理類(此代理類是接口的實現類,因此必須使用接口 面向接口生成代理,位於java.lang.reflect包下)

2)實現方式:

  1. 經過實現InvocationHandler接口建立本身的調用處理器 IvocationHandler handler = new InvocationHandlerImpl(…);
  2. 經過爲Proxy類指定ClassLoader對象和一組interface建立動態代理類Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
  3. 經過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
  4. 經過構造函數建立代理類實例,此時需將調用處理器對象做爲參數被傳入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

缺點:jdk動態代理,必須是面向接口,目標業務類必須實現接口

// 每次生成動態代理類對象時,實現了InvocationHandler接口的調用處理器對象 
public class InvocationHandlerImpl implements InvocationHandler {
	private Object target;// 這其實業務實現類對象,用來調用具體的業務方法
	// 經過構造函數傳入目標對象
	public InvocationHandlerImpl(Object target) {
		this.target = target;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;
		System.out.println("調用開始處理");
		result = method.invoke(target, args);
		System.out.println("調用結束處理");
		return result;
	}

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		// 被代理對象
		IUserDao userDao = new UserDao();
		InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
		ClassLoader loader = userDao.getClass().getClassLoader();
		Class<?>[] interfaces = userDao.getClass().getInterfaces();
		// 主要裝載器、一組接口及調用處理動態代理實例
		IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
		newProxyInstance.save();
	}

}

複製代碼
CGLIB動態代理

原理:利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。

什麼是CGLIB動態代理

使用cglib[Code Generation Library]實現動態代理,並不要求委託類必須實現接口,底層採用asm字節碼生成框架生成代理類的字節碼

CGLIB動態代理相關代碼

public class CglibProxy implements MethodInterceptor {
	private Object targetObject;
	// 這裏的目標類型爲Object,則能夠接受任意一種參數做爲被代理類,實現了動態代理
	public Object getInstance(Object target) {
		// 設置須要建立子類的類
		this.targetObject = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("開啓事物");
		Object result = proxy.invoke(targetObject, args);
		System.out.println("關閉事物");
		// 返回代理對象
		return result;
	}
	public static void main(String[] args) {
		CglibProxy cglibProxy = new CglibProxy();
		UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
		userDao.save();
	}
}

複製代碼

CGLIB動態代理與JDK動態區別

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,經過修改其字節碼生成子類來處理。

Spring中。

一、若是目標對象實現了接口,默認狀況下會採用JDK的動態代理實現AOP

二、若是目標對象實現了接口,能夠強制使用CGLIB實現AOP

三、若是目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

JDK動態代理只能對實現了接口的類生成代理,而不能針對類 。 CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法 。 由於是繼承,因此該類或方法最好不要聲明成final ,final能夠阻止繼承和多態。

AOP編程使用

註解版本實現AOP

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>  開啓事物註解權限
@Aspect							指定一個類爲切面類		
@Pointcut("execution(* com.service.UserService.add(..))")  指定切入點表達式
@Before("pointCut_()")				前置通知: 目標方法以前執行
@After("pointCut_()")				後置通知:目標方法以後執行(始終執行)
@AfterReturning("pointCut_()")		 返回後通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()")			異常通知:  出現異常時候執行
@Around("pointCut_()")				環繞通知: 環繞目標方法執行


@Component
@Aspect
public class AopLog {

	// 前置通知
	@Before("execution(* com.service.UserService.add(..))")
	public void begin() {
		System.out.println("前置通知");
	}


	// 後置通知
	@After("execution(* com.service.UserService.add(..))")
	public void commit() {
		System.out.println("後置通知");
	}

	// 運行通知
	@AfterReturning("execution(* com.service.UserService.add(..))")
	public void returning() {
		System.out.println("運行通知");
	}

	// 異常通知
	@AfterThrowing("execution(* com.service.UserService.add(..))")
	public void afterThrowing() {
		System.out.println("異常通知");
	}

	// 環繞通知
	@Around("execution(* com.service.UserService.add(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("環繞通知開始");
		proceedingJoinPoint.proceed();
		System.out.println("環繞通知結束");
	}
}


複製代碼

XML方式實現AOP

Xml實現aop編程:
	1) 引入jar文件  【aop 相關jar, 4個】
	2) 引入aop名稱空間
	3)aop 配置
		* 配置切面類 (重複執行代碼造成的類)
		* aop配置
			攔截哪些方法 / 攔截到方法後應用通知代碼
複製代碼
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 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">

	<!-- dao 實例 -->

	<bean id="userService" class="com.service.UserService"></bean>
	<!-- 切面類 -->
	<bean id="aop" class="com.aop2.AopLog2"></bean>
	<!-- Aop配置 -->
	<aop:config>
		<!-- 定義一個切入點表達式: 攔截哪些方法 -->
		<aop:pointcut expression="execution(* com.service.UserService.*(..))" id="pt" />
		<!-- 切面 -->
		<aop:aspect ref="aop">
			<!-- 環繞通知 -->
			<aop:around method="around" pointcut-ref="pt" />
			<!-- 前置通知: 在目標方法調用前執行 -->
			<aop:before method="begin" pointcut-ref="pt" />
			<!-- 後置通知: -->
			<aop:after method="after" pointcut-ref="pt" />
			<!-- 返回後通知 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pt" />
			<!-- 異常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pt" />
		</aop:aspect>
	</aop:config>

</beans>

複製代碼
public class AopLog2 {

	// 前置通知
	public void begin() {
		System.out.println("前置通知");
	}

	//
	// 後置通知
	public void commit() {
		System.out.println("後置通知");
	}

	// 運行通知
	public void returning() {
		System.out.println("運行通知");
	}

	// 異常通知
	public void afterThrowing() {
		System.out.println("異常通知");
	}

	// 環繞通知
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("環繞通知開始");
		proceedingJoinPoint.proceed();
		System.out.println("環繞通知結束");
	}
}

複製代碼
相關文章
相關標籤/搜索