一、Spring架構概述
Spring是一個非常活躍的開源架構, 它是一個基于IOC和AOP來構架多層JavaEE系統的架構,它的主要目地是簡化企業開發。
Spring以一種非侵入式的方式來管理你的代碼, Spring提倡“最少侵入”,這也就意味着你可以适當的時候安裝或解除安裝Spring。
Spring采用獨立的子產品式開發,這樣可以有選擇的使用其中的子產品,降低了業務對象的複雜性群組件之間的耦合性。
Spring的AOP支援允許将一些通用的功能,如:性能分析,事務管理,日志記錄等進行集中的子產品處理,提高了複用,降低了和業務功能的耦合。
Spring支援和ORM層以及MVC層架構的整合,簡化了對其他層次的通路。
Spring給開發者帶來的好處:降低元件之間的耦合度,實作軟體各層之間的解耦
不用關心事務API就可以執行資料庫事務;
不用關心遠端API就可以使用遠端操作;
不用關心JMX API就可以進行管理操作;
不用關心JMS API就可以進行消息處理。
①JMX,Java Management eXtension,Java管理擴充,是一個為應用程式、裝置、系統等植入管理功能的架構。JMX可以跨越一系列異構作業系統平台、系統體系結構和網絡傳輸協定,靈活的開發無縫內建的系統、網絡和服務管理應用。
②JMS,Java Message Service,Java消息服務,是Java平台上有關面向消息中間件(MOM)的技術規範,它便于消息系統中的Java應用程式進行消息交換,并且通過提供标準的産生、發送、接收消息的接口簡化企業應用的開發。
下載下傳
Spring官網: http://spring.io
下載下傳位址: http://repo.springsource.org/libs-release-local/org/springframework/spring/
1.1、Spring架構的特征和功能
輕量:從大小與開銷兩方面而言Spring都是輕量的。完整的Spring架構可以在一個大小隻有1MB多的JAR檔案裡釋出。并且Spring所需的處理開銷也是微不足道的。此外,Spring是非侵入式的:典型地,Spring應用中的對象不依賴于Spring的特定類。
控制反轉Ioc:Spring通過一種稱作控制反轉(IoC)的技術促進了低耦合。當應用了IoC,一個對象依賴的其它對象會通過被動的方式傳遞進來,而不是這個對象自己建立或者查找依賴對象。你可以認為IoC與JNDI相反——不是對象從容器中查找依賴,而是容器在對象初始化時不等對象請求就主動将依賴傳遞給它。
面向切面Aop:Spring提供了面向切面程式設計的豐富支援,允許通過分離應用的業務邏輯與系統級服務(例如審計(auditing)和事務(transaction)管理)進行内聚性的開發。應用對象隻實作它們應該做的——完成業務邏輯——僅此而已。它們并不負責(甚至是意識)其它的系統級關注點,例如日志或事務支援。
容器:Spring包含并管理應用對象的配置和生命周期,在這個意義上它是一種容器,你可以配置你的每個bean如何被建立——基于一個可配置原型(prototype),你的bean可以建立一個單獨的執行個體或者每次需要時都生成一個新的執行個體——以及它們是如何互相關聯的。然而,Spring不應該被混同于傳統的重量級的EJB容器,它們經常是龐大與笨重的,難以使用。
架構:Spring可以将簡單的元件配置、組合成為複雜的應用。在Spring中,應用對象被聲明式地組合,典型地是在一個XML檔案裡。Spring也提供了很多基礎功能(事務管理、持久化架構內建等等),将應用邏輯的開發留給了你。
MVC:Spring的作用是整合,但不僅僅限于整合,Spring 架構可以被看做是一個企業解決方案級别的架構,Spring MVC是一個非常受歡迎的輕量級Web架構。
所有Spring的這些特征使你能夠編寫更幹淨、更可管理、并且更易于測試的代碼。它們也為Spring中的各種子產品提供了基礎支援。
1.2、Spring的組成
Spring 架構是一個分層架構,由 7 個定義良好的子產品組成。Spring 子產品建構在核心容器之上,核心容器定義了建立、配置和管理 bean 的方式
組成 Spring 架構的每個子產品(或元件)都可以單獨存在,或者與其他一個或多個子產品聯合實作。每個子產品的功能如下:
- 核心容器:核心容器提供 Spring 架構的基本功能。核心容器的主要元件是
,它是工廠模式的實作。BeanFactory
使用控制反轉(IOC) 模式将應用程式的配置和依賴性規範與實際的應用程式代碼分開。BeanFactory
- Spring 上下文:Spring 上下文是一個配置檔案,向 Spring 架構提供上下文資訊。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和排程功能。
- Spring AOP:通過配置管理特性,Spring AOP 子產品直接将面向方面的程式設計功能內建到了 Spring 架構中。是以,可以很容易地使 Spring 架構管理的任何對象支援 AOP。Spring AOP 子產品為基于 Spring 的應用程式中的對象提供了事務管理服務。通過使用 Spring AOP,不用依賴 EJB 元件,就可以将聲明性事務管理內建到應用程式中。
- Spring DAO:JDBC DAO 抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不同資料庫供應商抛出的錯誤消息。異常層次結構簡化了錯誤處理,并且極大地降低了需要編寫的異常代碼數量(例如打開和關閉連接配接)。Spring DAO 的面向 JDBC 的異常遵從通用的 DAO 異常層次結構。
- Spring ORM:Spring 架構插入了若幹個 ORM 架構,進而提供了 ORM 的對象關系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和 DAO 異常層次結構。
- Spring Web 子產品:Web 上下文子產品建立在應用程式上下文子產品之上,為基于 Web 的應用程式提供了上下文。是以,Spring 架構支援與 Jakarta Struts 的內建。Web 子產品還簡化了處理多部分請求以及将請求參數綁定到域對象的工作。
- Spring MVC 架構:MVC 架構是一個全功能的建構 Web 應用程式的 MVC 實作。通過政策接口,MVC 架構變成為高度可配置的,MVC 容納了大量視圖技術,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 架構的功能可以用在任何 J2EE 伺服器中,大多數功能也适用于不受管理的環境。Spring 的核心要點是:支援不綁定到特定 J2EE 服務的可重用業務和資料通路對象。毫無疑問,這樣的對象可以在不同 J2EE 環境 (Web 或 EJB)、獨立應用程式、測試環境之間重用。
1.3、Spring Boot與Spring Cloud
Spring Boot 是 Spring 的一套快速配置腳手架,可以基于Spring Boot 快速開發單個微服務,Spring Cloud是一個基于Spring Boot實作的雲應用開發工具;Spring Boot專注于快速、友善內建的單個微服務個體,Spring Cloud關注全局的服務治理架構;Spring Boot使用了限制優于配置的理念,很多內建方案已經幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot來實作,Spring Boot可以離開Spring Cloud獨立使用開發項目,但是Spring Cloud離不開Spring Boot,屬于依賴的關系。
SpringBoot在SpringClound中起到了承上啟下的作用,如果你要學習SpringCloud必須要學習SpringBoot。
二、IOC簡介
IOC(Inversion of Control,即控制反轉),是一種設計思想,IOC其實就是包含并管理應用對象的配置和生命周期,你可以配置你的每個bean如何被建立,也可以配置每個bean是隻有一個執行個體,還是每次需要時都生成一個新的執行個體,以及它們是如何互相關聯的。
DI(Dependency injection,即依賴注入)是IOC的一種常見的方式,是指應用在運作期,由外部容器(Spring容器)動态地将依賴對象注入到另一個對象中。
IOC的思想最核心的地方在于,資源不由使用資源的雙方管理,而由不使用資源的第三方管理,這可以帶來很多好處:
第一,資源集中管理,實作資源的可配置和易管理。
第二,降低了使用資源雙方的依賴程度,也就是我們說的耦合度。
IOC是Spring架構的核心内容,使用多種方式完美的實作了IOC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置實作IoC。Spring容器在初始化時先讀取配置檔案,根據配置檔案或中繼資料建立與組織對象存入容器中,程式使用時再從Ioc容器中取出需要的對象。
采用XML方式配置Bean的時候,Bean的定義資訊是和實作分離的,而采用注解的方式可以把兩者合為一體,Bean的定義資訊直接以注解的形式定義在實作類中,進而達到了零配置的目的。
以上部分内容借鑒于部落格:https://www.cnblogs.com/best/p/5727935.html#_label3
補充:
BeanFactory是IOC容器的核心接口,它的職責包括:執行個體化、定位、配置應用程式中的對象及建立這些對象間的依賴。BeanFactory隻是個接口,并不是IOC容器的具體實作,但是Spring容器給出了很多種實作,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等
ApplicationContext:通過API文檔可以知道,ApplicationContext是BeanFactory的子接口。大部分程式都不會使用BeanFactory作為容器接口(通常JavaSE應用或者手機應用使用),而是使用ApplicationContext作為Spring的容器接口,它增強了BeanFactory的功能。
ApplicationContext的實作有四種方式:
FileSystemXmlApplicationContext:加載配置檔案的時候采用的是項目的路徑。
ClassPathXmlApplicationContext:加載配置檔案的時候根據ClassPath位置。
XmlWebApplicationContext:在Web環境下初始化監聽器的時候會加載該類。
AnnotationConfigApplicationContext:根據注解的方式啟動Spring 容器。
從上下文中擷取Bean的常用方法有:
Object getBean(String name);//通過id的名稱擷取,需要強制轉換
T getBean(Class<T> requiredType);//根據類型擷取Bean
T getBean(String name, Class<T> requiredType);//根據id的名稱擷取到指定類型的Bean
1.使用XML方式配置IOC
beans其他常用屬性:
(1)default-lazy-init:指定該beans元素下配置的所有的bean都預設延遲初始化(沒有使用到不進行執行個體化);
(2)default-autowire:指定該beans下配置的所有的bean預設是自動裝配的;
(3)default-init-method:指定該beans下配置的所有的bean預設的初始方法;
(4)default-destroy-method:指定beans元素下配置的所有的bean預設的回收方法。
注意:在beans下配置的屬性是全局屬性,在每個bean元素下也是可以單獨指定的,隻是要把default去掉即可(default-autowire對應為autowire)。bean中指定的屬性隻能在目前的bean元素上起作用,當二者出現沖突的時候會優先使用bean下指定的屬性。
bean的其他常用屬性:
(1)abstract以及parent: abstract以及parent是搭配使用的,如果bean定義了屬性abstact=true,那麼這個類就無法通過Spring容器拿到,而是作為一個模闆存在。有點類似于設計模式中的模闆方法。這個bean對應的類它定義的屬性,方法可以被複用。通過Parent屬性在子類中可以指向被abstract定義的bean。
(2)singleton(預設為“true”):定義Bean是否是Singleton(單例)。如果設為“true”,則在BeanFactory作用範圍内,隻維護此Bean的一個執行個體。如果設為“flase”,Bean将是Prototype(原型)狀态,BeanFactory将為每次Bean請求建立一個新的Bean執行個體。
(3)lazy-init(預設為“default”):用來定義這個Bean是否實作延遲初始化。如果為“true”,它将在BeanFactory啟動時初始化所有的SingletonBean。反之,如果為“false”,它隻在Bean請求時才開始建立SingletonBean。
(4)autowire(自動裝配,預設為“default”):它定義了Bean的自動裝載方式。
1、“no”:不使用自動裝配功能。
2、“byName”:通過Bean的屬性名實作自動裝配。
3、“byType”:通過Bean的類型實作自動裝配。
4、“constructor”:類似于byType,但它是用于構造函數的參數的自動組裝。
5、“autodetect”:通過Bean類的檢討機制(introspection)決定是使用constructor還是byType方式進行自動裝配。
(5)depends-on(依賴對象):這個Bean在初始化時依賴的對象,這個對象會在這個Bean初始化之前建立。
(6)init-method:用來定義Bean的初始化方法,它會在Bean組裝之後調用。它必須是一個無參數的方法。
init-method在Bean的屬性被初始化之後執行,一般做初始化資料
(7)destroy-method:用來定義Bean的銷毀方法,它在BeanFactory關閉時調用。同樣,它也必須是一個無參數的方法。它隻能應用于singletonBean。
destroy-method在Bean被銷毀之前進行執行,一般作為清理資源
Bean的生命周期如下:
(8)factory-method:定義建立該Bean對象的工廠方法。它用于下面的“factory-bean”,表示這個Bean是通過工廠方法建立。此時,“class”屬性失效。
(9)factory-bean:定義建立該Bean對象的工廠類。如果使用了“factory-bean”則“class”屬性失效。
(10)scope,用來指定該bean的作用域,其值有:
1、singleton: 在這個spring容器裡面隻有一個執行個體;
2、prototype: 每一次請求都會建立一個新的bean執行個體;
3、request: 每一次請求建立一個新的執行個體,在Web Application中有效;
4、session: 每一個session建立一個新的執行個體,在Web Application中有效;
5、global-session: 對每一個global HTTP session建立一個新的執行個體,在Web Application中有效。
作用域中比較常用的是singleton和prototype,沒有配置作用域的情況下Bean的預設作用域是singleton的。Spring容器對于singleton和prototype進行管理是不一樣的。如果Bean是singleton的時候Spring的容器會進行跟蹤Bean執行個體的狀态,負責維護Bean執行個體的生命周期的行為;如果Bean被設定為prototype作用域時,Spring容器僅僅隻是調用構造函數進行建立一個執行個體,一旦建立成功容器就不再進行跟蹤執行個體,也不會維護Bean執行個體的狀态。
單例的在系統中進行隻需要建立一次就能重複的使用,會減少系統在建立執行個體和回收執行個體時的資源開銷。
依賴注入的兩種方式:
(1).設值注入:
需要在對應的實體類中有相應的Setter方法
za
(2).構造注入:
需要在對應的實體類中有相應的構造函數
使用List、Set、Map:
<!-- List、Map、Set -->
<!-- 配置一個員工bean -->
<bean id="tempEmp" class="com.springstudy.pojo.Employee">
<property name="empId" value="1003"></property>
<property name="empName" value="王五"></property>
<property name="age" value="20"></property>
</bean>
<!-- 配置一個部門bean -->
<bean id="dept" class="com.springstudy.pojo.Department">
<!-- java.util.List -->
<property name="empList">
<list>
<ref bean="tempEmp" />
<bean class="com.springstudy.pojo.Employee">
<property name="empId" value="1004"></property>
<property name="empName" value="趙六"></property>
<property name="age" value="20"></property>
</bean>
</list>
</property>
<!-- java.util.Set -->
<property name="empSet">
<set>
<value>1</value>
<ref bean="tempEmp" />
<bean class="com.springstudy.pojo.Employee">
<property name="empId" value="1005"></property>
<property name="empName" value="趙六"></property>
<property name="age" value="20"></property>
</bean>
</set>
</property>
<!-- java.util.Map -->
<property name="empMap">
<map>
<entry key="Key1" value="1" />
<entry key="Key2" value-ref="tempEmp" />
<entry key="Key3">
<bean class="com.springstudy.pojo.Employee">
<property name="empId" value="1006"></property>
<property name="empName" value="趙六"></property>
<property name="age" value="20"></property>
</bean>
</entry>
</map>
</property>
</bean>
自動綁定:
也可以直接在某個bean中單獨設定,如:
2.基于Annotation的注解方式:
常用的注解:
(1)@Component:用來标注任何一個普通的Java類;如:
@Component
public class Employee {
...
}
(2)@Controller:用來标注一個控制類(Struts的Action或者Springwebmvc的Controller); 如:
@Controller
public class LoginServlet {
...
}
(3)@Service:用來标注一個業務邏輯的Java類;如:
@Service
public class EmployeeServiceImpl implements IEmployeeService {
...
}
(4)@Repository:标注一個DAO的Java類; 如:
@Repository
public class EmployeeDaoImpl implements IEmployeeDao {
...
}
(5)@AutoWired: 表示告訴Spring ,這裡是 一個屬性,自動注入;如:
@Service
public class EmployeeServiceImpl implements IEmployeeService {
@Autowired
private IEmployeeDao employeeDao;
...
}
補充:@Autowired(required=false):表示忽略目前要注入的bean,若有直接注入,若沒有則跳過(此時也不會報錯)。
@Autowired注入規則
@Autowired預設是按照byType進行注入的,但是當byType方式找到了多個符合的bean,又是怎麼處理的?Autowired預設先按byType,如果發現找到多個bean,則又按照byName方式比對,如果還有多個,則報出異常。
例子:
@Autowired
private Car redCar;
1. spring先找類型為Car的bean
2. 如果存在且唯一,則OK;
3. 如果不唯一,在結果集裡,尋找name為redCar的bean。因為bean的name有唯一性,是以,到這裡應該能确定是否存在滿足要求的bean了
@Autowired也可以手動指定按照byName方式注入,使用@Qualifier标簽,例如:
@Autowired()
@Qualifier("baseDao" )
因為bean的name具有唯一性,理論上是byName會快一點,但spring預設使用byType的方式注入。另外補充一點:@Resource(這個注解屬于J2EE的)的标簽,預設是按照byName方式注入的。
(6) @Resource: 依賴資源,依賴對象,不是Spring的注解;如:
@Repository("employeeDaoImpl_Test")
public class EmployeeDaoImpl implements IEmployeeDao {
...
}
@Service
public class EmployeeServiceImpl implements IEmployeeService {
//@Autowired
@Resource(name="employeeDaoImpl_Test")
private IEmployeeDao employeeDao;
...
}
(7)@Value : 屬性值 的注入,如:
@Component
public class Employee {
private int empId;
@Value("張三")
private String empName;
@Value("20")
private int age;
...
}
當上面的注解配置完成之後,可以方式一:在XML配置檔案中自動掃描和綁定,如:
方式二:基于注解的零xml配置:
(8)@Configuration: 用于指定目前類是一個spring配置類,當建立容器時會從該類上加載注解。擷取容器時需要使用,使用@Configuration注解表示這個一個配置類,等同于xml配置中xml檔案。
(9)@ComponentScan:用于指定spring在初始化容器時要掃描的包。
基于注解的零XML配置示例:
//使用@Configuration注解表示這個一個配置類,等同于xml配置中xml檔案。
@Configuration
//類似xml配置中的component-scan,我們可以配置針對特别package的掃描配置,自動将包内@Component、@Service 等注解的bean類給掃描出來,注入到容器中。用法如下:
@ComponentScan(basePackages = "com.springstudy")
public class SpringConfiguration {
}
public class SpringTest {
private static ApplicationContext ac;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
if(ac==null) {
//在純java類配置的場景中,AnnotationConfigApplicationContext承擔了同樣地建構容器執行個體的角色
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
}
}
@Test
public void test() {
IEmployeeService empService = ac.getBean("employeeServiceImpl",EmployeeServiceImpl.class);
Employee emp = empService.getEmployee();
System.out.println(emp);
}
}
其他常用的注解:
(10)@Bean:該注解隻能寫在方法上,将方法的傳回值作為一個bean,并且放入spring容器。
其屬性name:可以給目前@Bean注解方法建立的對象指定一個名稱。
@Bean(name = {"jdbcTemplate"})
public JdbcTemplate createJdbcTemplate(@Qualifier(value = "dataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
(11)@PropertySource:用于加載.properties檔案中的配置。如:
@PropertySource(value = {"classpath:jdbc.properties"})
public class JdbcConfig {
@Value(value = "${jdbc.driverClass}")
private String driverClassName;
@Value(value = "${jdbc.url}")
private String url;
@Value(value = "${jdbc.username}")
private String username;
@Value(value = "${jdbc.password}")
private String password;
@Bean(name = {"jdbcTemplate"})
public JdbcTemplate createJdbcTemplate(@Qualifier(value = "dataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Bean(name = {"dataSource"})
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
(12)@Import:用于導入其他配置類。
@Configuration
@ComponentScan(value = {"com.springstudy"})
@Import(value = JdbcConfig.class)
public class SpringConfiguration {
}