文章目錄
1、引言
從開始使用SpringMVC的那一天我就開始想如何減少SpringMVC的配置,如何使用更簡潔的方式搭建SpringMVC框架。通過學習和總結實現了本文說的零配置實現的SpringMVC。
本文的相關源碼請參考:chapter-5-springmvc-zero-configuration
https://gitee.com/leo825/spring-framework-learning-example.git
2、搭建過程
2.1 開發環境搭建
開發環境搭建省略了,如果不清楚的可以參考《SpringMVC學習(一)——快速搭建SpringMVC開發環境(非註解方式)》這個裏面的開發環境
2.2 項目搭建
2.2.1 首先看web.xml
配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- WEB應用的名字 -->
<display-name>springmvc</display-name>
<!-- WEB應用的描述 -->
<description>SpringMVC的demo</description>
<!-- 歡迎頁面,首頁 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
這裏面沒有少東西,我們本次不需要添加Spring的任何配置,因此也不需要之前Spring的applicationContext.xml
和myspringmvc-servlet.xml
配置文件。
2.2.2 增加WebApplicationInitializer
的實現類
這個實現類似web.xml配合,註冊bean並自啓動DispatcherServlet。
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
//創建一個Spring應用的root上下文
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ApplicationContextConfig.class);
System.out.println("ApplicationContextConfig註冊完畢");
//監聽root上下文的生命週期
container.addListener(new ContextLoaderListener(applicationContext));
//web上下文配置
AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
webApplicationContext.register(WebSpringMVCServletConfig.class);
System.out.println("WebSpringMVCServletConfig註冊完畢");
//創建並註冊DispatcherServlet
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(webApplicationContext));
registration.setLoadOnStartup(1);//設置啓動順序同等:
registration.addMapping("/");
System.out.println("DispatcherServlet註冊完畢");
}
}
2.2.3 增加ApplicationContextConfig
配置類
註冊bean配置類,類似我們的applicationContext.xml文
件
@Configuration
@ScanIgnore
public class ApplicationContextConfig extends WebMvcConfigurationSupport {
/**
* 加載配置文件,類似之前配置文件中的:
* <bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
* <property name="locations">
* <list>
* <value>classpath:*.properties</value>
* </list>
* <property/>
* </bean>
* <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
* <property name="properties" ref="configProperties"/>
* </bean>
*
* @return
*/
@Bean(name = "configProperties")
public PropertiesFactoryBean propertiesFactoryBean(){
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("application.properties"));
propertiesFactoryBean.setSingleton(true);
System.out.println("propertiesFactoryBean註冊完畢");
return propertiesFactoryBean;
}
@Bean(name = "propertyConfiguer")
public MyPropertyPlaceholder preferencesPlaceholderConfigurer(PropertiesFactoryBean configProperties) throws IOException {
MyPropertyPlaceholder propertyConfiguer = new MyPropertyPlaceholder();
propertyConfiguer.setProperties(configProperties.getObject());
System.out.println("preferencesPlaceholderConfigurer註冊完畢");
return propertyConfiguer;
}
/**
* 創建jdbcTemplate
* @return
*/
@Bean(name = "jdbcTemplate")//只要使用name = "driverManagerDataSource"就可以將註冊的bean依賴過來
public JdbcTemplate jdbcTemplate(DriverManagerDataSource driverManagerDataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(driverManagerDataSource);
System.out.println("jdbcTemplate註冊完畢");
return jdbcTemplate;
}
/**
* 創建數據源
* @return
*/
@Bean(name = "driverManagerDataSource")
public DriverManagerDataSource driverManagerDataSource(MyPropertyPlaceholder propertyConfiguer) {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(MyPropertyPlaceholder.getProperty("datesource.driverClassName"));
driverManagerDataSource.setUrl(MyPropertyPlaceholder.getProperty("datesource.url"));
driverManagerDataSource.setUsername(MyPropertyPlaceholder.getProperty("datesource.username"));
driverManagerDataSource.setPassword(MyPropertyPlaceholder.getProperty("datesource.password"));
System.out.println("driverManagerDataSource註冊完畢");
return driverManagerDataSource;
}
}
如果一個bean依賴其他的bean則可以按照如下方式處理
2.2.4 增加application.properties
配置文件
因爲2.2.3中我們使用了propertiesFactoryBean
通過配置文件來獲取配置的參數,因此這裏我們需要添加application.properties
文件。
datesource.driverClassName=com.mysql.jdbc.Driver
datesource.url=jdbc:mysql://localhost:3306/springframework_learning
datesource.username=cm9vdA==
datesource.password=cm9vdA==
細心的同學會發現,這裏的數據庫datesource.username
(用戶名)和datesource.password
(密碼)都是加過密的。我們正常的工作中也是會遇到用戶名密碼不能明文,但是實際連接的時候需要的是明文的情況,這就使用到了PreferencesPlaceholderConfigurer
來實現配置屬性的解密操作。參考如下實現:
public class MyPropertyPlaceholder extends PreferencesPlaceholderConfigurer {
//定義一個屬性的map對象
private final static Map<String, String> propertyMap = new HashMap();
/**
* 這裏要特別留意下,可以使用這種方式實現配置文件加解密,最常見的是對數據庫的配置的加解密
*
* @param beanFactoryToProcess
* @param props
* @throws BeansException
*/
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) throws BeansException {
super.processProperties(beanFactoryToProcess, props);
for (Object keyString : props.keySet()) {
String key = keyString.toString();
String value = props.getProperty(key);
//此處是做了配置文件的解密處理,以防止配置文件明文引起安全問題
if("datesource.username".equals(key) || "datesource.password".equals(key)){
try {
value = Base64Util.decodeDate(value);
} catch (Exception e) {
e.printStackTrace();
value = "";
}
}
propertyMap.put(key, value);
}
}
//自定義一個方法,即根據key拿屬性值,方便java代碼中取屬性值
public static String getProperty(String name) {
return propertyMap.get(name);
}
}
註冊bean的時候使用我們自定義的MyPropertyPlaceholder
就是解密後的配置參數了。
2.2.5 增加WebSpringMVCServletConfig
配置類
Web配置類,類似以前的myspringmvc-servlet.xml
,這裏使用了@ComponentScan
來掃描需要包,同時排除不需要掃描的包,如果不排除則會重複創建bean對象。《如何使用@component-scan排除不需要的類》 這篇文件介紹瞭如何排除不需要掃描的類。
@Configuration
@ScanIgnore
//@ComponentScan(basePackages = {"com.leo"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ApplicationContextConfig.class, WebSpringMVCServletConfig.class}))
//@ComponentScan(basePackages = {"com.leo"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = ScanIgnore.class))
//@ComponentScan(basePackages = {"com.leo"}, excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class))
@ComponentScan(basePackages = {"com.leo"}, excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.leo.config.*"))
public class WebSpringMVCServletConfig extends WebMvcConfigurationSupport {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
2.3 測試結果
其實到上面一步環境已經搭建完成了,下面就是寫測試類進行。
2.3.1 增加UserInfoService
以及實現類UserInfoServiceImpl
UserInfoService.java
如下
public interface UserInfoService {
/**
* 增加用戶信息
* @param userInfo
*/
void insertUserInfo(UserInfo userInfo);
/**
* 刪除用戶信息
* @param id
*/
void deleteUserInfo(Integer id);
/**
* 修改用戶信息
* @param id 舊用戶信息的id
* @param newUserInfo
*/
void updateUserInfo(Integer id, UserInfo newUserInfo);
/**
* 查詢用戶信息
* @return
*/
List<UserInfo> getUserInfoList();
}
UserInfoServiceImpl.java
如下
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public void insertUserInfo(UserInfo userInfo) {
jdbcTemplate.execute("INSERT INTO USER_INFO(NAME,GENDER,AGE,REMARKS) VALUES('" + userInfo.getName() + "','" + userInfo.getGender() + "','" + userInfo.getAge() + "','" + userInfo.getRemarks() + "')");
}
@Override
public void deleteUserInfo(Integer id) {
jdbcTemplate.execute("DELETE FROM USER_INFO WHERE ID = " + id);
}
@Override
public void updateUserInfo(Integer id, UserInfo newUserInfo) {
jdbcTemplate.update("UPDATE USER_INFO SET NAME=?, GENDER=?, AGE=? ,REMARKS=? WHERE ID=?", new Object[]{
newUserInfo.getName(),
newUserInfo.getGender(),
newUserInfo.getAge(),
newUserInfo.getRemarks(),
id
});
}
@Override
public List<UserInfo> getUserInfoList() {
List<UserInfo> userInfos = new ArrayList<>();
List<Map<String, Object>> results = jdbcTemplate.queryForList("SELECT * FROM USER_INFO");
for (Map obj : results) {
UserInfo userInfo = new UserInfo();
userInfo.setId((Integer) obj.get("ID"));
userInfo.setName((String) obj.get("NAME"));
userInfo.setGender("0".equals((String) obj.get("GENDER")) ? "女" : "男");
userInfo.setAge((String) obj.get("AGE"));
userInfo.setRemarks((String) obj.get("REMARKS"));
userInfos.add(userInfo);
}
return userInfos;
}
}
UserInfo.java
如下
public class UserInfo {
/**
* 用戶id
*/
Integer id;
/**
* 用戶名稱
*/
String name;
/**
* 用戶性別
*/
String gender;
/**
* 用戶年齡
*/
String age;
/**
* 備註
*/
String remarks;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getRemarks() {
return remarks;
}
public void setRemarks(String remarks) {
this.remarks = remarks;
}
@Override
public String toString() {
return "UserInfo{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age='" + age + '\'' +
", remarks='" + remarks + '\'' +
'}';
}
}
2.3.2 增加HelloWorldController
新增一個controller類,用接口測試實現
HelloWorldController.java
如下
@Controller
public class HelloWorldController {
@Autowired
UserInfoService userInfoService;
@RequestMapping(value = "/helloworld")
public String helloWorld() {
System.out.println("helloWorld");
return "success";
}
@RequestMapping(value = "/getUserInfoList", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
@ResponseBody
public String getUserInfoList() {
System.out.println("getUserInfoList");
String result = userInfoService.getUserInfoList().toString();
System.out.println(result);
return result;
}
}
2.3.3 啓動tomcat
進行測試
1.首先訪問http://localhost:8080/springmvc/helloworld
前端頁面跳轉到了success.jsp
2.然後訪問http://localhost:8080/springmvc/getUserInfoList
其中有個小插曲,訪問帶有數據的返回的時候前端頁面顯示亂碼可以使用produces = "application/json; charset=utf-8"
這個屬性來處理
3、總結
至此,以上已經完成了零配置實現的SpringMVC。至於爲什麼能使用WebApplicationInitializer
來實現類似於web.xml
的配置,另一篇文章《Spring中對於WebApplicationInitializer的理解》會解釋。