前言
项目构建的时候是截取了另一个项目的部分功能形成的,截取出的功能代码少但项目结构比较复杂,依赖严重 。改写过后,不少代码成了”鸡肋”,搞不清楚谁是谁 。
web/controller在一块,dao/domain独占一方 ; junit在6个pom。xml中出现了4次,版本还不一样 ; spring/mybatis的配置文件也有好几个,里面的xml代码也是长而杂 。
Clean Code,之前专门买了一本书,讲述代码清洁之道 。 整洁的代码对项目的开发效率至关重要,宏观一点,项目结构的清晰,也对后续的开发有所帮助。
SpringBoot
GitHub示例 Anddd7/SSM-SpringBoot-Starter`
前一段时间还没正式学习使用Spring时就听说了SpringBoot,还记得学生时代配置SSH,满篇的xml简直是童年阴影(- -如果xml节点还没对齐的话, 那 ……)。
SpringBoot可以达到0配置 (- -当然这是不可能的) 快速启动一个Spring应用,对简化配置有显著提升。
准备 - pom.xml
- 首先还是从Maven配置入手
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>anddd7.boot4ssh</groupId>
<artifactId>SSM-SpringBoot-Starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--官方推荐使用 spring-boot-starter-parent 包括一些预设配置:
1.使用java6编译级别<java.version>1.8</java.version>
2.使用utf-8编码<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3.实现了通用的测试框架 (JUnit, Hamcrest, Mockito).
4.智能资源过滤
5.智能的插件配置(exec plugin, surefire, Git commit ID, shade).
不继承也可使用dependcy引入 ,因为scope是import,不再允许在<properties>覆盖第三方包版本
如果要使用其他版本,需要在上面的前面添加一个完整的dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.4.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<mybatis.version>3.4.2</mybatis.version>
<mybatis-spring.version>1.3.1</mybatis-spring.version>
<spring-boot.version>1.4.4.RELEASE</spring-boot.version>
</properties>
<!-- 对应模块的starter包含常用依赖 ,例如starter-web{spring-web,spring-mvc}-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<!-- springboot+mybatis官方连接包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- dbcp 数据源 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--用于将项目打包成fat jar(executable jar)-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- mybatis 工具 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 如果使用Jetty ,需要替换掉内置的Tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
<!-- 使用jetty启动 ,先移除tomcat组件 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 添加jetty支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>${spring-boot.version}</version>
</dependency>
开始 - Application 和 Configuration
引入Maven后 ,准备一个Controller ,然后通过SpringApplication启动Spring就可以使用了 Spring Boot 快速入门 ;需要对Spring进行一些配置的时候(和Mybatis融合、配置数据源等)就需要将Application单独出来 。
- 1 Spring配置application.xml -> application.yaml
# application.yaml
# Server settings (ServerProperties)
# 以数据为中心的标记语言 ,易于读写 ,替代xml
# springboot 默认是8080端口 ,没有项目地址
# 服务器设置
server:
port: 8080
address: 127.0.0.1
sessionTimeout: 30
contextPath: /testboot
# 内置tomcat设置
tomcat:
accessLogEnabled: false
protocolHeader: x-forwarded-proto
remoteIpHeader: x-forwarded-for
basedir:
backgroundProcessorDelay: 30 # secs
- 2 设置SpringBoot启动入口
package anddd7.springboot;
import anddd7.springboot.configuration.MapperScannerConfiguration;
import anddd7.springboot.configuration.MyBatisConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
/**
* 将spring的启动和配置单独出来
*
* @SpringBootApplication 等价于 :
* - @Configuration 通过@ImportResource引入xml文件,@Value读取键值 ,@Bean读取配置的bean实例
* - @ComponentScan 同xml中的自动扫描组件
* - @EnableAutoConfiguration
* @EnableAutoConfiguration 下包含了一系列自动配置类的清单 ,并按顺序执行 ,在Spring3时代就有@EnableWebMvc注解
* [SpringBoot之@EnableAutoConfiguration原理及自定义扩展 ](http://blog.csdn.net/xiaoyu411502/article/details/52770723)
* <p>
* MongoAutoConfiguration.class为例 :
* - 使用了@Configuration ,标识是一个配置
* - 定义了必要的Mongo对象@Bean
* - 使用了@EnableConfigurationProperties ,将application.properties配置文件中的属性映射到Java类中 ,便于使用
* - @ConditionOnClass表明加载条件 - Mongo.class位于类路径上
* - @ConditionalOnMissingBean说明Spring Boot仅仅在当前上下文中不存在Mongo对象时,才会实例化一个Bean
*/
@Import({MyBatisConfiguration.class, MapperScannerConfiguration.class})
@SpringBootApplication
public class Application {
public static void main(String[] args) {
//一键自动启动
SpringApplication.run(Application.class, args);
//配置启动
// SpringApplication app = new SpringApplication();
// app.setBannerMode(Banner.Mode.OFF);
//链式API构建器
// new SpringApplicationBuilder()
// .bannerMode(Banner.Mode.OFF)
// .child(Application.class)
// .run();
}
}
依旧是@SpringBootApplication,只不过@Import了一些配置类,使用过xml配置Mybatis和Spring的童鞋对这些类名一定很熟悉,其实就是将原来xml转化为了Java类配置 — @Configuration注解
- 3.1 数据源
package anddd7.springboot.configuration;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* 对应xml中datasource的配置
*/
@Configuration
@PropertySource("classpath:prop.properties")
public class DataSourceConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.maxActive}")
private int maxActive;
@Value("${jdbc.maxIdel}")
private int maxIdel;
@Value("${jdbc.maxWait}")
private long maxWait;
@Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setMaxActive(maxActive);
dataSource.setMaxIdle(maxIdel);
dataSource.setMaxWait(maxWait);
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
return dataSource;
}
}
- 3.2 prop.properties
#jdbc
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=214124
jdbc.maxActive=2335
jdbc.maxIdel=120
jdbc.maxWait=100
- 4 MapperScanner
package anddd7.springboot.configuration;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 扫描mybatis的接口
*/
@Configuration
// 因为这个对象的扫描,需要在MyBatisConfig的后面注入,所以加上下面的注解
@AutoConfigureAfter(MyBatisConfiguration.class)
public class MapperScannerConfiguration {
@Bean(name = "mapperScannerConfigurer")
public MapperScannerConfigurer mapperScannerConfigurer() {
//创建内置的config
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//获取之前注入的beanName为sqlSessionFactory的对象
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
//指定mapper接口的路径 ,批量加上@Mapper ,也可以手动加上 ,省略此配置
//会触发 No MyBatis mapper was found in '[anddd7.springboot.controller, anddd7.springboot]' package. 警告
//mapperScannerConfigurer.setBasePackage("anddd7.springboot.dao");
return mapperScannerConfigurer;
}
}
- 5.1 SqlSessionFactory
package anddd7.springboot.configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import javax.sql.DataSource;
import java.io.IOException;
/**
* 对应xml中事务和sqlSessionFactory的配置
*/
@Configuration
@EnableTransactionManagement
@Import(DataSourceConfiguration.class)
public class MyBatisConfiguration implements TransactionManagementConfigurer {
@Autowired
private DataSource dataSource;
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean() {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
//设置数据源
bean.setDataSource(dataSource);
//设置config路径
ResourceLoader resourceLoader = new DefaultResourceLoader();
bean.setConfigLocation(resourceLoader.getResource("classpath:mybatis-configuration.xml "));
//设置mapper.xml路径
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
bean.setMapperLocations(resolver.getResources("classpath:anddd7/springboot/mapping/*.xml"));
} catch (IOException e) {
e.printStackTrace();
}
try {
return bean.getObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
- 5.2 mybatis-configuration.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="lazyLoadingEnabled" value="false" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="SIMPLE" />
<setting name="defaultStatementTimeout" value="25000" />
</settings>
</configuration>
- 6 除了application.xml还以properties或yaml存在,其他配置都Java化了,web.xml自然也不存在了(顺应servlet 3.0的注解方式)。
package anddd7.springboot.configuration;
import anddd7.springboot.filter.LoginFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.Filter;
/**
* 对应web.xml中的所有设置
*/
public class WebXmlConfiguration {
@Bean(name = "LoginFilter")
public Filter getLoginFilter() {
return new LoginFilter();
}
@Autowired
DispatcherServlet dispatcherServlet;
/*
新的springboot不需要再设置servlet -> DispatcherServlet ,默认会拦截请求交给DispatcherServlet
@Bean
public ServletRegistrationBean defaultServletRegistration() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.setServlet(dispatcherServlet);
registrationBean.addUrlMappings("*.ajax");
return registrationBean;
}
*/
@Bean
public FilterRegistrationBean loginFilterRegistration() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(getLoginFilter());
registrationBean.setName("LoginFilter");
registrationBean.addUrlPatterns("*.html", "*.ajax");
return registrationBean;
}
}
从上面文件可以看到 ,通过@Configuration
+@Import
将xml转化成了容易理解的Java 。所以不是不需要配置,而是规范化、简单化的配置,而且启动方式上也简便不少。
SpringBoot 将原来一些固定的启动方式打包在了一起 ,包含了很多自动化的配置 (@Enable*注解系列); 采用易读写的yaml和Java,摒弃xml(真的头晕)。
运行
Application.main()
总结
GitHub示例 Anddd7/SSM-SpringBoot-Starter`
如果要添加webapp(网页静态资源),重命名为resources放在源目录下 (SpringBoot默认查找resources/static/public作为静态目录)。熟练过后 ,配置一个可用的Spring要比之前快得多 ,而且配置文件减少 ,项目目录结构清晰不少 。
不过对Spring不熟悉 ,没有一条一条配过xml的入门者 ,这种一键式配置推荐使用不推荐学习 。封装太深 ,很难理解每一步的意义 ,所以使用过后还是对照xml一步步探究Spring所做的事 ,共勉 。
Next ,SpringBoot原理和Spring源码