Spring 5的架構圖以下:html
各組件之間的依賴圖以下:前端
Spring5有20 個組件(1300多個文件),這些組件被分別整合在覈心容器(Core Container)、AOP(Aspect Oriented Programming)和設備支持(Instrmentation)、數據訪問及集成(Data Access/Integeration)、Web、報文發送(Messaging)、Test中。java
1、核心容器git
由 spring-beans、spring-core、spring-context 和 spring-expression(Spring Expression Language, SpEL)組成,4個組件。程序員
spring-beans 和 spring-core 模塊是 Spring 框架的核心模塊,包含了控制反轉(Inversion of github
Control, IOC)和依賴注入(Dependency Injection, DI)。web
其中,BeanFactory 接口是 Spring 框架中的核心接口,它是工廠模式的具體實現。BeanFactory 使用控制反轉對應用程序的配置和依賴性規範與實際的應用程序代碼進行了分離。但 BeanFactory 容器實例化後並不會自動實例化 Bean,只有當 Bean被使用時 BeanFactory 容器纔會對該 Bean 進行實例化與依賴關係的裝配。spring
spring-context 模塊構架於核心模塊之上,他擴展了 BeanFactory,爲她添加了 Bean 生命週期控制、框架事件體系以及資源加載透明化等功能。此外該模塊還提供了許多企業級支持,如郵件訪問、遠程訪問、任務調度等,ApplicationContext 是該模塊的核心接口,她是 BeanFactory 的超類,與BeanFactory 不一樣,ApplicationContext 容器實例化後會自動對全部的單實例 Bean 進行實例化與依賴關係的裝配,使之處於待用狀態。數據庫
spring-expression 模塊是統一表達式語言(EL)的擴展模塊,能夠查詢、管理運行中的對象, express
同時也方便的能夠調用對象方法、操做數組、集合等。它的語法相似於傳統 EL,但提供了額外的功能,最出色的要數函數調用和簡單字符串的模板函數。這種語言的特性是基於 Spring 產品的需求而設計,他能夠很是方便地同 Spring IOC 進行交互。
2、AOP 和設備支持
由 spring-aop、spring-aspects 和 spring-instrument組成,3個組件。
spring-aop 是 Spring 的另外一個核心模塊,是 AOP 主要的實現模塊。做爲繼 OOP 後,對程序員影響最大的編程思想之一,AOP 極大地開拓了人們對於編程的思路。在 Spring 中,他是以 JVM 的動態代理技術爲基礎,而後設計出了一系列的 AOP 橫切實現,好比前置通知、返回通知、異常通知等,同時, Pointcut 接口來匹配切入點,可使用現有的切入點來設計橫切面,也能夠擴展相關方法根據需求進行切入。
spring-aspects 模塊集成自 AspectJ 框架,主要是爲 Spring AOP 提供多種 AOP 實現方法。
spring-instrument 模塊是基於 JAVA SE 中的「ava.lang.instrument」進行設計的,應該算是
AOP 的一個支援模塊,主要做用是在 JVM 啓用時,生成一個代理類,程序員經過代理類在運行時修改類的字節,從而改變一個類的功能,實現 AOP 的功能。
3、數據訪問及集成
由spring-jdbc、spring-tx、spring-orm、spring-jms 和 spring-oxm組成,5個組件。
spring-jdbc 模塊是 Spring 提供的 JDBC 抽象框架的主要實現模塊,用於簡化 Spring JDBC。主要是提供 JDBC 模板方式、關係數據庫對象化方式、SimpleJdbc 方式、事務管理來簡化 JDBC編程,主要實現類是 JdbcTemplate、SimpleJdbcTemplate 以及 NamedParameterJdbcTemplate。
spring-tx模塊是 Spring JDBC 事務控制實現模塊。使用 Spring 框架,它對事務作了很好的封裝, 經過它的 AOP 配置,能夠靈活的配置在任何一層;可是在不少的需求和應用,直接使用 JDBC 事務控制仍是有其優點的。其實,事務是以業務邏輯爲基礎的;一個完整的業務應該對應業務層裏的一個方法; 若是業務操做失敗,則整個事務回滾;因此,事務控制是絕對應該放在業務層的;可是,持久層的設計則應該遵循一個很重要的原則:保證操做的原子性,即持久層裏的每一個方法都應該是不能夠分割的。因此,在使用 Spring JDBC 事務控制時,應該注意其特殊性。
spring-orm 模塊是 ORM 框架支持模塊,主要集成 Hibernate, Java Persistence API (JPA) 和Java Data Objects (JDO) 用於資源管理、數據訪問對象(DAO)的實現和事務策略。
spring-jms 模塊(Java Messaging Service)可以發送和接受信息,自 Spring Framework 4.1
之後,他還提供了對 spring-messaging 模塊的支撐。
spring-oxm 模塊主要提供一個抽象層以支撐 OXM(OXM 是 Object-to-XML-Mapping 的縮寫,它是一個 O/M-mapper,將 java 對象映射成 XML 數據,或者將 XML 數據映射成 java 對象),例如:JAXB,Castor, XMLBeans, JiBX 和 XStream 等。
4、Web
由 spring-web、spring-webmvc、spring-websocket 和 spring-webflux 組成,4個組件。
spring-web 模塊爲 Spring 提供了最基礎 Web 支持,主要創建於核心容器之上,經過 Servlet 或 者 Listeners 來初始化 IOC 容器,也包含一些與 Web 相關的支持。
spring-webmvc模塊是一個的Web-Servlet模塊,實現了Spring MVC (model-view-Controller)的 Web 應用。
spring-websocket 模塊主要是與 Web 前端的全雙工通信的協議。
spring-webflux 是一個新的非堵塞函數式 Reactive Web 框架,能夠用來創建異步的,非阻塞,事件驅動的服務,而且擴展性很是好。
5、報文發送
包括spring-messaging ,1個組件。
spring-messaging是從 Spring4 開始新加入的一個模塊,主要職責是爲 Spring 框架集成一些基礎的報文傳送應用。
6、Test
包含spring-test,1個組件。
spring-test 模塊主要爲測試提供支持的,畢竟在不須要發佈(程序)到你的應用服務器或者鏈接到其餘企業設施的狀況下可以執行一些集成測試或者其餘測試對於任何企業都是很是重要的。
Bean配置信息定義了Bean的實現及依賴關係,Spring容器根據各類形式的Bean配置信息在容器內部創建Bean定義註冊表,而後根據註冊表加載、實例化Bean,並創建Bean和Bean的依賴關係,最後將這些準備就緒的Bean放到Bean緩存池中,以供外層的應用程序進行調用。下圖是一張老圖:
1、bean配置
bean配置有三種方法:
基於xml配置Bean
使用註解定義Bean
基於java類提供Bean定義信息
1.1 基於xml配置Bean
對於基於XML的配置,Spring 2.0之後使用Schema的格式,使得不一樣類型的配置擁有了本身的命名空間,是配置文件更具擴展性。
①默認命名空間:它沒有空間名,用於Spring Bean的定義;
②xsi命名空間:這個命名空間用於爲每一個文檔中命名空間指定相應的Schema樣式文件,是標準組織定義的標準命名空間;
③aop命名空間:這個命名空間是Spring配置AOP的命名空間,是用戶自定義的命名空間。
命名空間的定義分爲兩個步驟:第一步指定命名空間的名稱;第二步指定命名空間的Schema文檔樣式文件的位置,用空格或回車換行進行分分隔。
1.1.1 Bean基本配置
在Spring容器的配置文件中定義一個簡要Bean的配置片斷以下所示:
通常狀況下,Spring IOC容器中的一個Bean即對應配置文件中的一個<bean>,這種鏡像對應關係應該容易理解。其中id爲這個Bean的名稱,經過容器的getBean("foo")便可獲取對應的Bean,在容器中起到定位查找的做用,是外部程序和Spring IOC容器進行交互的橋樑。class屬性指定了Bean對應的實現類。
下面是基於XML的配置文件定義了兩個簡單的Bean:
<?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-3.0.xsd">
<bean id="car" name="#car1" class="com.baobaotao.simple.Car"></bean>
<bean id="boss" class="com.baobaotao.simple.Boss"></bean>
</beans>
1.1.2 依賴注入
屬性注入
構造函數注入
工廠方式注入
1.2 使用註解定義Bean
咱們知道,Spring容器成功啓動的三大要件分別是:Bean定義信息、Bean實現類以及Spring自己。若是採用基於XML的配置,Bean定義信息和Bean實現類自己是分離的,而採用基於註解的配置方式時,Bean定義信息即經過在Bean實現類上標註註解實現。
下面是使用註解定義一個DAO的Bean:
package com.baobaotao.anno;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
//①經過Repository定義一個DAO的Bean
@Component("userDao")
public class UserDao {
}
在①處,咱們使用@Component註解在UserDao類聲明處對類進行標註,它能夠被Spring容器識別,Spring容器自動將POJO轉換爲容器管理的Bean。
它和如下的XML配置是等效的:
<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
除了@Component之外,Spring提供了3個功能基本和@Component等效的註解,它們分別用於對DAO、Service及Web層的Controller進行註解,因此也稱這些註解爲Bean的衍型註解:(相似於xml文件中定義Bean<bean id=" " class=" "/>
@Repository:用於對DAO實現類進行標註;
@Service:用於對Service實現類進行標註;
@Controller:用於對Controller實現類進行標註;
之因此要在@Component以外提供這三個特殊的註解,是爲了讓註解類自己的用途清晰化,此外Spring將賦予它們一些特殊的功能。
1.2.1 使用註解配置信息啓動spring容器
Spring提供了一個context的命名空間,它提供了經過掃描類包以應用註解定義Bean的方式:
<?xml version="1.0" encoding="UTF-8" ?>
<!--①聲明context的命名空間-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
<!--②掃描類包以應用註解定義的Bean-->
<context:component-scan base-package="com.baobaotao.anno"/>
<bean class="com.baobaotao.anno.LogonService"></bean>
<!-- context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ -->
<!-- context:component-scan base-package="com.baobaotao">
<context:include-filter type="regex" expression="com\.baobaotao\.anno.*Dao"/>
<context:include-filter type="regex" expression="com\.baobaotao\.anno.*Service"/>
<context:exclude-filter type="aspectj" expression="com.baobaotao..*Controller+"/>
</context:component-scan -->
</beans>
在①處聲明context命名空間,在②處便可經過context命名空間的component-scan的base-package屬性指定一個須要掃描的基類包,Spring容器將會掃描這個基類包裏的全部類,並從類的註解信息中獲取Bean的定義信息。
若是僅但願掃描特定的類而非基包下的全部類,大家可使用resource-pattern屬性過濾特定的類,以下所示:
< context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class"/ >
這裏咱們將基類包設置爲com.baobaotao,默認狀況下resource-pattern屬性的值爲"**/*.class",即基類包裏的全部類。這裏咱們設置爲"anno/*.class",則Spring僅會掃描基包裏anno子包中的類。
1.3 基於java類提供Bean定義
在普通的POJO類中只要標註@Configuration註解,就能夠爲spring容器提供Bean定義的信息了,每一個標註了@Bean的類方法都至關於提供了一個Bean的定義信息。
package com.baobaotao.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//①將一個POJO標註爲定義Bean的配置類
@Configuration
public class AppConf {
//②如下兩個方法定義了兩個Bean,以提供了Bean的實例化邏輯
@Bean
public UserDao userDao(){
return new UserDao();
}
@Bean
public LogDao logDao(){
return new LogDao();
}
//③定義了logonService的Bean
@Bean
public LogonService logonService(){
LogonService logonService = new LogonService();
//④將②和③處定義的Bean注入到LogonService Bean中
logonService.setLogDao(logDao());
logonService.setUserDao(userDao());
return logonService;
}
}
①處在APPConf類的定義處標註了@Configuration註解,說明這個類可用於爲Spring提供Bean的定義信息。類的方法處能夠標註@Bean註解,Bean的類型由方法返回值類型決定,名稱默認和方法名相同,也能夠經過入參顯示指定Bean名稱,如@Bean(name="userDao").直接在@Bean所標註的方法中提供Bean的實例化邏輯。
在②處userDao()和logDao()方法定義了一個UserDao和一個LogDao的Bean,它們的Bean名稱分別是userDao和logDao。在③處,又定義了一個logonService Bean,而且在④處注入②處所定義的兩個Bean。
所以,以上的配置和如下XML配置時等效的:
<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="logService" class="com.baobaotao.conf.LogonService"
p:logDao-ref="logDao" p:userDao-ref="userDao"/>
基於java類的配置方式和基於XML或基於註解的配置方式相比,前者經過代碼的方式更加靈活地實現了Bean的實例化及Bean之間的裝配,但後面二者都是經過配置聲明的方式,在靈活性上要稍遜一些,可是配置上要更簡單一些。
2 Bean注入
Bean注入的方式有兩種,一種是在XML中配置,此時分別有屬性注入、構造函數注入和工廠方法注入;另外一種則是使用註解的方式注入 @Autowired,@Resource,@Required。
2.1 在xml文件中配置依賴注入
2.1.1 屬性注入
屬性注入即經過setXxx()方法注入Bean的屬性值或依賴對象,因爲屬性注入方式具備可選擇性和靈活性高的優勢,所以屬性注入是實際應用中最常採用的注入方式。
屬性注入要求Bean提供一個默認的構造函數,併爲須要注入的屬性提供對應的Setter方法。Spring先調用Bean的默認構造函數實例化Bean對象,而後經過反射的方式調用Setter方法注入屬性值。
package com.baobaotao.anno;
import org.springframework.beans.factory.BeanNameAware;
public class LogonService implements BeanNameAware{
private LogDao logDao;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setLogDao(LogDao logDao) {
this.logDao = logDao;
}
public LogDao getLogDao() {
return logDao;
}
public UserDao getUserDao() {
return userDao;
}
public void setBeanName(String beanName) {
System.out.println("beanName:"+beanName);
}
public void initMethod1(){
System.out.println("initMethod1");
}
public void initMethod2(){
System.out.println("initMethod2");
}
}
bean.xml配置:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName"
>
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean class="com.baobaotao.anno.LogonService">
<property name="logDao" ref="logDao"></property>
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
2.1.2 構造方法注入
使用構造函數注入的前提是Bean必須提供帶參數的構造函數。例如:
package com.baobaotao.anno;
import org.springframework.beans.factory.BeanNameAware;
public class LogonService implements BeanNameAware{
public LogonService(){}
public LogonService(LogDao logDao, UserDao userDao) {
this.logDao = logDao;
this.userDao = userDao;
}
private LogDao logDao;
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setLogDao(LogDao logDao) {
this.logDao = logDao;
}
public LogDao getLogDao() {
return logDao;
}
public UserDao getUserDao() {
return userDao;
}
public void setBeanName(String beanName) {
System.out.println("beanName:"+beanName);
}
public void initMethod1(){
System.out.println("initMethod1");
}
public void initMethod2(){
System.out.println("initMethod2");
}
}
bean.xml配置:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
<bean id="logDao" class="com.baobaotao.anno.LogDao"/>
<bean id="userDao" class="com.baobaotao.anno.UserDao"/>
<bean class="com.baobaotao.anno.LogonService">
<constructor-arg ref="logDao"></constructor-arg>
<constructor-arg ref="userDao"></constructor-arg>
</bean>
</beans>
2.1.3 工廠方法注入
非靜態工廠方法:
有些工廠方法是非靜態的,即必須實例化工廠類後才能調用工廠方法。
package com.baobaotao.ditype;
public class CarFactory {
public Car createHongQiCar(){
Car car = new Car();
car.setBrand("紅旗CA72");
return car;
}
public static Car createCar(){
Car car = new Car();
return car;
}
}
工廠類負責建立一個或多個目標類實例,工廠類方法通常以接口或抽象類變量的形式返回目標類實例,工廠類對外屏蔽了目標類的實例化步驟,調用者甚至不用知道具體的目標類是什麼。
<?xml version="1.0" encoding="UTF-8" ?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 工廠方法-->
<bean id="carFactory" class="com.baobaotao.ditype.CarFactory" />
<bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
</bean>
</beans>
靜態工廠方法:
不少工廠類都是靜態的,這意味着用戶在無須建立工廠類實例的狀況下就能夠調用工廠類方法,所以,靜態工廠方法比非靜態工廠方法的調用更加方便。
<?xml version="1.0" encoding="UTF-8" ?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="car6" class="com.baobaotao.ditype.CarFactory"
factory-method="createCar"></bean>
</beans>
2.2 使用註解的方式注入
2.2.1 使用@Autowired進行自動注入
Spring經過@Autowired註解實現Bean的依賴注入,下面是一個例子:
package com.baobaotao.anno;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
//① 定義一個Service的Bean(不須要在XML中定義Bean)
@Service
public class LogonService implements BeanNameAware{
//② 分別注入LogDao及UserDao的Bean(不須要在XML中定義property屬性注入)
@Autowired(required=false)
private LogDao logDao;
@Autowired
@Qualifier("userDao")
private UserDao userDao;
public LogDao getLogDao() {
return logDao;
}
public UserDao getUserDao() {
return userDao;
}
public void setBeanName(String beanName) {
System.out.println("beanName:"+beanName);
}
public void initMethod1(){
System.out.println("initMethod1");
}
public void initMethod2(){
System.out.println("initMethod2");
}
}
在①處,咱們使用@Service將LogonService標註爲一個Bean,在②處,經過@Autowired注入LogDao及UserDao的Bean。@Autowired默認按類型匹配的方式,在容器查找匹配的Bean,當有且僅有一個匹配的Bean時,Spring將其注入到@Autowired標註的變量中。
2.2.2 使用@Autowired的required屬性
若是容器中沒有一個和標註變量類型匹配的Bean,Spring容器啓動時將報NoSuchBeanDefinitionException的異常。若是但願Spring即便找不到匹配的Bean完成注入也不用拋出異常,那麼可使用@Autowired(required=false)進行標註:
@Service
public class LogonService implements BeanNameAware{
@Autowired(required=false)
private LogDao logDao;
...
}
默認狀況下,@Autowired的required屬性的值爲true,即要求必定要找到匹配的Bean,不然將報異常。
2.2.3 使用@Qualifier指定注入Bean的名稱
若是容器中有一個以上匹配的Bean時,則能夠經過@Qualifier註解限定Bean的名稱,以下所示:
@Service
public class LogonService implements BeanNameAware{
@Autowired(required=false)
private LogDao logDao;
//①注入名爲UserDao,類型爲UserDao的Bean
@Autowired
@Qualifier("userDao")
private UserDao userDao;
}
這裏假設容器有兩個類型爲UserDao的Bean,一個名爲userDao,另外一個名爲otherUserDao,則①處會注入名爲userDao的Bean。
2.2.4 對類方法進行標註
@Autowired能夠對類成員變量及方法的入參進行標註,下面咱們在類的方法上使用@Autowired註解:
package com.baobaotao.anno;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class LogonService implements BeanNameAware{
private LogDao logDao;
private UserDao userDao;
@Autowired
public void setLogDao(LogDao logDao) {
this.logDao = logDao;
}
@Autowired
@Qualifier("userDao")
public void setUserDao(UserDao userDao) {
System.out.println("auto inject");
this.userDao = userDao;
}
}
若是一個方法擁有多個入參,在默認狀況下,Spring自動選擇匹配入參類型的Bean進行注入。Spring容許對方法入參標註@Qualifier以指定注入Bean的名稱,以下所示:
@Autowired
public void init(@Qualifier("userDao")UserDao userDao,LogDao logDao){
System.out.println("multi param inject");
this.userDao = userDao;
this.logDao =logDao;
}
在以上例子中,UserDao的入參注入名爲userDao的Bean,而LogDao的入參注入LogDao類型的Bean。
通常狀況下,在Spring容器中大部分的Bean都是單實例的,因此咱們通常都無須經過@Repository、@Service等註解的value屬性爲Bean指定名稱,也無須使用@Qualifier按名稱進行注入。
2.2.5 對標準註解的支持
此外,Spring還支持@Resource和@Inject註解,這兩個標準註解和@Autowired註解的功能相似,都是對類變量及方法入參提供自動注入的功能。@Resource要求提供一個Bean名稱的屬性,若是屬性爲空,則自動採用標註處的變量名或方法名做爲Bean的名稱。
package com.baobaotao.anno;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class Boss {
private Car car;
public Boss(){
System.out.println("construct...");
}
// @Autowired
// private void setCar(Car car){
// System.out.println("execute in setCar");
// this.car = car;
// }
@Resource("car")
private void setCar(Car car){
System.out.println("execute in setCar");
this.car = car;
}
@PostConstruct
private void init1(){
System.out.println("execute in init1");
}
@PostConstruct
private void init2(){
System.out.println("execute in init1");
}
@PreDestroy
private void destory1(){
System.out.println("execute in destory1");
}
@PreDestroy
private void destory2(){
System.out.println("execute in destory2");
}
}
這時,若是@Resource未指定"car"屬性,則也能夠根據屬性方法獲得須要注入的Bean名稱。可見@Autowired默認按類型匹配注入Bean,@Resource則按名稱匹配注入Bean。而@Inject和@Autowired同樣也是按類型匹配注入的Bean的,只不過它沒有required屬性。可見無論是@Resource仍是@Inject註解,其功能都沒有@Autowired豐富,所以除非必須,大可沒必要在意這兩個註解。(相似於Xml中使用<constructor-arg ref="logDao"></constructor-arg>或者<property name="logDao" ref="logDao"></property>進行注入,若是使用了@Autowired或者Resource等,這不須要在定義Bean時使用屬性注入和構造方法注入了)
2.2.6 關於Autowired和@Resource
1.@Autowired注入是按照類型注入的,只要配置文件中的bean類型和須要的bean類型是一致的,這時候注入就沒問題。可是若是相同類型的bean不止一個,此時注入就會出現問題,Spring容器沒法啓動。
2.@Resourced標籤是按照bean的名字來進行注入的,若是咱們沒有在使用@Resource時指定bean的名字,同時Spring容器中又沒有該名字的bean,這時候@Resource就會退化爲@Autowired即按照類型注入,這樣就有可能違背了使用@Resource的初衷。因此建議在使用@Resource時都顯示指定一下bean的名字@Resource(name="xxx")
2.2.7 讓@Resource和@Autowired生效的幾種方式
1.在xml配置文件中顯式指定
<!-- 爲了使用Autowired標籤,咱們必須在這裏配置一個bean的後置處理器 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<!-- 爲了使用@Resource標籤,這裏必須配置一個後置處理器 -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
2.在xml配置文件中使用context:annotation-config
<context:annotation-config />
3.在xml配置文件中使用context:component-scan
<context:component-scan base-package="com.baobaotao.anno"/>
4.重寫Spring容器的Context,在自定義BeanFactory時調用AnnotationConfigUtils.registerAnnotationConfigProcessors()把這兩個註解處理器增長到容器中。
編寫本身的XmlWebApplicationContext,在這個context中重寫customizeBeanFactory(),在這個方法中調用了AnnotationConfigUtils.registerAnnotationConfigProcessors()方法把這兩自動註解處理器加入到BeanDefinitions中,這樣公在web層就支持@Resource和@Autowired進行自動注入。以下:
package com.alibaba.citrus.springext.support.context;
import com.alibaba.citrus.springext.ResourceLoadingExtendable;
import com.alibaba.citrus.springext.ResourceLoadingExtender;
import com.alibaba.citrus.springext.support.context.InheritableListableBeanFactory;
import com.alibaba.citrus.springext.support.resolver.XmlBeanDefinitionReaderProcessor;
import java.io.IOException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
public class XmlWebApplicationContext extends org.springframework.web.context.support.XmlWebApplicationContext implements ResourceLoadingExtendable {
private ResourceLoadingExtender resourceLoadingExtender;
private boolean parentResolvableDependenciesAccessible = true;
public XmlWebApplicationContext() {
}
public boolean isParentResolvableDependenciesAccessible() {
return this.parentResolvableDependenciesAccessible;
}
public void setParentResolvableDependenciesAccessible(boolean parentResolvableDependenciesAccessible) {
this.parentResolvableDependenciesAccessible = parentResolvableDependenciesAccessible;
}
public void setResourceLoadingExtender(ResourceLoadingExtender resourceLoadingExtender) {
if(this.resourceLoadingExtender != null) {
this.getApplicationListeners().remove(this.resourceLoadingExtender);
}
this.resourceLoadingExtender = resourceLoadingExtender;
if(resourceLoadingExtender instanceof ApplicationListener) {
this.addApplicationListener((ApplicationListener)resourceLoadingExtender);
}
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
(new XmlBeanDefinitionReaderProcessor(beanDefinitionReader)).addConfigurationPointsSupport();
}
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.customizeBeanFactory(beanFactory);
//AnnotationConfigUtils.registerAnnotationConfigProcessors()方法把這兩自動註解處理器加入到BeanDefinitions中,在web層就支持@Resource和@Autowired進行自動注入
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory, (Object)null);
}
protected DefaultListableBeanFactory createBeanFactory() {
return (DefaultListableBeanFactory)(this.isParentResolvableDependenciesAccessible()?new InheritableListableBeanFactory(this.getInternalParentBeanFactory()):super.createBeanFactory());
}
protected Resource getResourceByPath(String path) {
Resource resource = null;
if(this.resourceLoadingExtender != null) {
resource = this.resourceLoadingExtender.getResourceByPath(path);
}
if(resource == null) {
resource = super.getResourceByPath(path);
}
return resource;
}
protected ResourcePatternResolver getResourcePatternResolver() {
final ResourcePatternResolver defaultResolver = super.getResourcePatternResolver();
return new ResourcePatternResolver() {
public Resource[] getResources(String locationPattern) throws IOException {
ResourcePatternResolver resolver = null;
if(XmlWebApplicationContext.this.resourceLoadingExtender != null) {
resolver = XmlWebApplicationContext.this.resourceLoadingExtender.getResourcePatternResolver();
}
if(resolver == null) {
resolver = defaultResolver;
}
return resolver.getResources(locationPattern);
}
public ClassLoader getClassLoader() {
return defaultResolver.getClassLoader();
}
public Resource getResource(String location) {
return defaultResolver.getResource(location);
}
};
}
}
1、克隆
git clone https://github.com/spring-projects/spring-framework.git
2、使用maven
(1)Core
支持dependency injection, events, resources, i18n, validation, data binding, type conversion, SpEL, AOP,其依賴爲:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
(2)WebMVC
支持MVC, View Technologies, CORS, Web Socket, RESTful,同時繼承Spring Web的功能core HTTP integration,包括Servlet filters, Spring HTTP Invoker, infrastructure to integrate with
other web frameworks and HTTP technologies e.g. Hessian, Burlap。其依賴爲:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
注意:依賴了spring-webmvc就不須要依賴spring-web
(3)spring-boot-starter-web
注意:啓動器spring-boot-starter-web基於Spring MVC構建RESTful風格的web應用,使用內嵌tomcat做爲默認容器
3、使用Gradle
(1)spring-core和spring-webmvc
dependencies {
api 'org.springframework:spring-core:5.0.8.RELEASE'
api 'org.springframework:spring-webmvc:5.0.8.RELEASE'
}
或者
dependencies {
compile group: 'org.springframework', name: 'spring-core', version: '5.0.8.RELEASE'
compile group: 'org.springframework', name: 'spring-webmvc', version: '5.0.8.RELEASE'
}
(2)spring-boot-starter-web
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
}
注意:啓動器spring-boot-starter-web基於Spring MVC構建RESTful風格的web應用,使用內嵌tomcat做爲默認容器
能夠在「Spring Boot」中瞭解註解和啓動器清單。
(1)依賴Spring Web
***Maven***
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
***Gradle***
dependencies {
compile("org.springframework.boot:spring-boot-starter-web-services")
}
(2)定義web service domain
使用XSD(XML schema file)定義Domain。
例如:在resources目錄下建立countries.xsd文件來定義國家的名稱、人口、首都和貨幣。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://jiaxiaomei.com/test/soap-test"
targetNamespace="http://jiaxiaomei.com/test/soap-test" elementFormDefault="qualified">
<xs:element name="getCountryRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="getCountryResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="country" type="tns:country"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="country">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="population" type="xs:int"/>
<xs:element name="capital" type="xs:string"/>
<xs:element name="currency" type="tns:currency"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="currency">
<xs:restriction base="xs:string">
<xs:enumeration value="GBP"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="PLN"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
(3)產生Domain 類
由maven或者gradle基於xsd文件自動建立Domain類。
***Maven*** 修改pom.xml
首先新增依賴:
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
而後增長構建插件:
<!-- tag::JAXB2, xsd[] -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
<!-- end::JAXB2, xsd[] -->
注意plugin標籤放在plugins標籤裏面。
保存後,eclipse會自動執行xjc,在java目錄下自動生成六個java文件:
***Gradle*** 須要在build.gradle中配置JAXB:
A、在bootJar裏面增長:
from genJaxb.classesDir
B、新增configurations:
configurations {
jaxb
}
C、在dependencies裏面增長對Spring WS和JAXB的依賴:
compile("wsdl4j:wsdl4j:1.6.1")
jaxb("org.glassfish.jaxb:jaxb-xjc:2.2.11")
compile(files(genJaxb.classesDir).builtBy(genJaxb))
D、增長genJaxb任務(Ant任務,由於gradle還不支持JAXB任務):
task genJaxb {
ext.sourcesDir = "${buildDir}/generated-sources/jaxb"
ext.classesDir = "${buildDir}/classes/jaxb"
ext.schema = "src/main/resources/countries.xsd"
outputs.dir classesDir
doLast() {
project.ant {
taskdef name: "xjc", classname: "com.sun.tools.xjc.XJCTask",
classpath: configurations.jaxb.asPath
mkdir(dir: sourcesDir)
mkdir(dir: classesDir)
xjc(destdir: sourcesDir, schema: schema) {
arg(value: "-wsdl")
produces(dir: sourcesDir, includes: "**/*.java")
}
javac(destdir: classesDir, source: 1.6, target: 1.6, debug: true,
debugLevel: "lines,vars,source",
classpath: configurations.jaxb.asPath) {
src(path: sourcesDir)
include(name: "**/*.java")
include(name: "*.java")
}
copy(todir: classesDir) {
fileset(dir: sourcesDir, erroronmissingdir: false) {
exclude(name: "**/*.java")
}
}
}
}
}
E、增長afterEclipseImport任務
task afterEclipseImport {
dependsOn "genJaxb"
}
保存後自動生成java類,注意gradle採用了ant任務,比maven複雜不少。
關於jaxb和ant任務的更多知識,參見「9、JAXB和ANT」。
(4)建立Repository類
該類須要註解@Component,給web service提供數據。
例如:
A、建立CountryRepository類,並增長註解@Component
B、增長一個靜態成員:
private static final Map<String, Country> countries = new HashMap<>();
C、增長初始化數據的方法initData,返回類型爲void,代碼爲:
Country spain = new Country();
spain.setName("Spain");
spain.setCapital("Madrid");
spain.setCurrency(Currency.EUR);
spain.setPopulation(46704314);
countries.put(spain.getName(), spain);
Country poland = new Country();
poland.setName("Poland");
poland.setCapital("Warsaw");
poland.setCurrency(Currency.PLN);
poland.setPopulation(38186860);
countries.put(poland.getName(), poland);
Country uk = new Country();
uk.setName("United Kingdom");
uk.setCapital("London");
uk.setCurrency(Currency.GBP);
uk.setPopulation(63705000);
countries.put(uk.getName(), uk);
D、增長查找方法findCountry,參數爲name,返回Country
Assert.notNull(name, "The country's name must not be null");
return countries.get(name);
(5)建立service endpoint來處理SOAP請求
A、建立endpoint類,加註解@Endpoint,增長構造函數並註解@Autowired:
按照country例子:
@Autowired
public CountryEndpoint(CountryRepository countryRepository) {
this.countryRepository = countryRepository;
}
其中,@Endpoint註冊一個Spring WS類做爲處理SOAP消息的候選者。
B、增長靜態成員NAMESPACE_URI
例如:
private static final String NAMESPACE_URI = "http://jiaxiaomei.com/test/soap-test";
C、增長Repository的私有成員
例如:
private CountryRepository countryRepository;
D、增長查詢方法,註解@PayloadRoot、@RequestPayload和@ResponsePayload
例如:
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload
public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) {
GetCountryResponse response = new GetCountryResponse();
response.setCountry(countryRepository.findCountry(request.getName()));
return response;
}
其中,@PayloadRoot由Spring WS提供,選擇處理方法,屬性包括namespace和localpart。
@RequestPayload由Spring WS提供,表示SOAP消息將會綁定到request參數。
@ResponsePayload由Spring WS提供,映射返回值到響應負載中。
(6)配置web service bean
A、從WsConfigurerAdapter繼承一個webservice配置類,並註解@EnableWs、@Configuration
例如:
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
B、自定義DispatcherServlet bean
Spring WS使用不一樣的servlet類型來處理SOAP消息,該類型是MessageDispatcherServlet。
須要注入ApplicationContext到方法中,這樣才能讓Spring WS自動偵測到Spring bean。
從新命名MessageDispatcherServlet注入方法,就不替換Spring Boot的默認DispatcherServlet bean,默認bean經過連接點」/」提供服務,自定義的MessageDispatcherServlet經過ServletRegistrationBean從新設定連接點。
前面的@Endpoint註解已經提示DefaultMethodEndpointAdapter設置註解驅動Spring WS編程模型。這裏就採用這種註解驅動模型。
例如:
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/ws/*");
}
C、基於DefaultWsdl11Definition構建WSDL定義
DefaultWsdl11Definition使用XsdSchema接口展現WSDL1.1標準。
例如:
@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://jiaxiaomei.com/test/soap-test");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
其中@Bean的屬性name實際上指定了wsdl的文件名,因此,wsdl文件能夠得到於這個連接:
http://<host>:<port>/ws/countries.wsdl
(7)構建Application類,註解@SpringBootApplication
Build後啓動這個服務,建立一個請求xml,編寫進入SOAP請求。
例如:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:gs="http://jiaxiaomei.com/test/soap-test">
<soapenv:Header/>
<soapenv:Body>
<gs:getCountryRequest>
<gs:name>Spain</gs:name>
</gs:getCountryRequest>
</soapenv:Body>
</soapenv:Envelope>
下載curl,地址:https://curl.haxx.se/,設置bin目錄到環境變量path。而後,在命令行中使用curl發送soap請求,以下:
下載libxml2,包含三個文件iconv-1.14-win32-x86_64.7z、zlib-1.2.8-win32-x86_64.7z和libxml2-2.9.3-win32-x86_64.7z,下載地址:https://www.zlatkovic.com/pub/libxml/64bit/;解壓後把三個bin都放到path環境變量中。而後在命令行中執行:
curl --header "content-type: text/xml" -d @request.xml http://localhost:8080/ws |xmllint --format
這讓soap response文件更規整。
參考:https://spring.io/guides/gs/rest-service/作了一個實際例子。
(1)新建一個gradle project,修改build.gradle
A、修改依賴爲:
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
//api 'org.springframework.boot:spring-boot-starter-web:2.1.0.RELEASE'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'org.springframework.boot:spring-boot-starter-web:2.1.0.RELEASE'
// spring-boot-starter-test based on JUnit test framework
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.1.0.RELEASE'
}
B、修改構建腳本
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE")
}
}
C、引用插件
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
D、增長jar包輸出信息
bootJar {
baseName = 'restfultest'
version = '0.1.0'
}
E、增長兼容性信息
sourceCompatibility = 1.8
targetCompatibility = 1.8
保存後刷新項目(項目右鍵菜單àgradleàrefresh gradle project),提示:
修改項目gradle設置:
(2)新建一個model
(3)新建一個controller
@RestController在Spring4裏面引入,參見「5、Spring Boot」的「Annotation」,用於標註類爲控制器,該控制器每一個方法都返回一個model,而不是view。
@RequestMapping註解映射HTTP請求http://host:port/msg到所標註的方法上。
@RequestParam註解綁定HTTP請求參數到所標註的方法參數上(http://host:port/msg?name=)。
注意:這是一個RESTful web service controller,而不是傳統的MVC controller。這個controller僅僅返回一個model對象,這個對象數據直接使用JSON寫到HTTP response,MappingJackson2HttpMessageConverter自動被執行來支持Jackson 2。傳統MVC controller使用view技術在server-side把數據轉換爲html。
(4)新建一個Application
(5)執行
使用Boot Dashboard的左上角按鈕啓動服務。
在Chrome中驗證:
參考:https://spring.io/guides/gs/consuming-web-service/
(1)新建一個maven項目
修改pom.xml
(2)編寫任務,自動從WSDL產生類。
(3)建立一個web service客戶端
從WebServiceGatewaySupport類繼承而來:
其中getCountry方法完成SOAP數據交換任務。
GetCountryRequest和GetCountryResponse都是jaxb自動產生的。
WebServiceGatewaySupport的方法getWebServiceTemplate得到WebServiceTemplate,而後使用marshalSendAndReceive方法來實際完成wsdl文件接收和解析。
SoapActionCallback用來獲取SOAPAction header,WSDL解析時須要這個頭,其中包含<soap:operation/> 元素。
(4)配置web service
Spring WS經過Spring Framework的OXM模塊中的Jaxb2Marshaller來存取XML請求。
marshaller bean指向domain對象集合,在XML和POJOs之間轉換。
countryClient bean用來配置web service的URI,並綁定marshaller。
(5)配置應用入口類
參考:https://spring.io/guides/gs/consuming-rest/
(1)新建一個gradle project,修改build.gradle,以下
(2)建立一個domain類,以下:
(3)建立一個Application類,以下:
其中服務地址引用「RESTful Web Service生產服務」,執行後輸出:
(4)改造Application類
其中@Bean把restTemplate註解到CommandLineRunner回調函數中。
因爲和「RESTful Web Service生產服務」使用的8080端口衝突,因此須要增長Application.yml,以下:
啓動後,以下: