搭建SpringBoot环境
首先在Eclipse创建Maven工程。创建完后,在pom.xml里设置
<groupId>com.xx</groupId>
<artifactId>xxx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
这个设定了工程文件打包后,输出为war。
添加springboot的基础包,
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.22.RELEASE</version>
</parent>
用于引用springboot,其中version可以根据自己的需要自选。
还要在pom.xml里设定JDK的版本,
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
此外,还要在Eclipse检查下工程的库对应的是JDK还是JRE,要确保是JDK。
同时,要设定
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
这个是用于在mvn package时,将spring相关的包打入到war里。如果没有这一项,那么在mvn package时,只会把工程文件相关的包打入。在tomcat上部署时,不要这个plugin打包也能运行。
此外,也可以添加配置
<build>
<finalName>${project.artifactId}</finalName>
</build>
这个指明了打war包时,文件名仅为artifactId,不带版本号。
下面要添加dependency了:
<!--开发时内嵌tomcat,会在打包时去掉这些包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- 用于springboot的单元测试,后续会看到 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
同时为了支持@Slf4j注解(用于log打印)和@Data注解(用于自动产生getter, setter),引用
<!-- support log -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
用于支持properties属性加密。
<!-- encrypt properties -->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>{jasypt.version.id}</version>
</dependency>
为了让eclipse能支持lombok,需要在lombok.jar下载完毕后,找到对应的jar文件,运行以下命令,将lombok.jar作为插件安装在Eclipse上。
java -jar lombok.jar
创建启动类
在package的根目录下,创建启动类。
@Controller //spring mvc的handler
@EnableEncryptableProperties // 可读取加密的properties。ENC(xxx)
@SpringBootApplication //spring项目的入口类,用于开启自动配置
@Configuration //自身就是一个applicationContext.xml
@RequestMapping("ctrl") //指定该controller的访问路径
@Slf4j //用于调用log
public class App extends SpringBootServletInitializer {
public static void main( String[] args ) {
// 这个是在开发的时候用,run as application时指定该类即可启动
SpringApplication.run(App.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
// 这个用于以war包运行时,由外部容器进行配置
return builder.sources(App.class);
}
@RequestMapping("hello") //访问路径/ctrl/hello
@ResponseBody //以json格式返回对象
public String hello() {
return "hello world!";
}
}
创建单元测试类
在工程的src/test/java里面的创建的包下,创建测试类
@RunWith(SpringRunner.class)
@SpringBootTest //可以在这里设定启动的主类
public class AppTest {
@Test
public void testxx() {
};
}
这样,就可以在测试类中直接@Autowired Spring的Bean,然后就可以对各个Bean进行单元测试了。
静态文件
默认静态文件的路径是src/main/resources/static下,在这个文件里,可以创建html,css,js文件。
全局异常处理
SpringBoot默认出现异常时,不会printstacktrace,只会在页面显示简单的错误信息。为了能够自定义全局异常处理,可以注解类@ControllerAdvice,如下:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) //用于处理throw new Exception()类型
public ModelAndView defaultExceptionHandler(Exception ex) {
ex.printStackTrace();
ModelAndView mav = new ModelAndView();
mav.setViewName("showerr"); //异常模板文件:showerr.html
mav.addObject("code", 500);
mav.addObject("msg", ex.toString());
return mav;
}
}
这里使用了ModelAndView作为异常显示页面的模板,为了让Spring支持模板,还需要在pom.xml中定义
<!-- support html template -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
默认模板文件在src/main/resources/templates下。上面的ModelAndView模板文件可以写为
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8"></meta>
<title>异常页面</title>
</head>
<body>
<h1>http错误:<span th:text="${code}"></span></h1>
<div th:text="${msg}"></div>
</body>
这样ModelAndView就可以把addObject里的值传到src/main/resources/templates/showerr.html文件中。
配置文件
源代码默认配置文件路径是src/main/resources/application.properties下。
多profile支持
我们在开发时需要针对开发、测试、生产环境指定不同的application.properties配置文件。如在application.properties定义
spring.profiles.active=dev
那么SpringBoot默认配置文件就会取application.properties + application-dev.properties。这样就可以同时指定不同的配置信息,只需修改spring.profiles.active就可以自由进行配置信息的切换了。
关于前端和后端的Date类型的坑
Spring一直存在的坑是,Date始终要单独进行配置处理。首先,后端向前端返回Date类型时,需要指定返回的格式,默认下返回的是一个整数。可以在application.properties里面定义全局的后端向前端传送的格式:
spring.jackson.date-format=yyyy-MM-dd
spring.jackson.time-zone=GMT+8
这样返回给前端的才是一个以北京时间字符串。
另外在javascript里注意,用字符串初始化Date会发现有以下的不同
var x1 = new Date("2019-01-01"); //返回的是2019-01-01 00:00:00格林尼治时间
var x2 = new Date("2019-01-01 00:00:00"); //这个才是2019-01-01 00:00:00北京时间
此外,前台向后台传Date时,如果想要以字符串的形式传,还需要在后端的controller的函数中,进行参数转换,Date才能接收:
@RequestMapping("getVal")
@ResponseBody
public String getVal(@DateTimeFormat(pattern="yyyy-mm-dd") Date day) throws Exception{
}
运行
在开发环境下,用Eclipse选定App.java类,run as application即可运行。
在测试环境下,先用maven对源代码进行编译:
mvn compile
编译通过后,就可以运行
mvn spring-boot:run
在正式环境下运行,需要将工程文件打包成war,可以在Eclipse或者命令行运行
mvn clean package
Oracle数据库支持
由于Oracle停止了和Maven的合作,所以在Maven的仓库中是无法找到Oracle的连接支持的。一种方法是按照Oracle官网的做法,通过一系列复杂的操作链接Oracle的repository;还有一种做法就是用本地的jar包。首先在本地安装的Oracle客户端找到ojdbcxx.jar的包,把它复制到工程目录的lib/下,这样,就可以在pom.xml配置本地jar的路径:
<!-- local jar for oracle -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>8</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath>
</dependency>
然而,采用这种方法,在打war包时,不会自动将这个jar文件添加到war包中,需要人工将对应的文件拷入。
日志支持
前面提到的lombok包里,自动带了对日志的支持。相关日志可以配置在application.properties里,如下:
logging.level.org.springframework=INFO # 可定义对不同package的类的日志打印级别
logging.path=/home/xx/log # 可将log除打印在console外,也可以记录到文件里。
这样,对于任何要调用log的类,只需要注解@Slf4j,如下:
@Slf4j
public class ClassWithLog {
public void someAction() {
try {
// TODO:
log.info("finish");
}
catch(Exception e) {
log.error("error happens", e); //自动打印e.printstackTrace到log中
}
}
}
Mybatis支持
在pom.xml中定义
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.5</version>
</dependency>
同时在application.properties里定义数据库连接
spring.datasource.username=xx
spring.datasource.password=xx
spring.datasource.url=jdbc:oracle:thin:@mydbip:1521:dbservicename
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# mybatis.mapper-locations.classpath=mapping/*Mapper.xml
# mybatis.type-aliases-package=com.example.entit
SpringBoot建议之前在xml的定义,都改在代码里。因此,这里注释掉了后两项。
在Java的interface上,只要注解了@Mapper,就可以当成mapper处理,如
@Mapper
public interface NormalNVMapper {
@Select({"<script>",
"SELECT * FROM tbl_order",
"WHERE 1=1",
"<when test='title!=null'>",
"AND mydate = #{mydate}",
"</when>",
"</script>"})
public List<MyQueryResult> getDataBySelect(@Param("mydate") Date mydate);
static class SQLProvider {
public String sql(Map<String, Object> param) {
Date mydate = (Date)param.get("mydate");
SQL sql = new SQL().SELECT("*").FROM(TBL_ORDER);
if (myDate != null) {
sql.WHERE("mydate LIKE #{mydate}");
}
return sql.toString();
}
};
@SelectProvider(type = SQLProvider.class, method = "sql")
public List<MyQueryResult> getDataBySelect2(@Param("mydate") Date mydate);
}
上面代码要注意的点是:
- Mybatis的Mapper支持两种构造动态sql的方法,一种是@Select注解,使用方法和xml文件方法一样,只是首尾要带上<script>;另一种是@SelectProvider,通过java进行拼接。两种方法不能混用,只能任选一种
- 函数的参数必须要带@Param,否则Mapper会按照arg0,arg1解析参数,导致参数找不到
多数据源的支持
Springboot默认情况下只会产生一个sqlsessionfactory。如果想要让Springboot支持多个Mybatis数据源,需要自定义bean来处理了。
以配置2个数据源为例,在application.properties里定义
spring.datasource.db1.username=xx
spring.datasource.db1.password=xx
spring.datasource.db1.url=jdbc:oracle:thin:@xx
spring.datasource.db1.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.db2.username=xx
spring.datasource.db2.password=xx
spring.datasource.db2.url=jdbc:oracle:thin:@xx
spring.datasource.db2.driver-class-name=oracle.jdbc.driver.OracleDriver
这里定义了2个数据库的连接db1,db2。对应的db1 Bean的配置为:
@Configuration
@MapperScan(basePackages="${db1扫描的mapper路径}", sqlSessionTemplateRef = "db1Template")
public class DB1Configuration {
@Bean
@Primary // 注意spring在初始化时,会尝试调用DataSource Bean初始化一个数据库的连接,如果有多个dataSource会报错,因此要指定primary。如果想禁止Spring的这种行为,可以设置spring.datasource.initialize=false
@ConfigurationProperties(prefix = "spring.datasource.db1") //.properties里对应的db1的配置
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory db1SqlSessionFactory(DataSource db1DataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(db1DataSource);
return bean.getObject();
}
@Bean
public DataSourceTransactionManager db1TransactionManager(DataSource db1DataSource) {
return new DataSourceTransactionManager(db1DataSource);
}
@Bean
public SqlSessionTemplate db1Template(SqlSessionFactory db1SqlSessionFactory) throws Exception {
return new SqlSessionTemplate(db1SqlSessionFactory);
}
}
db2的Bean配置和db1类似,除了不需要指定primary dataSource了。
@Configuration
@MapperScan(basePackages="${db2扫描的mapper路径}", sqlSessionTemplateRef = "db2Template")
public class DB2Configuration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
// 指定了@Primary之后,将无法通过autowired按照名称绑定DataSource,必须要用@Qualifier
@Bean
public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource db2DataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(db2DataSource);
return bean.getObject();
}
@Bean
public DataSourceTransactionManager db2TransactionManager(@Qualifier("db2DataSource") DataSource db2DataSource) {
return new DataSourceTransactionManager(db2DataSource);
}
@Bean
public SqlSessionTemplate db2Template(SqlSessionFactory db2SqlSessionFactory) throws Exception {
return new SqlSessionTemplate(db2SqlSessionFactory);
}
}
这样将db1、db2对应的mapper分别放置在对应的base package下就可以自动读取了。