1、引言
2、搭建过程
- 2.1 开发环境搭建
- 2.2 项目搭建
- 2.2.1 首先看`web.xml`配置
- 2.2.2 增加`WebApplicationInitializer`的实现类
- 2.2.3 增加`ApplicationContextConfig`配置类
- 2.2.4 增加`application.properties`配置文件
- 2.2.5 增加`WebSpringMVCServletConfig`配置类
- 2.3 测试结果
- 2.3.1 增加`UserInfoService`以及实现类`UserInfoServiceImpl`
- 2.3.2 增加`HelloWorldController`
- 2.3.3 启动`tomcat`进行测试
- 3、总结
1、引言
从开始使用SpringMVC的那一天我就开始想如何减少SpringMVC的配置,如何使用更简洁的方式搭建SpringMVC框架。通过学习和总结实现了本文说的零配置实现的SpringMVC。 本文的相关源码请参考:chapter-5-springmvc-zero-configurationhttps://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的理解》会解释。