這學期的課程安排,我們可以系統的學習spring架構了。 相比較自己看部落格筆記,系統的在課堂上學習還是很有必要。
spring是一個開源架構,是為了解決企業應用程式開發複雜性而建立的。 架構的主要優勢之一就是利用其分層架構。 分層架構允許選擇使用哪一個元件,同時為了J2EE 應用程式開發提供內建的架構。
spring 的核心是 IOC, Inverse of control, 控制反轉, 和 AOP , Aspect oriented programming,圍繞切面的程式設計。
第一步:
導入maven包:
spring含有7個邏輯元件,我們有必要對每個元件的作用進行相應的了解,因為隻有了解了每個元件的作用以及它相關的依賴關系,我們在使用時才能左到心裡有數,才算得上“學習”吧。
我在早些時間對spring的4個包進行了源碼的周遊,雖然意義不大,但是糾正了我的一些誤區。 比如 : spring-beans,提供了很多的功能,但是它并不是spring提出的邏輯元件。 就像默默無聞的科技工作者一下,雖不被人所知,但是發揮着它強有力的作用! 此外,spring-beans 位于spring-aop邏輯元件中。 但是spring-aop即作為了一個邏輯元件的名稱,同時也是一個包。 可謂雙赢。 就像 李彥宏,馬克紮克伯格之類。 同時spring-context也是被自己周遊過,是以它裡面的每個類我都應該不是第一次照面。
先看書本的理論: spring-context即spring上下文是一個配置檔案,向spring架構提供上下文資訊。 spring上下文包括企業服務,如: JNDI,EJB,電子郵件,國際化,檢驗和排程。
了解這個概念的關鍵在于一個詞語: 上下文! 何謂上下文?
有道釋義:context: 上下文,語境。
了解上下文的含義對于初次接觸的人來說并不輕松。 先說自己所接觸過的上下文吧: 在學習javaweb的時候,首次聽說了上下文的概念----> 之後接觸到了spring架構,再次接觸到了 上下文的說法 ------> 直到最後學習了安卓,又接觸到了上下文,并且在裡面摸爬滾打了好一陣。 總算是對上下文有一些感性上的認識。
就我的了解來看,我們直到一個程式要被執行,它需要經過這樣一些流程: 源代碼(二進制串,被儲存在硬碟上)--->被加載進記憶體--> 運作。 當然它實際上還經曆了很多複雜的過程如連結資源檔案,記憶體重定位,權限驗證等等。廣範一點來說,上下文就是整個程式二進制串在記憶體中的長度所代表的所有内容。 狹義一點的說,它是程式使用者指定的目前程式的某一段記憶體長度。(不一定是連貫的,但是它們在邏輯一般具有連貫關系)。 上下文就是這樣的一種存在,能夠在你目前的環境(程序中)得到你想要得到的。
對上下文最簡單的一種實作: public static final Map context = new HashMap(); 隻要它能滿足: 單例,生命周期跟程序一樣長。 那麼他就是目前程式的上下文了。上下文的本質是記憶體。
繼續回到練習:當我們導入改包了之後,它提供給我們的包結構有:
其中屬于core邏輯元件的是: core,logging; 屬于aop元件的是:core,beans,aop; 屬于expression元件的是:core,beans,aop,expression,context。 總之,就是如果我們用的産品,都要依賴邏輯元件: spring-core,spring-aop,spring-context。 也就是要包含這裡的依賴。 當然,如果我們是要基于spring來開發産品,那麼依賴可能就會另算。
ok,那還有個問題,就是我們為什麼必需得用到上下文的時候才能IOC呢?
通過學習的理論知識我們知道,spring提供容器的接口是: BeanFactory,它位于核心元件: spring-aop下,(spring-beans包下)但是我們都知道,接口不能被我們執行個體化。 或者說隻能以全實作的方式實作一個接口,那樣的話接口存在的意義就不大了。 我對接口的了解就是,它具有發散性,抽閑類具有内聚性,當我們設計的時候,判斷某個邏輯塊将來具有發散的趨勢就用接口,具有内聚的趨勢就用抽象類。 是以,這個BeanFactory不能被我們直接使用,或者不能被我們友善的使用。是以我們需要尋找它的實作類。
在spring-context中就提供了兩個它的實作:
但是它實際應該是有三個實作的,還有一個是XmlWebApplicationContext。 它是與web項目緊密結合的,是以它的實作位于:
這裡順便提一下,web項目本身有一個上下文。 是由伺服器容器提供的,spring-web核心元件提供了相關的api使得這兩個容器在某種程度上能夠共享。 應該是共享而不是拷貝,因為無論從性能方面還是設計方面拷貝都不占什麼優勢,隻是在多線程并發上面可能有一點好處。
嗯嗯 ,它的三個實作類我知道的都寫在上面了。 但是去看Beanfactory的實作結構的時候,發現還是有一些非抽象類也實作了它,或許也可以用吧。 等以後去探究探究。
----------------------
到目前為止,我們已經實作了環境的準備。下面就開始使用吧。
先看ClassPathXmlApplicationContext的用法:
它可以通過指定一個父類上下文(相當于父類上下文的一個子類,類似于拷貝吧),單個配置檔案,多個配置檔案,以及單個配置檔案的路徑或者多個配置檔案的路徑。 嗯嗯算是所有的使用方式了呢!
再看看FileSystemXmlApplicationContext的用法:
它能夠通過父類上下文,單個配置檔案,多個配置檔案來進行配置。 它們的使用還是大同小異的。
我本次練習使用的是ClassPath的實作。
先定義好一個實體類:
package com.automannn.practice.entity;
/**
* @author [email protected]
* @time 2018/9/19 9:44
*/
public class User {
private Long id;
private String username;
public User() {
}
public User(Long id, String username) {
this.id = id;
this.username = username;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
'}';
}
}
然後建一個配置檔案:配置檔案的名稱随意。 但是必須引入shema的xml限制。 寫法再spirng.io可以查到。 但是其實不寫這個shema應該也是可以,隻是sping官方強制要我們寫,不然報錯。 因為這樣可以提高命中率,不會由于你的xml結果的問題而導緻程式異常退出。 因為遊戲規則是别人制定的,并且遵守規則對大家都有好處,是以我們按照它的shema來就是。
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用構造器執行個體的方法-->
<bean id="user" class="com.automannn.practice.entity.User" />
</beans>
最後就是使用了,最簡單的就是建立一個還有main入口方法的函數:
package com.automannn.practice;
import com.automannn.practice.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author [email protected]
* @time 2018/9/19 9:48
*/
public class Main {
public static void main(String[] args) {
ApplicationContext context=null;
//推薦使用
context= new ClassPathXmlApplicationContext("beans.xml");
//String[] configs = {"beans1.xml","beans2.xml","beans3.xml"};
//context = new ClassPathXmlApplicationContext(configs);
//推薦用于 使用spring 架構的獨立的應用程式種
//context = new FileSystemXmlApplicationContext("配置檔案的全路徑");
//String[] configss = {"path1","path2","path3"};
//context = new FileSystemXmlApplicationContext(configss);
//spring-web 提供的一個用于 web工程量身定制的一個實作類
//該靜态方法可支援 在jsp 和servlet 種 根據 ServletContext 取得 IOC容器的引用
//context = WebApplicationContextUtils.getRequiredWebApplicationContext(null);
User u = (User) context.getBean("user");
System.out.println(u.toString());
}
}
最後一步,就是将程式啟動起來了:
通過截圖,我們發現雖然列印了出來我們要的資訊,但是這幾乎不是我們要的結果。 因為我們用的時候都是: new User("屬性1","屬性2"); 或者:User u= new User(); u.setShuxing1("屬性1"); u.setShuxing2("屬性2"); 是以我們有必要了解bean 容器配置檔案的相關用法。
一,bean工廠獲得執行個體的方法:
1, 使用構造器的方法:
2,使用靜态工廠執行個體化的方法:
package com.automannn.practice.entity;
/**
* @author [email protected]
* @time 2018/9/19 10:19
*/
public class UserFactory {
private static final User user =new User(1L,"小二郎");
private UserFactory(){}
public static User getInstance(){
return user;
}
}
3,使用執行個體工廠執行個體化的方法:
package com.automannn.practice.entity;
/**
* @author [email protected]
* @time 2018/9/19 10:19
*/
public class UserFactory {
private static final User user =new User(1L,"小二郎");
private UserFactory(){}
public static User getInstance(){
return user;
}
public User getUser(){
return new User(2L,"李四");
}
}
二,依賴注入的配置方法:
1,property的方式:
2,有參構造方法的方式:
當參數類型沖突的時候,需将二者結合使用。
3.autowire,自動裝配的方式:
package com.automannn.practice.entity;
/**
* @author [email protected]
* @time 2018/10/9 12:42
*/
public class UserComponent {
User user;
public User getUser() {
return user;
}
@Override
public String toString() {
return user.toString();
}
}
預設的自動注入的方式是按類型。 是以當我們有多個接口的實作類并且以接口的方式注入的話,就不能用@Autowire,而用@Resource(name="").
當然這裡說到了注解,那麼注解又是spring架構提供給我們的另一個強大功能!也就是基于注解的spring容器。
基于注解的spring容器的學習:
在學習之前我們有必要回顧一些曆史:
java的注解與1.5引入,時間在2004年前後,同時,java1.5之後,Java就成了一門成熟的開發語言,算是正式在語言市場确定自己江湖霸主的地位。雖不能算上武林盟主,但怎麼也能算的是華山派啊,少林派啊這等有頭有臉的大門派了。
也就是04年左右,spring架構勢如破竹,在江湖上展露頭角,并且頗受大宗族的喜愛。一路勢如破竹,遂成為現今武林世界不容小觑的一股力量! 當然14年左右springboot也展露了頭角,它的未來必定又是一方雄霸。
是以,spring也好,springboot也好,它們的發展都是與java的脈絡發展息息相關的。 同時我們有必要知道,注解并不是spring所帶來的功能,而是Java自身的一個功能。 了解這點對我們學習很有必要!
下面就正式進入注解的學習:
為什麼使用注解? 因為它很好的滿足了我們程式開發的兩個要求:高内聚,低耦合。就是說,我們希望開發的時候,與一個邏輯相關的所有代碼都盡量的放在一起,越緊密越友善自己找到,就能更輕松的實作業務邏輯;但是對于邏輯聯系不強烈,或者說将來有很大可能性要修改的邏輯塊,我希望它們就最好一點關系都沒有,将來修改的時候不會導緻牽一發而動全身!
注解本身不給程式改變,它隻是提供了一種新型的方式程式設計。 就spring的ioc注解而言,它的邏輯任然是一樣的,它主要就是以注解去代替了xml配置檔案! 注意是代替,是以它們的邏輯功能是一毛一樣的。 因為當配置檔案大起來,多起來的時候,配合複雜的業務邏輯,足夠讓我們感到煩躁了!
bean容器的注解,實際上就一個: @Component
但是它派生了一些:@Controller,@Service,@Repository
自動裝配的注解: @Autowire; 此外要注意,自動裝配的方式是通過setter通路器寫入的,而非使用直接使用反射的方式去設定屬性。 大概是因為開銷的原因吧。 是以,當我們用@Resource(name="")的時候,即可以寫在屬性上面(前提是必須是非靜态屬性,并且有标準的setter通路器),也可以寫在setter通路器上的原因。
使用注解的步驟:
spring1.x,需要我們手動的打開注解的功能:
<context:annotation-config/>
配置掃描的包路徑:
<context:component-scan base-package="com.automannn.practice"/>
如果是spirng2.x及以後的版本,則隻需要指定掃描的包路徑就可以了。
----最後總結一下,spring容器的使用:
一,基于配置檔案
1,引入spirng-context的依賴
2,定義一個實體
3,建立一個配置檔案
4,使用spring容器
二,基于注解:
1,開啟注解掃描的功能