一.SpringBoot介绍
1.SpringBoot基本概念
Spring Boot是其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
2.回顾Spring的IOC,DI,AOP
IOC基本概念
DI基本概念
AOP基本概念
一.Spring回顾
XML配置IOC
1.创建项目
2.导入Spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
3.创建xml配置文件
4.创建一个MyBean
5.把MyBean在xml中配置
6.测试
加载xml配置,获取容器,获取MyBean (ClassPathXmlApplicationContext)
二.Spring的注解配置
一.Anno配置IOC
1.创建Spring的配置类
/**
* Spring的配置类 相当于是:applicationContext.xml
* @Configuration :Spring的配置标签,标记改类是Spring的配置类
*/
@Configuration
public class ApplicationConfig {
}
2.在配置类中定义Bean
/**
* Spring的配置类 相当于是:applicationContext.xml
* @Configuration :Spring的配置标签,标记改类是Spring的配置类
*/
@Configuration
public class ApplicationConfig {
/**
* @Bean : Spring的bean的定义标签 ,标记方法返回的对象交给Spring容器管理
*/
@Bean
public MyBean myBean(){
return new MyBean();
}
}
3.测试
加载配置类,获取容器,获取MyBean
@Test
public void test(){
//加载配置文件,拿到Spring容器
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfig.class);
//通过容器获取Mybaean
MyBean bean = applicationContext.getBean(MyBean.class);
System.out.println(bean);
}
二.ComponentScan&ComponentScans自动扫描
1.准备MyBean帖@Component
@Component
public class MyBean {
}
2.创建配置类,帖@ComponentScan
@Configuration
//@ComponentScans
@ComponentScan("包名")
public class ApplicationConfig {
}
ComponentScan是ioc组件自动扫描,相当于是 context:component-scan base-package=*
默认扫描当前包,及其子包 ;
ComponentScan.lazyInit :懒初始化
ComponentScan.excludeFilters :排除
注意:如果使用了自动扫描 ,那么配置类中就不要去配置@Bean
3.bean的详解
- bean的id: 方法名
- bean的name:可以通过 @Bean标签的name属性指定
- 生命周期方法:initMethod , destroyMethod
- 单利多利: @Scope(“prototype”)
- 懒初始化: @Lazy
@Bean(name="" , initMethod ="" ,destroyMethod="" )
//@Scope("prototype")
//@Lazy
public MyBean myBean(){
return new MyBean();
}
三.依赖注入
1.手动定义bean的方式
直接通过调方法的方式注入bean,或者通过参数注入bean
2.自动扫描定义bean的方式
使用 @Autowired 注入
/**
* @Bean : Spring的bean的定义标签 ,标记方法返回的对象交给Spring容器管理
* bean的id: 方法名 myBean
* bean的name:可以通过 @Bean标签的name属性指定
* 生命周期方法:initMethod , destroyMethod
* 单利多利: @Scope("prototype")
*/
@Bean(initMethod = "init" , destroyMethod = "destroy")
public MyBean myBean(OtherBean otherBean){
MyBean myBean = new MyBean();
myBean.setName("娃娃");
//myBean.setOtherBean(otherBean());
myBean.setOtherBean(otherBean);
return myBean ;
}
@Bean
//@Lazy
public OtherBean otherBean(){
return new OtherBean();
}
四.条件Conditional
Conditional注解帖在bean的定义方法上来判断,如果不满足条件就不会定义bean
1.在Bean的定义方法帖@Conditional
@Bean
@Conditional(value = MyCondition.class)
public MyBean windowsMyBean(){
return new MyBean("windowMyBean");
}
@Bean
@Conditional(value = LinuxCondition.class)
public MyBean linuxMyBean(){
return new MyBean("linuxMyBean");
}
2.定义条件类
public class MyCondition implements Condition {
/**
* 匹配方法,返回值决定是否满足条件
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取系统环境
String systemName = context.getEnvironment().getProperty("os.name");
if("Windows 10".equals(systemName)){
return true;
}
return false;
}
}
五.@Import
1.直接导入Bean或者配置类
@Configuration
@Import(ApplicationOtherConfig.class) //导入其他的配置类
public class ApplicationConfig
2.导入ImportSelector
定义ImportSelector
public class MyImportSelector implements ImportSelector {
//选择导入,该方法返回我们需要导入的类的全限定名
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"cn.itsource._07_import_selector.MyBean",
"cn.itsource._07_import_selector.OtherBean"};
}
}
@Configuration
@Import(MyImportSelector.class) //导入选择器
public class ApplicationConfig
3.导入ImportBeanDefinitionRegistrar
定义 ImportBeanDefinitionRegistrar
public class MyBeanRegistor implements ImportBeanDefinitionRegistrar {
//注册bean , BeanDefinitionRegistry :注册bean的注册器
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//参数:beanName :bean的名字
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(MyBean.class);
registry.registerBeanDefinition("myBean",rootBeanDefinition );
}
}
@Configuration
@Import(MyBeanRegistor.class) //导入bean注册器
public class ApplicationConfig
六.FactoryBean
通过工厂定义bean
1.定义FactoryBean
public class MyBeanFactoryBean implements FactoryBean<MyBean> {
public MyBean getObject() throws Exception {
return new MyBean();
}
public Class<?> getObjectType() {
return MyBean.class;
}
public boolean isSingleton() {
return true;
}
}
2.配置 MyBeanFactoryBean的bean定义
@Configuration
public class ApplicationConfig {
@Bean
public MyBeanFactoryBean myBean(){
return new MyBeanFactoryBean();
}
}
3.测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes ={ ApplicationConfig.class})
public class AnnoTest {
@Autowired
private MyBean myBean;
@Test
public void test(){
System.out.println(myBean);
}
}
七.Bean生命周期
1.Bean+InitMethod+DestoryMethod
@Bean(InitMethod="" , DestoryMethod="")
2.InitializingBean, DisposableBean
public class MyBean implements InitializingBean, DisposableBean {
public MyBean(){
System.out.println("创建.............");
}
public void destroy() throws Exception {
System.out.println("destroy方法...........");
}
public void afterPropertiesSet() throws Exception {
System.out.println("初始化方法...........");
}
}
3.PostConstruct+PreDestroy
@Component
public class MyBean {
@PostConstruct
public void init(){
System.out.println("init.....");
}
@PreDestroy
public void destory(){
System.out.println("destory.....");
}
}
4.BeanPostProcessor后置处理器
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前.....:"+bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之后.....:"+bean);
return bean;
}
}
三.SpringBoot-HelloWorld
一.HelloWorld
1.HelloWorld-web应用
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.创建配置类
@SpringBootApplication
public class ApplicationConfig {
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class);
}
}
3.编写Controller
@Controller
public class Example {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!";
}
}
二.HelloWorld-web应用分析
1.疑惑的点分析
-
spring-boot-starter-parent :SpringBoot的父工程,帮我们管理了很多的基础jar包
-
spring-boot-starter-web :SpringBoot和SpringMvc整合的jar包,并且导入了日志,tomcat,等等相关的jar包
-
RestController : Controller+ResponseBody
-
@EnableAutoConfiguration : 开启自动配置功能
-
SpringApplication.run : 启动SpringBoot应用
-
jar :SpringBoot应用默认打jar包
-
SpringBootApplication:包括三个标签组成
@SpringBootConfiguration - @Configuration : Spring的配置标签
@EnableAutoConfiguration :开启自动配置
@ComponentScan :组件自动扫描
/**
* 我懵逼了?
* 1. spring-boot-starter-parent : SpringBoot的父工程 ,(dependencyManagement)管理了很多很多的一些jar包。
* 2. spring-boot-starter-web : 这个是和web整合的包,把web环境所需要的的jar包全部导入进来了 ,包括:Spring,SpringMvc,日志,tomcat.json等等
* 3. @RestController : @Controller + @ResponseBody
* 4. @EnableAutoConfiguration: 开启自动配置的标签(比如前段控制器,视图解析器自动配置了) , 这个标签通过一个导入选择器(AutoConfigurationImportSelector)
* 会去加载 Spring-boot-auto-confiuration.jar 包里面的 spring.factories 里面的 EnableAutoConfiguration 节点下面的自动配置的的一些类 ,然后完成各种自动配置
* 比如:前段控制器: DispatcherServletAutoConfiguration 自动配置类里面使用@Bean的方式定义了 DispatcherServlet前段控制器..
* 5. SpringApplication.run : 启动项目,启动内嵌的tomcat
* 6. web项目居然打jar包: SpringBoot默认打jar包。
* 7. web.xml中的配置去哪儿了? - 前端控制器 : 自动配置
* 8. SpringMvc中的配置去哪儿了? - 视图解析器 : 自动配置
* 9. Tomcat是哪儿来的? :内嵌的tomcat spring-boot-starter-web 包把tomcat引入进来了
*
* 拓展:(如果一个父工程的pom.xml有这两个元素)
* dependencyManagement :这个元素里面的所有jar包不能被子模块直接使用,如果子模块非要用,需要在子模块自己的 dependencies里面依赖jar包
* 但是了版本号不写,使用父工程的版本号 ,作用就是统一版本号。
*
* dependencies : 这个元素里面的所有jar包默认都可以被子模块直接继承使用
*/
dependencyManagement
该标签下的jar包,默认是不能被子项目直接使用的 , 他只有声明的功能 , 如果只项目想用这里标签里面的jar包 ,需要显示的写出来 ,而版本号使用父工程的。达到版本号统一管理的效果
dependencies
这个标签下面的jar包默认会被子项目直接继承直接使用
2.SpringBoot自动配置分析
@EnableAutoConfiguration开启自动配置功能 ,
@EnableAutoConfiguration ->
AutoConfigurationImportSelector ->
spring-boot-autoconfigure-2.0.5.RELEASE.jar ->
spring.factories - EnableAutoConfiguration ->
在程序启动的过程中,加载 EnableAutoConfiguration 节点下的自动配置的类,完成相关的自动配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5Znnj3b-1581172094801)(images/1568527822807.png)]
四.SpringBoot的特点
1.使用注解配置,无需xml(简单粗暴)
2.快速搭建,开发
3.简化的maven
4.方便的和三方框架集成
5.内嵌tomcat,部署简单
6.内置健康检查,监控等
7.自动配置,让配置更加简单
五.SpringBoot基本使用
一.项目结构
src
--main
--java
--resources
--static //静态资源目录
--templates //模板页面目录,如:jsp ,ftl
--application.properties/application.yml //默认配置文件
二.独立运行
1.导入打包插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.打包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJ07ynBv-1581172094802)(images/1568515028409.png)]
打包命令会把jar打包到target目录
3.运行
java -jar xxx.jar
三.热部署
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.编译代码
ctrl + F9
3.配置IDEA的自动编译功能
配置setting的自动编译
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9kU8y8tE-1581172094802)(images/1568515414056.png)]
按 ctrl +shift + alt + / -> registry -> 如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ittQQ0VU-1581172094802)(images/1568515493870.png)]
四.yml基本语法
多个级之间用:分类,并且换行缩进 , 值不用换行 , 值前面有个空格
五.SpringBoot读取配置
一.使用@Value标签
配置文件
user:
username: ls
password: 456
age: 99
绑定配置的对象
@Component
public class User {
//@Value :从配置文件中取值 SPEL
@Value("${user.username}")
private String username = "zs";
@Value("${user.password}")
private String password = "123";
@Value("${user.age}")
private int age = 18;
. . . . . .
二.使用@ConfigurationProperties
配置文件
employee:
username: ls
password: 456
age: 99
绑定配置的对象
@Component
@ConfigurationProperties(prefix = "employee")
public class Employee {
private String username = "zs";
private String password = "123";
private int age = 18;
@ConfigurationProperties : 自动的根据前缀从配置中过滤出配置项目,然后根据当前对象的列名进行匹配,自动赋值
六.多环境配置切换
1.方式一
spring:
profiles:
active: test #激活(选择)环境test
---
spring:
profiles: dev #指定环境名字dev
server:
port: 9999
---
spring:
profiles: test #指定环境名字test
server:
port: 8888
2.方式二
通过配置文件的名字来识别环境
application-dev.yml
server:
port: 9999
application-test.yml
server:
port: 8888
application.yml
spring:
profiles:
active: test
#根据文件名字配置 application-dev.properties
七.日志的使用
1.基本使用
private Logger logger = LoggerFactory.getLogger(MySpringBootTest.class);
...
logger.error("我是一个error日志.....");
logger.warn("我是一个warn日志.....");
logger.info("我是一个info日志.....");
logger.debug("我是一个debug日志.....");
logger.trace("我是一个trace日志.....");
2.配置日志
#logging.level.cn.itsource=error
#logging.file=my.txt
#logging.file.max-size=1KB
#logging.pattern.console="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
3.指定配置文件配置logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 定义常量 : 日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n"/>
<!--ConsoleAppender 用于在屏幕上输出日志-->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--定义控制台输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--打印到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/springboot.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/springboot-%d{yyyyMMdd}-%i.log.gz</fileNamePattern>
<maxFileSize>1KB</maxFileSize>
<maxHistory>30</maxHistory>
<!--总上限大小-->
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<!--定义控制台输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--root是默认的logger 这里设定输出级别是debug-->
<root level="info">
<!--定义了两个appender,日志会通过往这两个appender里面写-->
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</root>
<!--如果没有设置 additivity="false" ,就会导致一条日志在控制台输出两次的情况-->
<!--additivity表示要不要使用rootLogger配置的appender进行输出-->
<logger name="cn.itsource" level="error" additivity="false">
<appender-ref ref="stdout"/>
<appender-ref ref="file"/>
</logger>
</configuration>
八.SpringBoot集成Thymeleaf
1.模板引擎的原理
2.jsp的原理
集成Thymeleaf
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.创建模板 resources/templates/hello.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>成功!</h1>
<!--使用语法th:text 将div里面的文本内容设置为 -->
<div th:text="${msg}">这是显示欢迎信息</div>
</body>
</html>
3.编写controller
@Controller
public class HelloController {
@RequestMapping("/index")
public String hello(Model model){
model.addAttribute("msg","后面有人,认真听课" );
return "hello";
}
}
4.编写主配置类
5.Thymeleaf的自动配置原理
@EnableAutoConfiguration 开启自动配置功能,通过一个AutoConfigurationImportSelector导入选择器去扫描 spring-boot-autoconfigure-2.0.5.RELEASE.jar 自动配置包下面的 spring.factories 文件中的很多很多的自动配置的类
而:ThymeleafAutoConfiguration 是的Thymeleaf的自动配置 ,在这个自动配置类里面通过一个ThymeleafProperties去读取配置文件中的配置(也有默认配置) ,来自动配置Thymeleaf,比如Thymeleaf的视图解析器的自动配置如下:
@Bean
@ConditionalOnMissingBean(name = "thymeleafViewResolver")
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
...
return resolver;
}
6.Thymeleaf的语法
下去找时间看
九.静态资源
1.静态资源目录
resource/static
2.webjars
导入jquery依赖 ( http://www.webjars.org/)
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.4.1</version>
</dependency>
导入jquery的js
<script src="/webjars/jquery/3.4.1/jquery.js"></script>
3.首页
resources/index.html
4.图标
resources/favicon.ico
十.SpringBoot中的MVC配置
1.配置拦截器
定义拦截器
@Component
public class MyHandlerInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle................:"+request.getRequestURI());
return super.preHandle(request, response, handler);
}
}
注册拦截器
@SpringBootApplication
public class ApplicationConfig implements WebMvcConfigurer {
@Autowired
private MyHandlerInterceptor myHandlerInterceptor;
/**
* 注册拦截器
* @param registry :拦截器的注册器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myHandlerInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
2.添加视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/xx").setViewName("hello2.html");
}
当访问/xx 定位到 templates/hello2.html
十一.集成DataSource
1.导入依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!-- mysql 数据库驱动. -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
2.手动配置方式
手动配置四个属性
jdbc:
username: root
password: 123456
url: jdbc:mysql:///ssm
driver-class-name: com.mysql.jdbc.Driver
@Bean
@ConfigurationProperties(prefix = "jdbc")
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
return dataSource ;
}
3.自动配置方式 - 重点
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql:///ssm
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
十二.集成MyBatis
1.导入依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
2.配置Mybatis
mybatis:
mapper-locations: classpath:cn/itsource/mapper/*Mapper.xml
3.配置Mapper接口扫描包
@SpringBootApplication
@MapperScan("cn.itsource.web.controller.mapper")
public class ApplicationConfig {
...
}
十三.集成事务
1.使用注解方式
开启事务管理器
@SpringBootApplication
@MapperScan("cn.itsource.web.controller.mapper")
@EnableTransactionManagement
public class ApplicationConfig{
...}
service打事务标签
@Transactional
@Service
public class EmployeeServiceImpl
2.使用xml方式配置
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
配置事务xml
<!-- 配置事物管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<aop:config>
<aop:pointcut expression="execution(* cn.itsource.web.controller.service..*.*(..))" id="coreServicePointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="coreServicePointcut"/>
</aop:config>
<!-- aop应用事务管理 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="select*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
导入xml配置
...
@ImportResource("classpath:applicationContext-service.xml")
public class ApplicationConfig
十四.PageHelper的使用
1.导入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
2.编写查询语句
<select id="selectPage" resultType="cn.itsource.web.controller.domain.Employee">
select id,username,realName from t_employee
</select>
注意:用了pagehelper之后 ,查询总条数的sql不用写 , limit 不用写 , 如上:
3.查询的service
@Override
public Page<Employee> selectPage() {
PageHelper.startPage(1,5 );
Page<Employee> page = (Page<Employee>) employeeMapper.selectPage();
return page;
}
PageHelper.startPage(1,5 ):设置当前页和每页条数
Page :是pagehelper自己封装的页面对象,如同我们以前的PageList ,里面有总条数,列表,总页数等等
4.获取结果
Page<Employee> page = employeeService.selectPage();
System.out.println("总条数:"+page.getTotal());
for (Employee employee : page.getResult()) {
System.out.println(employee);
}
面试题
请你说说springboot的启动流程
-
创建SpringApplication实例,判定环境,是web环境还是普通环境。加载所有需要用到的Initializers和Listeners,这里使用约定大于配置的理念揭开了自动配置的面纱。
-
加载环境变量,环境变量包括system environment、classpath environment、application environment(也就是我们自定义的application.properties配置文件)
-
创建SpringApplicationRunListeners
-
创建ApplicationContext,设置装配context,在这里将所有的bean进行扫描最后在refreshContext的时候进行加载、注入。最终将装配好的context作为属性设置进SpringApplicationRunListeners,这就完成了一个spring boot项目的启动。