SpringBoot的一般使用流程及常见问题记录

文章目录

    • 1. 搭建流程
    • 2. 常见问题
      • 2.1 Springboot创建非web应用
      • 2.2 打包Springboot项目,运行jar包提示“jar中没有主清单属性”
      • 2.3 让springboot在控制台打印MyBatis的日志
      • 2.4 springboot时间修改时区后接口返回时间不变
      • 2.5 springboot中Bean的循环依赖
      • 2.6 springboot中开启定时任务支持
      • 2.7 springboot打包成jar运行找不到 resources 下文件

1. 搭建流程

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,其他一样。

2. 常见问题

2.1 Springboot创建非web应用

 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);
    }
}

2.2 打包Springboot项目,运行jar包提示“jar中没有主清单属性”

 引入插件即可解决,可以自动添加清单,如下:

 <plugin>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-maven-pluginartifactId>
     <version>2.0.3.RELEASEversion>
     <executions>
         <execution>
             <goals>
                 <goal>repackagegoal>
             goals>
         execution>
     executions>
 plugin>

2.3 让springboot在控制台打印MyBatis的日志

 在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

2.4 springboot时间修改时区后接口返回时间不变

 在使用VOD时,UTC转成北京时间时,由于序列化的原因时间不论怎么转,控制台打印出来的是北京时间,但PostMan使用得到的还是UTC的时间,主要原因是Spring默认的序列化框架FastJson的问题,所以建议配置文件中加入:

# 配置序列化时时区
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

2.5 springboot中Bean的循环依赖

 在一次项目中出现这样下面的情况:

@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用到了aServicebService,就算将上述两个Service的Bean设成懒加载了,在Spring的IOC容器初始化后实例化Controller的Bean时,发现不少Controller通过@Autowired注入了aServicebService,所以上述的方法极少会解决问题,更好的方式应该在出现循环依赖的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时注入aServicebService,就不会有影响,比如初始化aService时,并不会去初始化bService,只有在调用aService中的某个涉及bService的具体方法时才会去初始化bService,至此问题解决(其实还是建议尽量减少代理之间的耦合度,良好的代码设计才是规避循环依赖的最好方式)。

2.6 springboot中开启定时任务支持

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,表示了23454个值;
  • ,:表示一个列表,可以理解为-一个意思,只是它可以取到间断不连续的值,比如2,4,6
  • /:语法为x/yx表示从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等(其他表达式效果一样即可)。

2.7 springboot打包成jar运行找不到 resources 下文件

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();
    }

你可能感兴趣的:(#,SpringBoot)