简单学习了SpringBoot,真的及其简化了spring的开发,省去了很多麻烦,感谢黑马程序员的课程,看的是黑马程序员SpringBoot2全套视频教程,讲的很好,在此总结一下,以便以后复习,
内容很多,请善用Ctrl+f
org.springframework.boot
spring-boot-starter-parent
2.6.0
org.springframework.boot
spring-boot-starter-web
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
注解源码其实就可以看到(虽然没怎么看懂),直接映入眼帘的便是@ComponentScan
这个注解,即spring注解开发中配置注解配置类,不配置这个就默认扫描当前包及其子包,所以那些controller,service.mapper都要放在当前包及其子包及其子包中,上面还配置了@SpringBootConfiguration
,点击进去可知这个注解就配置了@Configuration
说明这就是个spring的配置类AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
,boot中SpringApplication.run(class,args)
将配置类的字节码文件传过去,所以像是spring注解开发,并把服务器也启动了起来, 它的返回值也是spring上下文类型ConfigurableApplicationContext,run方法有重载形式,不一定需要传入args,不传args表示不接受临时变量,可能更安全点.spring-boot-starter-web
中包含spring-boot-starter-tomcat
,而tomcat的starter中又包含tomcat-embed-core
,即内嵌的tomcat代码,tomcat也是由java语言写的,boot就将tomcat运行过程封装成一个对象,并由spring管理,至于内部操作一定很复杂如果排除了tomcat,但没有添加其他服务器,web程序则无法启动
实例
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-jetty
boot配置文件在maven中resource目录中,默认名称为application.properties
,idea中提供了强大的提示功能,且idea是根据你所导入的技术来提示,没导入的技术不会提示,你也可以从spring官网中查找相应的功能配置(需了解的是官网中为全部配置属性且格式为properties
配置格式),步骤为:
配置文件格式分为三种
格式 | server.port=8081 | |
---|---|---|
properties格式 | 传统格式/默认格式 | server.port=8081 |
yml格式 | 主流模式 | server: port: 8081 |
yaml格式 | - | 与yml格式一样 |
配置文件名称这个概念感觉比较重要,但是比较简单,后面也会用到,在这里简单介绍一下,配置文件名称默认为application,即boot项目运行时就会在resource中找名application
的配置文件,不管是什么格式的,内容合并依靠上面的优先级决定。
更专业的解释是,格式为:application-{profile}.properties,其中 {profile} 对应你的环境标识(后面解释)
而在实际开发中如果某开发人员离职,如果它知道配置文件名称,可能会导致不安全,所以给配置文件起别名会使项目更安全
方法1: 在运行时添加临时变量属性,即:–spring.config.name=ebank
不用后缀名,只要是这个名称的配置文件都可
方法2: 在运行时添加临时变量,配置文件路径(可多个,有逗号隔开,级别谁后定义谁级别高)
即:–spring.config.location=d://adc.properties[这是全路径名]
--spring.config.location=classpath:/abc.properties,classpath:/edank.yml[这是类路径]
boot启动时就会找相应的配置文件,在cmd中运行jar包时,传递参数也为–spring.config.name=ebank(还是两个减号,一个减号可能会识别命令行的参数,具体也不懂,知道的可以评论一下,蟹蟹)
yml格式优点:
1.格式比较有层次感,
2.不会有太多的冗余
3.配置数组简单
数据前面要加空格与冒号隔开
元素数组定义
likes:
- book
- eat
- program
-122 #减号后面要用空格不然不识别
元素数组缩略版定义
likes2: [book,eat,program]
对象数组定义
users:
- name: tom
age: 15
- name: lucy
age: 16
users2:
-
name: tom
age: 15
-
name: lucy
age: 16
对象数组缩略版定义(json)
users3: [{name:tom,age=15},{name:lucy,age=16}]#name:tom中冒号不用加空格
Environment
类型new String(name.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)
@PropertySource(value= "classpath:pro/application.properties", encoding="UTF-8")
,但是这样好像有点问题,试了多次,在此总结一下,如有错误,还请斧正。使用这个注解时如果直接是resource目录下的application.properties
,试了好多遍都不行,只能想到在加载这个配置文件时其他配置(注解)替代了@PropertySource的配置,但是换到其他目录上去或更改为其他名字便可以解决乱码#案例
#properties这样定义对象数组
users[0].name="张三"
users[0].age=16
users[1].name="王五"
users[1].age=18
#properties定义对象,这样也可以封装成User对象
user.name=王五
user.age=17
user.like[0]=book
user.like[1]=吃
user.like[2]=sleep
#相比yml是不是很麻烦,所以主流为yml格式
# yml中可以这样配置
baseDir: c:\Windows
tempDir: ${baseDir}\temp #这样tempDir实际上就是 c:\Windows\temp
#properties中可以这样配置
baseDir=c:\Windows
tempDir=${baseDir}\temp #与yml效果一样
多环境开发即多个环境分别为:生产环境Production,开发环境Development,测试环境Test, 在不同环境下配置也不尽相同,最明显的就是数据库配置、端口配置等,处理办法有如下
---
(三个减号)作为分隔线,可以配置若干个,但是不管配置几个环境,最下面一个没有定义环境名称的环境就是默认环境,配置的为公共配置(指定引用环境),在boot启动时会被加载,我们默认将最上面一个环境设置为默认环境,即:不设置环境名,只设置环境名,不设置引用哪个属性名,则不会执行任何环境,只会执行默认环境(properties配置文件不支持方法一)spring: profiles: 名
,2). spring: config: activate: on-profile: 名
,这两种视频上讲了,第三种好像也可以(已试),3). profile:名
。spring: profile: active: 环境名
。# 默认环境(不定义则为最后一个环境)
#配置公共配置
#使用哪个环境
spring:
profiles:
active: dev
---
# 生产环境
spring:
profiles: pro
server:
port: 80
---
# 开发环境
#spring:
# profiles: dev
#profiles: dev
spring:
config:
activate:
on-profile: dev
server:
port: 8001
---
# 测试环境
spring:
profiles: test
server:
port: 8081
总配置名称-环境标识
的配置文件,不管格式(这里用到了上面配置文件名称知识)spring=profile.include=${profile},${profile}
,**优点:**可以直接使用,不需要action启动相应的环境(没有多环境),include的配置直接被引用, **缺点:**不可以很好的与action相关联,切换环境时,即action改变时,必须手动改变相应的配置文件。spring.profile.group.*=${profile},${profile}
,**优点:**可以很好的与action关联,当action改变切换环境时,可以设定相对应的属性来指定配置文件,如总配置文件内容所示。**缺点:**只能联合action一起使用,不能在不引用action是导入配置文件 # 配置文件内容中不需要配置名称,只需要名称格式为:[此文件名-调用名称](properties也是这样)
# 在开发中默认还要分多个功能[模块],一般将每个功能[模块]的配置文件都分开书写,即application-devDB,application-devMVC
# 可以include/group引用其他配置文件
spring:
profiles:
active: dev
# include每次切换环境都要手动改变配置文件名称
# include: devDB,devMVC
# springBoot2.4开始使用group简化include
group:
"dev": devDB,devRedis,devMVC
"pro": proDB,proRedis,proMVC
"test": testDB,testRedis,testMVC
其他配置文件内容application-pro.yml内容
# 生产环境
server:
port: 80
application-dev.yml内容
# 开发环境
server:
port: 8001
application-test.yml内容
# 测试环境
server:
port: 8081
application-devMVC.yml内容
# 开发环境中MVC相关配置
MVCdata: devMVC
环境配置
dev
环境配置
,在加载环境所对应的内容并顺序加载dev
,devDB,devMVC环境配置
,最后顺序加载group里的,相同的配置文件不会再次加载。dev
dev
,devRedis在实际开发中,程序员开发时需要一套配置,而项目经理也需要一套配置,运维人员也需要一套配置,运维组长甚至也需要一套,如果相互的改动,不知道什么时候就乱了,于是boot就帮我们解决这一问题,他将配置文件分为四级,级别大的配置就替换级别小的配置,不同配置都会保留,这样也使各级人员互不干扰,减少冗余配置。
application
,不管是什么格式(yml,properties,yaml)的配置文件,如果想设置其他名称可以参考上面的配置文件名称中的方法一,java -jar 包 --spring.config.name=ebank
名称.
配置文件,一般是程序员使用的配置名称.
配置文件,一般为项目经理使用名称.
配置文件,不过使用idea工具时,和项目文件夹在同一目录下的名称.
配置文件也是三级配置,不过开发过程不会遇到名称.
配置文件, 和三级配置一样,使用idea工具项目文件夹就相当于项目jar包。idea具有强大的提示功能,但有时候我们自定义一些配置文件时,会出现编写配置不提示的问题,可以通过下面步骤来解决,**提醒: **这是设置只是让idea知道这是springboot的配置文件,来进行提示,项目中想引用配置文件还需要配置include或group。
步骤如下:1). 点击(1)处图标,或点击工具栏File → Project Structure 。2). 点击Facets。3). 选择项目。4)点击右侧的选项,是boot叶子图标变绿(表示可点击)。5). 点击boot标志叶子。6). 点击加号。7).找到你要提示的配置文件。8). 点击ok,
9).这里有特殊情况,情况一: 原先有配置文件,第(9)步位置处有值就不用了操作直点OK,情况二: 没有任何配置文件,你选择了配置文件,之后在第(9)步位置上输入名称(看图二),如果没有提示可能你将application.properties文件()没有删除而是移动到某个文件夹,你填application的相关配置文件就行,具体还是百度吧=_=
利用springboot的starter,可以很快的集成某项技术
@Mapper//让容器识别到完成自动导入
public interface BookMapper {
@Select("select * from book_tb where id = #{id}")
public Book findById(int id);
}
2.定义配置文件spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: 1234
3.在测试类中测试 driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
当springboot将至2.4.1时执行mybatis报错The server time zone value '�й���ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the 'serverTimezone' configuration property) to use a more specific time zone value if you want to utilize time zone support.
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
2.利用MP简化mapper@Mapper//需要添加模块泛型
public interface BookMapper extends BaseMapper<Book> {}
3.配置文件进行相关配置spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 1234
@TableName("表名")
注解@Data
@TableName("book_tb")
public class Book {
Integer id;
String type;
String name;
String description;
}
# 模型类不需要配置注解@TableName
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
@Autowired
BookMapper bookMapper;//这里可能会
@Test
void contextLoads() {
System.out.println(bookMapper.selectById(1));//根据查询查询
System.out.println(bookMapper.selectList(null));//根据添加查询
}
驼峰命名法: 当变量名或函数名是由一个或多个单词连结在一起,而构成的唯一识别字时,第一个单词以小写字母开始;从第二个单词开始以后的每个单词的首字母都采用大写字母, 例如:myFirstName、myLastName
下划线命名法: 每个单词都为小写且单词之间以下划线作为分隔, 例如:my_first_ame、my_last_name
驼峰命名法转换(自己起的): 数据库字段使用下划线命名法,即:user_name, java实体类属性名使用驼峰命名法,
CRUD时,数据库下划线字段与实体类驼峰字段相互转换
案例:
数据库字段: id,user_name
实体类属性: id,userName,user_name
操作: 从数据库读取数据
结果: 会把查询的结果集字段重命名为为userName,从而导致实体类user_name属性没有值
输出实体: id=1,userName=Tom,user_name=null
在使用MybatisPlus时默认为默认打开驼峰命名法转换的,简单看了下spring构建Bean的源码MybatisSqlSessionFactoryBean
,在buildSqlSessionFactory
方法中有个MybatisConfiguration
类型参数,在一开始会判断是否配置了configuration和configLocation,如果都没有(其他情况暂时不看)则直接使用空参构造创建MybatisConfiguration
,而空参构造方法中,mapUnderscoreToCamelCase
默认值为true;final MybatisConfiguration targetConfiguration;
// TODO 使用 MybatisXmlConfigBuilder 而不是 XMLConfigBuilder
MybatisXMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
// TODO 使用 MybatisXMLConfigBuilder
xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
// TODO 使用 MybatisConfiguration
targetConfiguration = new MybatisConfiguration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
核心文件中配置
spring中配置(直接使用MP同理):
springboot配置: mybatis-plus.configuration.map-underscore-to-camel-case=false
@TableField(下划线字段名)
,这个注解只有用MP时才会读取,即: 使用SqlSessionFactoryBuilder
构建时不会生效@TableField(value = "user_name") private String userName; //这个注解只有用MP时才会读取
@TableField
也可以解决实体类中某属性有值且在数据库不存在字段,可以使用@TableField(exist=false)
来解决,不使用此注解可能出现插入数据是报错.继承BaseMapper<泛型>
方法简解
//Mapper接口
@Mapper//需要添加模块泛型
public interface BookMapper extends BaseMapper<Book> { }
//模型类
@Data
@TableName("book_tb")
public class Book {
private Integer id;
private String type;
private String name;
private String description; }
Book book = new Book();
bookMapper.insert(book);
@TableId(type = IdType.AUTO)
Caused by: org.apache.ibatis.reflection.ReflectionException: Could not set property 'id' of 'class com.muyu.pojo.Book' with value '1467064655366000642' Cause: java.lang.IllegalArgumentException: argument type mismatch
mybatis-plus:
global-config:
db-config:
id-type: auto
bookMapper.deleteById(15);
Book book = new Book();
book.setId(15);
book.setName("mapper测试修改");
bookMapper.updateById(book);
Book book = bookMapper.selectById(12);
bookMapper.selectList(QueryWrapper);
参数为查询封装器类型
QueryWrapper<Book> qw = new QueryWrapper<Book>();
qw.like("name", "spring");
List<Book> books = bookMapper.selectList(qw);
//查询所有
List<Book> books = bookMapper.selectList(null);
String search = "spring";
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
//如果search为空可以手动添加if进行判断
//if (Strings.isNotEmpty(search))qw.like(Book::getName, search);
//也可是使用like方法的重载like(boolean, R , object)
lqw.like(Strings.isNotEmpty(search),Book::getName, search);//org.apache.logging.log4j.util.Strings;
bookMapper.selectList(lqw);
IPage page = new Page(1,5);
bookMapper.selectPage(page,null);
/*要想分页生效需要配置拦截器,*/
System.out.println(page.getRecords()); // 包含数据
System.out.println(page.getPages()); // 共几页
System.out.println(page.getTotal()); // 数据总数
//此类要放在引导类的包及其子包下
//定义与Mp相关的一些配置
//方式一:
@Configuration
public class MPConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//这时候拦截器还是空的
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());//配置内部拦截器
return interceptor;
}
}
//方式一:
@Configuration
public class MPConfig {
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor() {
return new PaginationInnerInterceptor();//直接配置分页
}
}
使用MP简化Mapper层实现也可以通过注解或xml配置进行开发,mybatis与MP都可以这样配置
@Select("SELECT * FROM user WHERE #{id}")
List<User> findOne(int id);
*Mapper.xml
,springboot会自动加载mapper包及其子包下的xml映射文件,映射文件名称无所谓只要配置
,即使存在两个namespace一样的映射文件,也可以在boot配置文件中配置mybatis-plus.mapper-locations=classpath:*Mapper.xml
,mybatis.mapper-location好像不行。
和
都可以,但是如果配置包那种如:
和
就会报错,Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [sqlMapConfig.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.StringIndexOutOfBoundsException: String index out of range: -110
... 70 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.ibatis.session.SqlSessionFactory]: Factory method 'sqlSessionFactory' threw exception; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [sqlMapConfig.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.StringIndexOutOfBoundsException: String index out of range: -110
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 88 more
Caused by: org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [sqlMapConfig.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.StringIndexOutOfBoundsException: String index out of range: -110
... 89 more
Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.StringIndexOutOfBoundsException: String index out of range: -110
... 102 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -110
#mybatis:配置好像不行
mybatis-plus:
configuration:
map-underscore-to-camel-case: false
mapper-locations: classpath:UserMapper.xm #配置Mybatis映射文件
type-aliases-package: com.muyu.pojo #给相应包起别名
# config-location: classpath:sqlMapConfig.xml #配置核心配置类
#mapperLocations:它表示我们的Mapper文件存放的位置,当我们的Mapper文件跟对应的Mapper接口处于同一位置的时候可以不用指定该属性的值
# configLocation:用于指定Mybatis的配置文件位置。如果指定了该属性,那么会以该配置文件的内容作为配置信息构建对应的SqlSessionFactoryBuilder,但是后续属性指定的内容会覆盖该配置文件里面指定的对应内容
# typeAliasesPackage:它一般对应我们的实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。多个package之间可以用逗号或者分号等来进行分隔(value的值一定要是包的全)
# typeAliases:数组类型,用来指定别名的。指定了这个属性后,Mybatis会把这个类型的短名称作为这个类型的别名,前提是该类上没有标注@Alias注解,否则将使用该注解对应的值作为此种类型的别名(value的值一定要是类的完全限定名)
# plugins:数组类型,用来指定Mybatis的Interceptor
# typeHandlersPackage:用来指定TypeHandler所在的包,如果指定了该属性,SqlSessionFactoryBean会自动把该包下面的类注册为对应的TypeHandler。多个package之间可以用逗号或者分号等来进行分隔
# typeHandlers:数组类型,表示TypeHandler
# mapper: #不知道是什么,暂且挌下
# mappers: com.pinyu.miniprogram.mysql.mappers.BaseMapper
# identity: mysql
//使用标准接口开发,前面加I
public interface IBookService extends IService<Book> {
//追加的操作与原始操作通过名称区分
IPage<Book> getPage(int currentPage, int pageSize,Book book);
}
/* ServiceImpl需要定义两个泛型,<引用的实现类,模型类> */
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements IBookService {
@Autowired
BookMapper bookMapper;
@Override
public IPage<Book> getPage(int currentPage, int pageSize,Book book) {
IPage page = new Page(currentPage,pageSize);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Strings.isNotEmpty(book.getType()), Book::getType, book.getType());
lqw.like(Strings.isNotEmpty(book.getName()), Book::getName, book.getName());
lqw.like(Strings.isNotEmpty(book.getDescription()), Book::getDescription, book.getDescription());
bookMapper.selectPage(page, lqw);
//如果查询完后当前页大于总页数(这样表明没有数据),就在执行一次,保证有数据,
if (currentPage > page.getPages()) {
page.setCurrent(page.getPages());
bookMapper.selectPage(page, lqw);
}
return page;
}
Book book = new Book();
bookService.save(book);
bookService.removeById(17);
Book book = new Book();
book.setId(15);
book.setName("Service测试数据123");
bookService.updateById(book);
Book byId = bookService.getById(12);
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
lqw.like(Book::getName, "spring");
List<Object> objects = bookService.listObjs(lqw);
//查询所有,两种方法
List<Object> objects1 = bookService.listObjs();
List<Object> objects2 = bookService.list();
IPage<Book> page = new Page<Book>(2,5);
bookService.page(page);
//分页条件查询
LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
bookService.page(page,lqw);
功能 | Mapper层 | Service层 |
---|---|---|
插入方法 | insert(T) |
save(T) |
根据id删除 | deleteById(Serializable) |
removeById(Serializable) |
根据id更改 | updateById(T) |
updateById(T) |
根据id查询 | selectById(Serializable) |
getById(Serializable) |
查询全部 | selectList(null) |
list() , listObject() ,不能 listObject(null) ,需要强转类型 |
条件查询 | selectList(Warpper |
listObjs(Warpper |
分页查询 | selectPage(E, null) |
|
分页条件查询 | selectPage(E, Warpper |
|
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.6version>
dependency>
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 1234
type: com.alibaba.druid.pool.DruidDataSource #配置数据源类型
Druid整合专用配置spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: 1234
@Ignore
表示不执行测试).打包是需要添加boot打包插件,才可以打包成一个可独立运行的jar包,运行通过java -jar
指令
org.springframework.boot
spring-boot-maven-plugin
***.jar没有主清单属性
错误,