Spring框架學習一(IOC、DI、AOP思想)

Spring框架學習一(IOC、DI、AOP思想)

1.控制反轉IOC和依賴注入DI

​ 首先了解到Spring框架是通過標籤來將對象注入到容器中,管理對象的創建過程。這就是控制反轉的思想。那麼爲什麼會出現這種思想呢?

​ 我們都知道,在採用面向對象方法設計的軟件系統中,它的底層實現都是由N個對象組成的,所有的對象通過彼此的合作,最終實現系統的業務邏輯。這麼多對象難免會存在耦合關係,類似於手錶中的齒輪,各個部件相互依賴,協同工作,完成指針的運轉。對象之間的關係就類似於齒輪,緊密相連。如果其中一個齒輪出現問題,整個系統就無法完成工作。對象之間的耦合關係是無法避免的,也是必要的,這是協同工作的基礎。但是必須得避免對象之間的多重依賴關係,因此出現了控制反轉這一思想。

什麼叫控制:傳統的程序設計中,當某個對象需要另一個對象時,通常是在對象中創建另一對象,而在Spring中將對象的創建放在容器中!就是由spring來負責控制對象的生命週期和對象間的關係。

什麼叫反轉依賴對象的獲取被反轉了,之前是主動創建的,而在Spring框架中是容器來控制注入到對象中,對象只是被動的接受依賴對象。

對於DI和IOC其實就是相同概念不同角度的描述

什麼叫依賴:應用程序依賴於IOC容器來創建和注入對象所需要的外部資源(對象,常量值…)。

什麼叫注入:IOC容器注入外部資源到對象中。

依賴注入”明確描述了“被注入對象依賴IoC容器配置依賴對象”。

SpringIOC的設計原理

在這裏插入圖片描述

2.注入的實現和註解

​ 在這需要先導入Spring的jar包和約束文件:

jar

  1. spring-beans:Spring IOC的基礎實現,包含訪問配置文件、創建和管理bean等。
  2. spring-core:Spring的核心容器
  3. spring-context:在基礎IOC功能上提供擴展服務
  4. spring-expression:Spring表達式語言

約束文件:spring-beans.xsd(bean標籤)

​ spring-context.xsd(指定掃描哪些包中註解)<context:component-scan base-package=“包名”>

2.1.實例化bean

  1. xml方式

    1. 主要是通過標籤
  2. 註解方式

    1. @Component
    2. @Controller
    3. @Service
    4. @Resposity

    這四個註解的作用都是一樣的,爲了方便區分才這樣寫!

2.2.bean屬性注入

​ Spring的本質就是一個bean工廠或者說是bean容器,它按照我們的要求,生產需要的bean。一般通過兩種方式對bean的屬性初始化。

  1. set方法注入:

    1. 值類型:<property name="" value=""></>
    2. 引用類型:<property name="" ref=""></>
  2. 構造注入:

    1. 值類型:<constructor name="參數名" value="參數值" index="參數名的索引位置" type="參數名的類型">
    2. 引用類型:value改成ref

    ref的值要和bean標籤中的id名一致

  3. 註解方式

    1. 值類型:
      1. 直接在字段上:@Value("")
      2. 在set方法上:@Value("")
    2. 引用類型:
      1. @Autowired:自動裝配,不需要指定類型名(當出現類型名相同的時候有可能無法選擇,不過一般沒有人會這麼做)
      2. @Qualifier(“類型名”):和Autowired一起使用指定類型名
      3. Resource(name=“類型名”):手動注入指定類型名

3.spring整合junit測試

​ 一般使用@test測試會比較麻煩,要先讀取配置文件在使用getBean()。

ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
User user=(User) ac.getBean("user");

​ Spring中有個spring-text.jar包可以幫助我們方便的進行測試。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
相當於
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
對於User user:
可以直接在上面通過註解的引用類型方式
@Autowired
User user;

4.Spring的AOP思想

​ 通過一個簡單的銀行例子來說明,銀行有兩個流程取款和查詢餘額,很明顯發現驗證用戶這一操作是重複的。

在這裏插入圖片描述

​ 於是我們可以將驗證用戶這一操作設置爲一個公共方法,這樣每個流程就不需要對於驗證用戶這一操作copy一份了。但是仍然會出現一個問題,每個流程都需要調用公共方法。

在這裏插入圖片描述

​ 這時聯想到spring的依賴注入,似乎可以將驗證用戶提取出來,當需要的時候就注入流程中。對於提取和注入的流程通過一些專用名詞來定義。

在這裏插入圖片描述

首先將取款流程和查詢流程看對是一個一個類。

Aspect切面:相當於Java中的類,在 Aspect 中會包含着一些 Pointcut 以及相應的 Advice。

PointCut切點:需要增強的方法,類似於上例中的驗證用戶。切點定義了Advice將要發生的地方。

Advice(通知/增強):也是一系列方法,主要是用來修飾PointCut,也就是說可以在驗證用戶操作之前之後調用這些方法來增強驗證用戶這一方法。類似於動態代理。

Join Point連接點:首先可以先想象一下一個切面不可能每個方法都是PointCut(需要增強)。joinPoint代表的是可以被增強的方法。

Target目標對象:被通知的對象,上例中的取款和查詢流程。

再舉一個例子來源

​ 下面我以一個簡單的例子來比喻一下 AOP 中 Aspect, Joint point, Pointcut 與 Advice之間的關係.

讓我們來假設一下, 從前有一個叫爪哇的小縣城, 在一個月黑風高的晚上, 這個縣城中發生了命案. 作案的兇手十分狡猾, 現場沒有留下什麼有價值的線索. 不過萬幸的是, 剛從隔壁回來的老王恰好在這時候無意中發現了兇手行兇的過程, 但是由於天色已晚, 加上兇手蒙着面, 老王並沒有看清兇手的面目, 只知道兇手是個男性, 身高約七尺五寸. 爪哇縣的縣令根據老王的描述, 對守門的士兵下命令說: 凡是發現有身高七尺五寸的男性, 都要抓過來審問. 士兵當然不敢違背縣令的命令, 只好把進出城的所有符合條件的人都抓了起來.

來讓我們看一下上面的一個小故事和 AOP 到底有什麼對應關係.
首先我們知道, 在 Spring AOP 中 Joint point 指代的是所有方法的執行點, 而 point cut是一個描述信息, 它修飾的是 Joint point, 通過 point cut, 我們就可以確定哪些 Joint point 可以被織入 Advice. 對應到我們在上面舉的例子, 我們可以做一個簡單的類比, Joint point 就相當於 爪哇的小縣城裏的百姓,pointcut 就相當於 老王所做的指控, 即兇手是個男性, 身高約七尺五寸, 而 Advice 則是施加在符合老王所描述的嫌疑人的動作: 抓過來審問.
爲什麼可以這樣類比呢?

  • Joint point : 爪哇的小縣城裏的百姓: 因爲根據定義, Joint point 是所有可能被織入 Advice 的候選的點, 在 Spring AOP中, 則可以認爲所有方法執行點都是 Joint point. 而在我們上面的例子中, 命案發生在小縣城中, 按理說在此縣城中的所有人都有可能是嫌疑人.

  • Pointcut :男性, 身高約七尺五寸: 我們知道, 所有的方法(joint point) 都可以織入 Advice, 但是我們並不希望在所有方法上都織入 Advice, 而 Pointcut 的作用就是提供一組規則來匹配joinpoint, 給滿足規則的 joinpoint 添加 Advice. 同理, 對於縣令來說, 他再昏庸, 也知道不能把縣城中的所有百姓都抓起來審問, 而是根據兇手是個男性, 身高約七尺五寸, 把符合條件的人抓起來. 在這裏 兇手是個男性, 身高約七尺五寸 就是一個修飾謂語, 它限定了兇手的範圍, 滿足此修飾規則的百姓都是嫌疑人, 都需要抓起來審問.

  • Advice :抓過來審問, Advice 是一個動作, 即一段 Java 代碼, 這段 Java 代碼是作用於 point cut 所限定的那些 Joint point 上的. 同理, 對比到我們的例子中, 抓過來審問 這個動作就是對作用於那些滿足 男性, 身高約七尺五寸 的爪哇的小縣城裏的百姓.

  • Aspect::Aspect 是 point cut 與 Advice的組合, 因此在這裏我們就可以類比: “根據老王的線索, 凡是發現有身高七尺五寸的男性, 都要抓過來審問” 這一整個動作可以被認爲是一個 Aspect.

