介紹
Spring是一個分層的(一站式) 輕量級開源框架
Spring的核心是控制反轉(IoC)和麪向切面(AOP)
爲什麼說分層一站式呢?
javaEE分三層開發 WEB層,業務層,持久層。在ssh整合框架中s == Struts2, s == spring,h == Hibernate ,spring 的一站式開發就是不用struts2 和hibernate,在spring中有SpringMvc可以替代Struts2,springJDBC可以替代Hibernate。等於一個spring框架可以快速開發JavaEE應用。關於輕量級就不太多說了,spring整個框架打包出來也才1M多的內存大小。spring運行中的消耗也不大。那肯定是輕量級的框架。
什麼是IOC的功能?
概念
IoC – Inverse of Control,控制反轉,將對象的創建權反轉給Spring!!
使用IOC可以解決的程序耦合性高的問題!!
控制反轉
假設我需要做一個功能,在這個功能當中我需要調用servic層,然後再調用dao層,去取數據。在傳統的javaEE開發中我就直接去new一個service 然後再new一個dao。但是在spring框架中,我們吧new service和new dao的權利交個spring框架,假設我需要使用我就直接去spring框架中尋找。等於說我的資源創建的權利交給了spring框架,這就叫做控制反轉。
解耦
剛剛我們說資源創建交給了sring,我們需要什麼就找spring。這過程就像是工廠模式。但是在spring框架中它需要創建哪些對象,它需要一個配置文件。這個配置文件告訴spring,需要創建哪些資源。
例如:假設我需要去數據庫查詢數據顯示頁面
程序啓動,spring框架去找配置文件創建資源,把資源放置再一個容器中,開始運行,前端請求數據,在spring中找controller層,再找service層,再找dao層要數據,最後數據原路返回controller,再顯示到頁面上。其中service被spring注入到controlller層,dao層被spring注入到service層。這個過程分工明確。每一層各司其職。傳統的一個開發,在servlet中直接new然後去查數據,然後數據返回到界面上。萬一操作一多所有的判斷,查詢不同的表,這個servlet的代碼變得十分的臃腫。不說開發慢,你開發完了看代碼也費勁。 所以說控制反轉可以用來解耦
什麼是面向切面(AOP)?
概念
- 在軟件業,AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程
- AOP是一種編程範式,隸屬於軟工範疇,指導開發者如何組織程序結構
- AOP最早由AOP聯盟的組織提出的,制定了一套規範.Spring將AOP思想引入到框架中,必須遵守AOP聯盟的規範
- 通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術
- AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型
- 利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率
其實AOP可以在不修改源代碼的前提下,對程序進行增強!!
Spring框架的AOP的底層實現
Srping框架的AOP技術底層也是採用的代理技術,代理的方式提供了兩種
- 基於JDK的動態代理
必須是面向接口的,只有實現了具體接口的類才能生成代理對象
- 基於CGLIB動態代理
對於沒有實現了接口的類,也可以產生代理,產生這個類的子類的方式
- Spring的傳統AOP中根據類是否實現接口,來採用不同的代理方式
- 如果實現類接口,使用JDK動態代理完成AOP
- 如果沒有實現接口,採用CGLIB動態代理完成AOP
JDK的動態代理
注意:需要實現類接口
例子:假設我有兩個工作,工作1,工作2.
//寫一個接口
public interface Working {
void wokingOne();
void WorkingTwo();
}
//接口實現類
public class WorkingImpl implements Working {
@Override
public void wokingOne() {
System.out.println("做任務1");
}
@Override
public void WorkingTwo() {
System.out.println("做任務2");
}
}
好的,現在我要先做任務1,然後再做任務2我們的寫法爲:
public static void main(String[] args) {
Working working = new WorkingImpl();
working.wokingOne();//做任務1
working.WorkingTwo();//做任務2
}
好的精彩的地方來了,我再做任務2之前我要先休息10分鐘,但是不能修改源代碼。怎麼班呢?這時候就用到我們的JDK動態代理了。代碼如下:
先寫一個代理的工具類。再做任務2前我們休息十分鐘
public class MyProxyUtils {
public static Working getProxy(final Working working) {
// 使用Proxy類生成代理對象
Working proxy = (Working) Proxy.newProxyInstance(working.getClass().getClassLoader(),
working.getClass().getInterfaces(), new InvocationHandler() {
// 代理對象方法一直線,invoke方法就會執行一次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//再做工作2之前我先休息10分鐘
if ("WorkingTwo".equals(method.getName())) {
System.out.println("休息10分鐘");
}
//工作繼續進行下去
return method.invoke(working, args);
}
});
// 返回代理對象
return proxy;
}
}
public static void main(String[] args) {
Working working = new WorkingImpl();
Working proxy = MyProxyUtils.getProxy(working);
proxy.wokingOne();
proxy.WorkingTwo();
}
運行的結果可想而知:
做任務1
休息10分鐘
做任務2
CGLIB動態代理
注意:沒有實現類接口
CGLIB也是一個java項目,所以要使用它就要引入CGLIB的開發的jar包,因爲在Spring框架核心包(core)中已經引入了CGLIB的開發包了。所以直接引入Spring核心開發包即可
好我們同樣使用上面的例子來做事例吧
//工作類
public class Working {
public void wokingOne() {
System.out.println("做任務1");
}
public void WorkingTwo() {
System.out.println("做任務2");
}
}
new一個對象調用任務1 任務2得到的結果這個就沒必要說了。我們重點看下怎麼使用CGLIB來在不改變源碼的情況下,做任務之前休息十分鐘。
public static Working getProxy(){
// 創建CGLIB核心的類
Enhancer enhancer = new Enhancer();
// 設置父類
enhancer.setSuperclass(Working.class);
// 設置回調函數
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
if("WorkingTwo".equals(method.getName())){
// 休息10分鐘
System.out.println("休息10分鐘...");
}
return methodProxy.invokeSuper(obj, args);
}
});
// 生成代理對象
Working proxy = (Working) enhancer.create();
return proxy;
}
public static void main(String[] args) {
Working working = new WorkingImpl();
Working proxy = MyCglibUtils.getProxy(working);
proxy.wokingOne();
proxy.WorkingTwo();
}
最後的運行結果也是相同的:
做任務1
休息10分鐘
做任務2
分析
我上面簡單分析了一下spring AOP的底層實現的兩種方式:JDK動態代理,CGLIB。在Spring框架中它會自動選擇使用哪一種方式。如果有接口實現類,那就使用jdk動態代理,沒有接口就使用CGLIB。有了這個功能那麼我們修改東西就方便多了。假設我再做任務一前我需要記錄下日誌。我就直接寫一個切面類,直接去記錄日誌。都不用修改本身的源碼。
結尾
作者剛剛學習spring中,記錄一些知識點,梳理了一下自己的理解,如有錯誤希望各位大神提出。