長(zhǎng)沙建站挺找有為太極百度seo軟件優(yōu)化
啥?Spring Boot 不用?——對(duì)。就只是使用 Spring MVC + Embedded Tomcat,而不用 Boot。為啥?——因?yàn)?Boot 太重了:)
那是反智嗎?Spring Boot 好好的就只是因?yàn)樘鼐筒挥?#xff1f;——稍安勿躁,這里并非說(shuō)重新寫(xiě)代替 Spring 的輪子,而是繼續(xù)使用原裝的 Spring MVC,進(jìn)而對(duì)其加強(qiáng)升級(jí),——請(qǐng)聽(tīng)我跟你說(shuō), 優(yōu)化后的 Spring MVC 幾乎能做到 Spring Boot 的事情,是一個(gè)近乎 99% 完成度的平替,而且它更輕量級(jí),何樂(lè)不為呢?Yes,讓我們?cè)囋?#xff1a;Spring Framework without Spring Boot!
為了說(shuō)明如何打造輕量級(jí)的 Spring Boot,本文分為“嵌入式 Tomcat”、“增強(qiáng) Spring MVC”和“打包/部署”三個(gè)小節(jié)來(lái)介紹。
嵌入式 Tomcat
目的是通過(guò)執(zhí)行main()
函數(shù)即可啟動(dòng) Web 程序。在上一篇文章《嵌入式 Tomcat 調(diào)?!分幸呀?jīng)討論了如何制定化 Tomcat,但仍未與 Spring 結(jié)合。
實(shí)際上,從 Spring MVC 時(shí)代起就支持通過(guò) Java 注解來(lái)配置,代替古老的 XML 方式。筆者在兩年之前的文章《Spring MVC 用起來(lái)還是很香的》已經(jīng)介紹過(guò)。那時(shí)還未擺脫標(biāo)準(zhǔn) Tomcat 的運(yùn)行模式,而目前要做的,就是結(jié)合嵌入式 Tomcat 與 Spring MVC 兩者。
因?yàn)槭羌兪謩?dòng)編碼(Programmatically)達(dá)成的,所以要了解 Tomcat 加載的生命周期。當(dāng)為LifecycleState.STARTING_PREP
之時(shí),才能有關(guān)鍵的ServletContext ctx
對(duì)象,以便 Spring 綁定。
完整代碼在這里。
調(diào)用例子
一般情況下,要指定的只有 Tomcat 端口和 Context 目錄,甚至 Context 目錄都可以不傳。所以多數(shù)情況下你調(diào)用 EmbeddedTomcatStarter 的靜態(tài)方法即可。
另外start()
有 class… 的參數(shù)列表,它是個(gè)可變長(zhǎng)度的數(shù)組,表示 Java 配置類,如下例的DemoApp.class
、DemoConfig.class
,第一個(gè) class 是 main 函數(shù)的那個(gè)類,第二個(gè)、第三……第 n 個(gè)是帶有@Configuration
注解的配置類。
import com.ajaxjs.data.sql_controller.ServiceBeanDefinitionRegistry;
import com.ajaxjs.framework.spring.BaseWebMvcConfigure;
import com.ajaxjs.framework.spring.EmbeddedTomcatStarter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;@Configuration
@EnableWebMvc
@ComponentScan("com.ajaxjs.demo")
public class DemoApp extends BaseWebMvcConfigure {public static void main(String[] args) {EmbeddedTomcatStarter.start(8300, DemoApp.class, DemoConfig.class);}
}
配置類是這樣的,與 Spring Boot 的無(wú)異,還是熟悉的配方。
增強(qiáng) SpringMVC
YAML 配置
主流采用 YAML 作為配置文件,properties/xml 文件則不考慮了。在 Spring MVC 中支持 YAML 配置文件,首先引入 yaml 依賴。
<!-- YAML 配置文件 -->
<dependency><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId><version>1.33</version>
</dependency>
然后初始化加載 YAML。這是封裝到框架里面的,位于BaseWebMvcConfigure
。
YAML 有個(gè)問(wèn)題,就是沒(méi)有直接提供靜態(tài)方法的手段,于是重寫(xiě)PropertySourcesPlaceholderConfigurer.postProcessBeanFactory()
方法,獲取內(nèi)部的 Key/Value 結(jié)構(gòu)Properties localProperties
,暴露出來(lái)給外界獲取,傳入 key 即可得到的配置 value。源碼如下:
package com.ajaxjs.framework.spring;import com.ajaxjs.util.convert.ConvertBasicValue;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;import java.io.IOException;
import java.util.Properties;/*** PropertySourcesPlaceholderConfigurer 是一個(gè)由 Spring 提供的用于解析屬性占位符的配置類,* 它沒(méi)有提供直接獲取私有屬性 localProperties 的公開(kāi)方法。但是,可以通過(guò)以下步驟獲取 localProperties 的值*/
public class CustomPropertySources extends PropertySourcesPlaceholderConfigurer {private Properties localProperties;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);try {localProperties = mergeProperties();} catch (IOException e) {throw new RuntimeException(e);}}public Properties getLocalProperties() {return localProperties;}/*** 獲取配置值** @param key 配置 key* @return 配置值*/public static String getConfig(String key) {CustomPropertySources bean = DiContextUtil.getBean(CustomPropertySources.class);assert bean != null;Object o = bean.getLocalProperties().get(key);if (o != null)return o.toString();else {System.err.println("找不到 " + key + "配置");return null;}}public static <T> T getConfig(String key, Class<T> clz) {String value = getConfig(key);return ConvertBasicValue.basicCast(value, clz);}
}
上述靜態(tài)的方法就是獲取配置的手段。
用戶配置
用戶來(lái)說(shuō),具體操作就是在 resources 目錄下設(shè)置application.yml
文件。
其他
另外,這里有個(gè)大神開(kāi)源的作品 spring-config-ext,也是在 MVC 中實(shí)現(xiàn)類似 Boot 的配置,號(hào)稱“spring mvc config simple extension, make it have the same config abilities as spring boot does.”,大家有興趣的可去看看。
運(yùn)行 Web 頁(yè)面
盡管打包為 JAR 包了,都是弄 API 接口了,也就沒(méi)什么理由存放那些 Web 頁(yè)面了。但某些情況下,作為一個(gè)前-前端人員,還是覺(jué)得有必要打開(kāi) JSP 渲染的,可以訪問(wèn)一下 html/css/js/jsp 資源。
按照 Servlet 3.0 規(guī)范,有一塊地方是專門(mén)存放 html/css/js 甚至 JSP 的,即META-INF\resources
,在工程的資源目錄下,即\src\main\resources\META-INF\resources
。所以,以前是在src\main\webapp
下面的所有文件,移動(dòng)到\src\main\resources\META-INF\resources
目錄下。
新建一個(gè) index.jsp 設(shè)置內(nèi)容<%=88888%>
即可測(cè)試之。
存在問(wèn)題:這個(gè)不像以前在 Eclipse 下可以修改了 JSP 重新編譯,在 IDEA 下沒(méi)法那樣子玩了,所以每次修改后要手動(dòng)重啟服務(wù)器,非常麻煩。如果有懂行的朋友知道怎么搞自動(dòng)重啟,請(qǐng)多告知!
單元測(cè)試
單元測(cè)試一般都有這兩個(gè)類,一個(gè)是配置,一個(gè)是基類。
配置很簡(jiǎn)單,但是你要修改掃描的包名,@ComponentScan
那里的。
package com.ajaxjs.iam.server;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("com.ajaxjs.iam.server")
public class TestConfig {}
基類是個(gè)抽象類,主要是綁定配置類和數(shù)據(jù)庫(kù)連接跟關(guān)閉,方便你不用每次都手動(dòng)連接數(shù)據(jù)庫(kù)。
package com.ajaxjs.iam.server;import com.ajaxjs.data.jdbc_helper.JdbcConn;
import com.ajaxjs.framework.spring.filter.dbconnection.DataBaseConnection;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;@ContextConfiguration(classes = TestConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public abstract class BaseTest {@Beforepublic void initDb() {DataBaseConnection.initDb();}@Afterpublic void closeDb() {JdbcConn.closeDb();}
}
一個(gè)例子。
打包與部署
Maven 打包
我們希望打出哪個(gè)環(huán)境的包,就只需要包含這個(gè)環(huán)境的配置文件即可,不想包含其他環(huán)境的配置文件,這時(shí)候可以直接在 maven 中使用 profiles 和 resources 來(lái)配置,打包時(shí)使用mvn package -P dev
即可。
<profiles><!--開(kāi)發(fā)環(huán)境--><profile><id>dev</id><properties><spring.profiles.active>dev</spring.profiles.active></properties><activation><activeByDefault>true</activeByDefault></activation></profile><!--測(cè)試環(huán)境--><profile><id>test</id><properties><spring.profiles.active>test</spring.profiles.active></properties></profile><!--生產(chǎn)環(huán)境--><profile><id>prod</id><properties><spring.profiles.active>prod</spring.profiles.active></properties></profile>
</profiles>
<build><resources><resource><directory>src/main/resources</directory><filtering>false</filtering></resource><resource><directory>src/main/resources.${spring.profiles.active}</directory><filtering>false</filtering></resource></resources>
</build>
原理如下:
maven 在構(gòu)建項(xiàng)目時(shí),默認(rèn)是把
main/resoures
目錄作為資源文件所在目錄的,現(xiàn)在我們?cè)?code>main/conf目錄下也存放了資源文件(即application.properites
文件),因此需要告訴 maven 資源文件所在的目錄有哪些,通過(guò) build 元素中增加 resources 元素就可以達(dá)到這一目的。這里告訴 maven 有兩個(gè)地方存在資源文件,一個(gè)是默認(rèn)的 resources 目錄,另一個(gè)是在src/main/conf/${env}
目錄下,而${env}
引用的是上面 properties 元素中定義的 env 的值,而它的值引用的又是spring.profiles.active
的值(其值為 dev、test 和 online 中的一個(gè)),因此,目錄要么是src/main/conf/dev
,要么是src/main/conf/test
,要么是main/conf/online
,這最終取決于參數(shù)spring.profiles.active
的值。因此,根據(jù)參數(shù)spring.profiles.active
的值的不同,在構(gòu)建打包時(shí)最終會(huì)選擇 dev、test 和 online 這三個(gè)目錄中的一個(gè)中的application.properties
打包到項(xiàng)目中來(lái)。
將應(yīng)用打成一個(gè) Fat Jar 的方式,可以用 Spring 的:
<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>1.3.3.RELEASE</version><configuration><mainClass>com.demo.proj.Main</mainClass></configuration><executions><execution><phase>package</phase><goals><goal>repackage</goal></goals></execution></executions>
</plugin>
Profiles
在實(shí)際使用環(huán)境中,我們同一個(gè)應(yīng)用環(huán)境可能需要在不同環(huán)境運(yùn)行(開(kāi)發(fā)、測(cè)試、生產(chǎn)等),每個(gè)環(huán)境的參數(shù)都有可能不同(連接參數(shù)、日志級(jí)別等),使用 Profiles 可以將不同環(huán)境下的參數(shù)進(jìn)行拆分,并指定加載。
IDEA 配置,在 src 目錄下創(chuàng)建 profiles 目錄,安排如下圖的配置文件。
然后 Maven Profile 打勾即可。
啟動(dòng)參數(shù)
開(kāi)始以為要 run 配置中加入--spring.profiles.active=dev
參數(shù),其實(shí)不用,還是在 IDEA 里面選 profile 打勾即可。
小結(jié)
參考
- SpringMVC 純注解配置