1.Servlet中AOP的體現

​ AOP意思就是面向切面編程,可以用一句話概況AOP編程思想,即橫向重複,縱向抽取。在使用servlet編程的時候通常需要解決亂碼問題,每個Servlet都需要解決就很麻煩。通常使用filter來一次性解決代碼問題。

​ 參照第一個例子,將每個Servlet看成一個縱向流程,解決亂碼就是橫向重複的(對於每個Servlet都會有這個操作),Filter通過攔截每個Servlet然後定義一個類來解決亂碼問題,最後再放行。這就是縱向抽取(將亂碼問題抽取出來統一解決)。

2.Spring中的AOP實現

首先導入AOP的jar包和約束文件

jar包

  • spring-aop.jar:aop包
  • spring-aspect.jar:切面包
  • com.springsource.org.aspectj.weaver.RELEASE.jar:織入包
  • com.springsource.org.aopalliance.jar:aop聯盟包

約束文件:spring-aop.xsd

實現AOP原理

  1. 動態代理:有關動態代理詳解及源碼分析AOP主要實現就是通過動態代理,此方式需要被代理的類需要實現接口。
  2. cglib代理:可以對任何類實現代理,主要是對目標對象繼承代理(故類不能被final修飾)

applicationContext.xml

<!-- 1.配置目標對象 -->
	<bean name="userService" class="Service.UserServiceImpl" ></bean>
<!-- 2.配置通知對象 -->
	<bean name="myAdvice" class="Advice.MyAdvice" ></bean>
<!-- 3.配置將通知織入目標對象 -->
	<aop:config>
		<aop:pointcut expression="execution(* Service.*ServiceImpl.*(..))" id="pc"/>
		<aop:aspect ref="myAdvice" >
			<!-- 指定名爲before方法作爲前置通知 -->
			<aop:before method="before" pointcut-ref="pc" />
			<!-- 後置 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pc" />
			<!-- 環繞通知 -->
			<aop:around method="around" pointcut-ref="pc" />
			<!-- 異常攔截通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pc"/>
			<!-- 後置 -->
			<aop:after method="after" pointcut-ref="pc"/>
		</aop:aspect>
	</aop:config>
</beans>

UserServiceImpl.java(目標對象)

public class UserServiceImpl implements UserService{
	public void save() {
		System.out.println("保存用戶!");
	}
	public void delete() {
		System.out.println("刪除用戶!");
	}
	public void update() {
		System.out.println("更新用戶!");
	}
	public void find() {
		System.out.println("查找用戶!");
	}
}

UserService.java

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}

Myadvice.java(通知類)

//通知類
public class MyAdvice {
	public  void before(){
		System.out.println("前置通知!");
	}
	public  void afterReturning(){
		System.out.println("後置通知!(出現異常不會運行)");
	}
	public  Object around(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("環繞通知之前!");
		Object proceed=pjp.proceed();
		System.out.println("環繞通知之後!");
		return proceed;
	}
	public  void afterThrowing(){
		System.out.println("異常攔截通知!");
	}
	public  void after(){
		System.out.println("後置通知!(出現異常也會運行)");
	}
}

AOPDemo.java(測試)

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPDemo {
	@Resource(name="userService")
	private UserService us;
	@Test
	public void fun(){
		us.save();
	}
}

參考:https://www.cnblogs.com/xys1228/p/6057587.html?utm_source=itdadao&utm_medium=referral

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章