1.引入Spring-Boot依赖、Spring-Boot整合MyBatis的插件依赖、MyBatis由数据库表逆向生成JavaBean和Mapper文件的mybatis-generator插件等:
<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.0modelVersion>
<groupId>com.glodon.tot.librarygroupId>
<artifactId>mybatis-tot-libraryartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.4.5.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
<mybatis.version>3.4.0mybatis.version>
<mybatis.generator.version>1.3.2mybatis.generator.version>
<mybatis.springboot.version>1.1.1mybatis.springboot.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.springboot.version}version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.2version>
<executions>
<execution>
<id>Generate MyBatis Artifactsid>
<goals>
<goal>generategoal>
goals>
execution>
executions>
<configuration>
<verbose>trueverbose>
<overwrite>trueoverwrite>
configuration>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>${mybatis.generator.version}version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>${mybatis.version}version>
dependency>
dependencies>
plugin>
plugins>
build>
project>
2.resources目录下配置SpringBoot的配置文件和逆向工程插件mybatis-generator的配置文件,主要如下:
[application.yml]
spring:
application:
name: mybatis-spring-demo-tot
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/glodon_test
username: root
password: root
[generatorConfig.xml]
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/glodon_test"
userId="root"
password="root">
jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
javaTypeResolver>
<javaModelGenerator targetPackage="com.glodon.tot.models" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
javaModelGenerator>
<sqlMapGenerator targetPackage="com.glodon.tot.mappers" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.glodon.tot.mappers" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="books" domainObjectName="Books">table>
<table tableName="buser" domainObjectName="Buser">table>
<table tableName="bookrecords" domainObjectName="BookRecords">table>
context>
generatorConfiguration>
上面是我之前的基本配置(如果mybatis的maven插件报红重启IDEA即可),然后通过侧边菜单栏Maven Projects中Plugins中的mybatis-generator插件运行即可生成数据库中表的mapper接口和接口对应的映射文件和javaBean对象,的下面有一个差不多的配置文件,也可参照:
[generatorConfig.xml]
<generatorConfiguration>
<context id="DB2Tables" defaultModelType="flat" targetRuntime="MyBatis3Simple">
<commentGenerator>
<property name="suppressTypeWarnings" value="true"/>
<property name="suppressDate" value="true"/>
<property name="suppressAllComments" value="false"/>
<property name="javaFileEncoding" value="UTF-8"/>
commentGenerator>
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://10.1.83.41:3306/demo?useUnicode=true&characterEncoding=utf8"
userId="root" password="root">
jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
javaTypeResolver>
<javaModelGenerator targetPackage="com.glodon.demo.mybatis.models" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="com.glodon.demo.mybatis.mappers" targetProject="src/main/resources">
<property name="enableSubPackages" value="true"/>
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER" targetPackage="com.glodon.demo.mybatis.mappers" targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
javaClientGenerator>
<table tableName="user" domainObjectName="User"
enableCountByExample="true"
enableUpdateByExample="true"
enableDeleteByExample="true"
enableSelectByExample="true"
selectByExampleQueryId="true">
<generatedKey column="id" sqlStatement="JDBC"/>
<columnOverride column="sex" javaType="com.glodon.demo.mybatis.models.Sex" />
table>
context>
generatorConfiguration>
3.创建SpringBoot的入口类,该类会自动扫描它所在目录及子级目录,所以注意一下它的位置。
@SpringBootApplication
@MapperScan("com.glodon.tot.mappers") //指定mapper接口的扫描路径
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.利用maven插件生成对应的JavaBean和Mapper接口以及对应的mapper文件,在创建好数据的表后,直接在Maven Projects侧边菜单栏找到Plugins–>mybatis-generator–>mybatis-generator:generate运行即可生成。
5.根据业务需求进行开发,注解啥的都跟Spring和SpringMVC一样,但是Controller类上的注解由@Controller
变成了@RestController
,其他一样。
SpringBoot除了可以用来写API,也可以用来直接作应用,只是这个时候需要改动Spring-Boot的启动类,继承CommandLineRunner
类,只是不用写Controller层了,如下:
/**
* @author liuwg-a
* @date 2018/12/18 20:32
* @description
*/
@SpringBootApplication
public class Application implements CommandLineRunner {
@Autowired
DownloadService downloadService;
public static void main(String[] args) {
// 这里的args一定不能掉,否则无法获取参数
SpringApplication.run(Application.class, args);
}
// 接受命令行中的参数
@Override
public void run(String... args) {
if (args.length!=1) {
throw new IllegalArgumentException("Please input only one path that you want to store!");
}
// 调用的时候直接调用Service层即可,即不用再写Controller层了
downloadService.getAllPublicRfas(args);
exit(0);
}
}
引入插件即可解决,可以自动添加清单,如下:
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.0.3.RELEASEversion>
<executions>
<execution>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
在springboot的配置文件application.properties
(如果是yml文件具体更改格式)中写入:
# 让MyBatis打印日志
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
指定某个模块使用其他多个模块中的mapper.xml文件,在配置文件中application.properties
(如果是yml文件具体更改格式)中写入:
# mybatis
mybatis.mapperLocations=classpath:com/goujianwu/portal/common/mapper/*.xml
mybatis.typeAliasesPackage=com.goujianwu.portal.common.model
# 引用多个模块的mapper文件
mybatis.mapperLocations=classpath*:mapper/*.xml
在使用VOD时,UTC转成北京时间时,由于序列化的原因时间不论怎么转,控制台打印出来的是北京时间,但PostMan使用得到的还是UTC的时间,主要原因是Spring默认的序列化框架FastJson的问题,所以建议配置文件中加入:
# 配置序列化时时区
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
在一次项目中出现这样下面的情况:
@Service("aService")
public class AServiceImpl implements AService {
@Autowired
private BService;
//...
}
@Service("bService")
public class BServiceImpl implements BService {
@Autowired
private AService;
//...
}
启动项目时出现:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testCallbackController': Unsatisfied dependency expressed through field 'AService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [bService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:370)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1336)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230)
at com.goujianwu.task.Application.main(Application.java:23)
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'AService': Bean with name 'AService' has been injected into other beans [bService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:581)
出现提示AService和BService循环依赖,项目无法启动,有点类似于死锁的场景,Spring在初始化容器时会将所有Bean对象进行初始化,在初始化AService
对象,发现需要初始化BService
,但在初始化时发现又要初始化AService
,Spring就有点懵逼了,这特么到底要宝宝先初始化哪个Bean啊,所以会出现上述的启动异常。
解决方案:通过将其中一个Bean设成懒加载即可,如:
@Lazy
@Service("aService")
public class AServiceImpl implements AService {
@Autowired
private BService bService;
//...
}
@Service("bService")
public class BServiceImpl implements BService {
@Autowired
private AService aService;
//...
}
但通常不会这么简单,我们发现有很多Controller
用到了aService
和bService
,就算将上述两个Service的Bean设成懒加载了,在Spring的IOC容器初始化后实例化Controller的Bean时,发现不少Controller通过@Autowired
注入了aService
和bService
,所以上述的方法极少会解决问题,更好的方式应该在出现循环依赖的Bean中注入另一个Bean时同时指定为懒加载,另一个不变:
@Service("aService")
public class AServiceImpl implements AService {
@Lazy
@Autowired
private BService bService;
//...
}
@Service("bService")
public class BServiceImpl implements BService {
@Autowired
private AService aService;
//...
}
这样一来就算加载Controller时注入aService
和bService
,就不会有影响,比如初始化aService
时,并不会去初始化bService
,只有在调用aService
中的某个涉及bService
的具体方法时才会去初始化bService
,至此问题解决(其实还是建议尽量减少代理之间的耦合度,良好的代码设计才是规避循环依赖的最好方式)。
SpringBoot启动类上添加@EnableScheduling
注解开启支持:
@SpringBootApplication
@EnableScheduling
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
使用的时候只需要在Bean中的方法上加上注解@Scheduled
注解即可:
@Scheduled(cron = "0/1 0 20 * * *")
protected void run() {
...
}
该注解支持多种参数,上面是仅是利用的cron
表达式cron = x x x x x x x
,个人觉得在简单场景下使用较为方便,表达式中的7个x
从前至后分别表示秒、分、时、日、月、星期几、年份(可选参数),下面提供cron
参数的说明:
参数 | 说明 |
---|---|
第1位x |
表示秒,取值范围0-59 |
第2位x |
表示分,取值范围0-59 |
第3位x |
表示时,取值范围0-23 |
第4位x |
表示日,取值范围1-31 |
第5位x |
表示月份,取值范围1-12 |
第6位x |
表示星期几,取值范围1-7 (注:从周日开始算,即表示周日-周六) |
第7位x |
表示年份(可选),取值范围1970-2099 |
注:
此外,上述7位参数还支持如下取值:
*
:可以匹配任意值,具体范围根据所处参数位置判断,可以理解为每秒,每分,每时…?
:表示不确定的值,它只能出现在日和星期几中,即第4、6位参数中;-
:表示取值区间,包含首尾,如2-5
,表示了2
、3
、4
、5
4个值;,
:表示一个列表,可以理解为-
一个意思,只是它可以取到间断不连续的值,比如2,4,6
;/
:语法为x/y
,x
表示从x
开始,y
表示步进值;#
:表示第几个,只能出现星期几中,即第6位参数,比如1#2
,表示第2周中的周日;下面是示例:
0 0 3 * * ? 每天3点触发任务
0 5 3 * * ? 每天3点5分触发任务
0 5 3 ? * * 每天3点5分触发任务,其中日使用?表示不确定值,因为可能为28、30、31
0 5/10 3 * * ? 每天3点5分触发任务,并且期间每隔10分钟周期性执行一次,直到3点55结束当天任务,次日再次重复
0 10 3 ? * 1 每周星期日的3点10分触发任务
0 10 3 ? * 1#3 每个月的第三周中的星期天触发任务,#号只能出现在星期几的位置上;
啰嗦一句,不要写代码写傻了,和我一样想不通@Scheduled(cron = "0/1 0 20 * * *")
为什么在晚上8点触发任务后,每一秒都执行一次,为什么只能执行到20:00:59
,不继续往下运行了,因为分钟位置上限制了为0
,即20:01:00
后就不在触发任务了,触发将其改作0-1
等(其他表达式效果一样即可)。
SpringBoot 打包成 jar 后无法读取 resources 下的文件,必须以流的方式读取,否则会抛文件未找到的异常,示例代码如下:
public String uEditorOption() {
// springboot打包后只能以流的方式读取jar包classes下的文件
StringBuilder config = new StringBuilder();
try {
InputStream stream = this.getClass().getClassLoader().getResourceAsStream("ueditor/config.json");
if (stream == null) {
return null;
}
BufferedReader br = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) {
config.append(line);
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return config.toString();
}