SpringBoot基本知识
一、简介
1、spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
2、springboot底层就是基于spring注解开发,实现了自动装配原理。
二、模拟Spring完全注解配置
@Configuration:作用与类上,将当前类声明为一个配置类,相当于一个xml 配置文件
@ComponentScan:自动扫描指定包下标注有@Repository,@Service,@Controller
@Component:注解的类并由Ioc 容器进行实例化和维护
@Bean::作用于方法上,相当于xml 文件中<bean> 声明当前方法返回值为一个bean
@Value:获取properties 文件指定key value值
注入Bean对象注解:
@AutoWired:Spring 官方提供注解
@Inject:JSR-330 提供注解(标准制定方)
@Resource:JSR-250 提供注解
声明Bean对象注解
@Component:组件 没有明确规定其角色,作用在类级别上声明当前类为一个业务组件,被Spring Ioc容器维护;
@Service:在业务逻辑层(Service 层)类级别进行声明;
@Repository:在数据访问层(dao 层) 类级别声明;
@Controller:在展现层(MVC) 使用 标注当前类为一个控制器
- 导入spring依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
- spring注解配置类
package com.zwf.config;
import com.zwf.anno.MyCompentScan;
import com.zwf.dao.BaseDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 10:33
*/
//配置类
//开启扫描注解包
@ComponentScan(value = "com.zwf")
//加载properties文件
@PropertySource(value = {"classpath:db.properties"})
@Configuration
//@MyCompentScan(value = "com.zwf") 使用自定义组合注解
public class SpringConfig {
@Value("${jdbc.user}")
private String user;
@Value("${jdbc.pwd}")
private String pwd;
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
//加载数据连接池信息
//@Bean 每个方法名就是Bean的id值
// @Bean
// public BaseDao loadDataSourceConfig(){
// System.out.println(user+","+pwd+","+driver+","+url);
// return new BaseDao();
// }
}
- 测试代码
package com.zwf.dao;
import org.springframework.stereotype.Repository;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 10:42
*/
@Repository
public class BaseDao {
public void test(){
System.out.println("baseDao测试类执行……");
}
}
package com.zwf;
import com.zwf.config.SpringConfig;
import com.zwf.dao.BaseDao;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 10:43
*/
public class SpringStart {
public static void main(String[] args) {
//获取spring配置上下文环境
ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);
// BaseDao baseDao = (BaseDao)ac.getBean("loadDataSourceConfig");
BaseDao baseDao1 =(BaseDao) ac.getBean("baseDao");
baseDao1.test();
}
}
三、模拟SpringMVC完全注解配置
- 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
- 编写MVC配置类
package com.zwf.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 14:59
*/
@Configuration
@EnableWebMvc //开启WebMVC支持
@ComponentScan("com.zwf.Controller")
public class MvcConfig extends WebMvcConfigurationSupport {
//配置视图解析器
@Bean
public InternalResourceViewResolver getInternal(){
InternalResourceViewResolver intern=new InternalResourceViewResolver();
intern.setPrefix("/WEB-INF/page/");
intern.setSuffix(".jsp");
return intern;
}
//若要开启拦截器 extends WebMvcConfigurationSupport实现其他组件配置
@Override
protected void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
}
//放行静态资源文件
@Override
protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable(); //不进行处理 直接响应到客户端
}
}
- 编写Controller进行代码测试
@Controller
public class PageController {
@RequestMapping("/page")
public String getPageDemo(){
return "mvc";
}
}
- 配置获取配置上下文环境加载(相当于web.xml文件的配置,若配置以下代码,可删除web.xml文件)
public class MvcStarter implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//加载配置文件
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
//注册配置文件类对象
context.register(MvcConfig.class);
//设置servlet容器对象
context.setServletContext(servletContext);
//上下文对象添加servlet 并对servlet配置DispatcherServlet映射 并启用优先加载
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.addMapping("/");
dispatcher.setLoadOnStartup(1);
}
}
- 进行Tomcat测试(并访问请求路径)
四、SpringBoot快速入门
- 配置Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<groupId>com.zwf</groupId>
<artifactId>springBootDemo1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--引入spring父项目路径 实现继承父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
</parent>
<!--引入springboot web包 thymeleaf freemark -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--日志文件导入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置核心yml文件(application.yml)或者使用application.properties
#开发环境 定义项目根路径
server:
port: 9999
servlet:
context-path: /springboot
#配置模板引擎存放目录
spring:
freemarker:
charset: UTF-8
template-loader-path: "classpath:/views/"
suffix: ".ftl"
#配置控制台输出格式和级别 输出文件路径
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n"
level: debug
file:
path: "D:\\bigCode\\springBootDemo1\\log"
level:
root: debug
- 配置MVC三层包,并使用注解,让IOC容器管理
package com.zwf.controller;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 17:54
*/
@Controller
public class DirectPage {
@RequestMapping("/page1")
public String getPage(){
return "page"; //跳转到配置的视图解析器文件中
}
@RequestMapping("/index")
public String getPage2(Model model){
model.addAttribute("message","hello spring");
return "index";
}
}
- 编写springboot启动类(此类位置必须与Controller包同级)
package com.zwf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-06 16:51
*/
@SpringBootApplication
public class SpringApplicationStarter extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringApplicationStarter.class);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringApplicationStarter.class);
}
}
五、springboot依赖分析
1、springboot依赖包,是一个全新的组合依赖包,每个starter依赖包下集成了大量的第三方组件包环境整合。
2、引入依赖时,只需要在引入spring-boot-starter-***依赖坐标,比如引入thymeleaf依赖:spring-boot-starter-thymeleaf。
3、每个包都继承spring-boot-starter-parent依赖,实现了子工程继承父工程的依赖包。
- 常见的springboot依赖(或者在https://mvnrepository.com找依赖坐标)
六、多环境切换
#开发环境 定义项目根路径 #配置thymeleaf 和环境名称 或者以application-dev.yml定义
server:
port: 9999
servlet:
context-path: /springboot
#配置模板引擎存放目录
spring:
freemarker:
charset: UTF-8
template-loader-path: "classpath:/views/"
suffix: ".ftl"
profiles: dev
#配置控制台输出格式和级别 输出文件路径
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n"
level: debug
file:
path: "D:\\bigCode\\springBootDemo1\\log"
level:
root: debug
#########################################################################################
#测试环境
server:
port: 5555
#配置thymeleaf 和环境名称 或者以application-test.yml定义
spring:
thymeleaf:
cache: false
encoding: UTF-8
profiles: test
#配置logging日誌文件
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n"
level: debug
file:
path: "."
name: "springboot.log"
level:
root: debug
#######################################application.yml文件中启用环境#######################
#切换开发环境
spring:
profiles:
active: dev
七、配置日志
#配置logging日誌文件
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger- %msg%n"
level: debug
file:
path: "."
name: "springboot.log"
level:
root: debug
注意:logging.file.path与logging.file.name不能同时配置,logging.file.path只配置路径,在该路径下默认生成springboot.log文件,而name值可以配置路径到自定义文件名,或者只配置文件名,默认生成在类路径下。
八、springboot集成页面模板引擎
常见的模板引擎有thymeleaf和freeMarker,官方推荐使用thymeleaf模板引擎。
- 导入依赖
<!--引入freemarker模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--引入thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
IDEA连续两次按下shift,可以通过查找thymeleafproperties.class类来查看模板引擎的配置属性,在yml文件中进行配置。
所有的set方法属性,都可以在yml文件中设置,当然要加上spring.thymeleaf前缀,前缀在***properties.class类注解上定义。
- 核心配置文件
#配置thymeleaf 和环境名称
spring:
thymeleaf:
cache: false
encoding: UTF-8
prefix: classpath:/views/
九、Springboot静态资源访问
找到适配器内部类中的addResourceHandles方法
然后进行this.resourceProperties类中,找到Resources静态内部类
- 总结
从以上源码分析可以看出,在/webjars/**, /META-INF/resources/webjars/, /META-INF/resources/, /resources/, /public/, /static/目录下存放静态文件,可以被外部访问。
十、springboot项目打包
-
打成jar包(默认不配置packing标签就是jar包)
-
导入maven编译插件依赖和Tomcat依赖
<!--打包时排除springboot内置Tomcat,让内置Tomcat只在编译时有效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--引入spring-boot-Maven插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 使用命令打包
clean completion package -Dmaven.test.skip=true //跳过测试进行打包
- Dos窗口中执行
java -jar jar包名字
-
打成war包
-
导入maven依赖配置
<packaging>war</packaging>
<!--打包时排除springboot内置Tomcat,让内置Tomcat只在编译时有效-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--引入spring-boot-Maven插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 再配置springboot启动类 继承
SpringBootServletInitializer
类重写configure方法。
@SpringBootApplication
public class SpringApplicationStarter extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringApplicationStarter.class);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringApplicationStarter.class);
}
}
- 再使用maven命令打包
clean completion package -Dmaven.test.skip=true //跳过测试进行打包
或者直接
mvn package
- 再把打包好的war包放入Tomcat webapp目录下发布,启动Tomcat,访问路径。(打包好的war包放在target目录下,访问项目以Tomcat端口为准,springboot端口失败,项目根路径就是你的war包文件名字!)
十一、springboot整合mybatis
- 步骤一、导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--开启热部署支持-->
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 配置springboot核心配置文件
server:
port: 9999
servlet:
context-path: /ssm
# 配置数据库连接信息
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root@123456
url: jdbc:mysql://127.0.0.1:3306/springbootproject?useUnicode=true&characterEncoding=utf8&serverTimeZone=Asia/shanghai&useSSL=true
type: com.mchange.v2.c3p0.ComboPooledDataSource
#解决swagger2与springboot版本不兼容问题
mvc:
pathmatch:
matching-strategy: ant_path_matcher
#开启热部署 作用于Java包下
devtools:
restart:
enabled: true
additional-paths: src/main/java
#核心配置文件加载缓存xml配置文件
cache:
ehcache:
config: classpath:ehcache.xml
#配置mybatis 开启驼峰命名
mybatis:
mapper-locations: classpath:/mappers/*.xml
type-aliases-package: com.zwf.springboot.pojo
configuration:
map-underscore-to-camel-case: true
#分页插件支持mysql方言 因为每个数据库的分页参数不同
pagehelper:
helper-dialect: mysql
logging:
level:
com:
zwf:
springboot:
dao: debug
file:
path: "."
name: "spring.log"
- 编写dao层接口
package com.zwf.springboot.dao;
import com.zwf.springboot.pojo.UseInfor;
import com.zwf.springboot.vo.UserQuery;
import java.util.List;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:20
*/
public interface UserInforDao {
public List<UseInfor> queryAllUserInfor();
public List<UseInfor> queryUserById(Integer id);
public Integer saveUser(UseInfor useInfor);
public Integer updateUser(UseInfor useInfor);
public Integer deleteUserById(Integer id);
public List<UseInfor> queryUserByParams(UserQuery userQuery);
}
- 编写mapper映射配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zwf.springboot.dao.UserInforDao">
<select id="queryAllUserInfor" resultType="useinfor">
select * from userinfo
</select>
<select id="queryUserById" parameterType="int" resultType="useinfor">
select * from userinfo where id=#{id}
</select>
<insert id="saveUser" parameterType="useinfor">
insert into userinfo values (#{id},#{username},#{password})
</insert>
<update id="updateUser" parameterType="useinfor">
update userinfo set username=#{username},password=#{password} where id=#{id}
</update>
<delete id="deleteUserById" parameterType="int">
delete from userinfo where id=#{id}
</delete>
<select id="queryUserByParams" parameterType="com.zwf.springboot.vo.UserQuery" resultType="useinfor">
select * from userinfo
<where>
<if test=" null !=username and username !=''">
username=#{username}
</if>
</where>
</select>
</mapper>
- springboot启动项开启mapper接口扫描
package com.zwf.springboot;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 20:51
*/
@SpringBootApplication
@MapperScan(basePackages = {"com.zwf.springboot.dao"})
@EnableCaching //开启缓存
public class SpringBootApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationStarter.class);
}
}
十二、SpringBoot整合Swagger2
- 在快速入门案例基础上导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
- 配置swagger2配置类
package com.zwf.springboot.config;
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-08 11:56
*/
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {
@Bean
public Docket getDocketConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.zwf.springboot.controller"))
.paths(PathSelectors.any())
.build();
}
public ApiInfo apiInfo(){
//编辑作者信息
Contact concat=new Contact("小曾","https://www.baidu.com","[email protected]");
return new ApiInfoBuilder().description("这是一个测试文档").
title("用户接口文档")
.version("2.0")
.license("许可证号")
.contact(concat)
.build();
}
}
如果版本不兼容,我们需要在springboot核心配置文件中配置
#解决swagger2与springboot版本不兼容问题
mvc:
pathmatch:
matching-strategy: ant_path_matcher
- 常用的注解如下:
- 使用注解声明文档
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:43
*/
@Api(tags = {"这是一个用户接口测试类"})
@RestController
public class FindAllController {
@Resource
private UserInforService userInforService;
@ApiOperation(value = "查询所有用户信息")
@GetMapping("/queryAll")
public List<UseInfor> findAll(){
return userInforService.findAll();
}
@ApiOperation(value = "根据id查询用户信息")
@ApiImplicitParam(name = "用户id",required = true,paramType = "Integer")
@GetMapping("/queryAll/{id}")
public ResultInfo findUserById(@PathVariable Integer id){
return userInforService.findUserById(id);
}
@ApiOperation(value = "保存用户信息")
@PostMapping("/save")
public ResultInfo saveUser(@RequestBody UseInfor useInfor){
return userInforService.saveUseInforByUse(useInfor);
}
@ApiOperation(value = "根据Id更新用户信息")
@ApiImplicitParam(name = "用户对象",required = false,paramType = "UseInfo")
@PutMapping("/update")
//@Valid开启字符校验
public ResultInfo updateUserById(@RequestBody @Valid UseInfor useInfor){
return userInforService.updateUseInforByUse(useInfor);
}
@ApiOperation(value = "根据ID删除用户信息")
@ApiImplicitParam(name = "用户id",required = true,paramType = "Integer")
@DeleteMapping("/delete/{id}")
public ResultInfo deleteUserById(@PathVariable Integer id){
return userInforService.deleteUseInforByUse(id);
}
@ApiOperation(value = "分页查询用户信息")
@GetMapping("/queryPage")
public PageInfo<UseInfor> queryPage(UserQuery userQuery){
return userInforService.queryPageByParams(userQuery);
}
}
package com.zwf.springboot.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:20
*/
@ApiModel(value = "用户信息表标题",description = "用户信息实体类")
public class UseInfor implements Serializable {
@ApiModelProperty(value = "用户ID")
private Integer id;
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "邮箱")
private String Email;
public UseInfor() {
}
public UseInfor(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public String getEmail() {
return Email;
}
public void setEmail(String email) {
Email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UseInfor{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", Email='" + Email + '\'' +
'}';
}
}
十三、Springboot热部署
热部署是一个比较常用的功能,因为我们在开发中经常要调试修改项目的时候,需要重启Tomcat,很费时间,有了热部署,我们就不用重启Tomcat,IDEA会自动重新构建,只需要我们对IDEA窗口失焦或者alt+F
9重新build,等待5秒,修改后的代码就会生效。当然我们在使用IDEA的时候,新版IDEA和老版IDEA有所不同,要注意配置IDEA的环境。
一、配置IDEA的环境
- 先开启seeting---->compiler中的功能
- 然后开启注册插件
如果是老版IDEA需要开启以下:
如果是新版IDEA需要开启以下配置:
二、导入依赖坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!--热部署工具被继承后 不再向下传递-->
<optional>true</optional>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--开启热部署支持-->
<fork>true</fork>
</configuration>
</plugin>
- 最后就是开启Tomcat时,修改代码,对IDEA窗口失去焦点等待5秒,会重新加载,或者按住alt+F9重新构建项目,代码生效!
十四、分布式缓存(Ehcache)
缓存的作用是提升系统的查询效率,对于一些不常改变的数据,我们可以设置缓存,对于经常改变的数据就不要设置缓存。
整合步骤
- 先导入依赖坐标
<!--开启缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache 坐标 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
- 配置缓存的核心配置
<!--缓存持久化存放的位置-->
<ehcache name="mycache">
<diskStore path="D:\springbootCache\cache"/>
<!--缓存设置的一些参数意思-->
<!--
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统宕机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。
仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置
时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。
最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,
默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists
between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是
30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据
指定的策略去清理内存。
默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使
用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进
先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,最近最少被访问的。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,
当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳
离当前时间最远的元素将被清出缓存。
-->
<!--默认缓存设置-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<!--设置自定义缓存设置-->
<cache
name="users"
eternal="false"
maxElementsInMemory="100"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="300"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
- springboot核心配置加载ehcache缓存配置
spring:
#核心配置文件加载缓存xml配置文件
cache:
ehcache:
config: classpath:ehcache.xml
- springboot启动类开启缓存
package com.zwf.springboot;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 20:51
*/
@SpringBootApplication
@MapperScan(basePackages = {"com.zwf.springboot.dao"})
@EnableCaching //开启缓存
public class SpringBootApplicationStarter {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationStarter.class);
}
}
- 注解使用缓存
@Cacheable(value = "users") //使用缓存 value值对应ehcache配置文件的name属性值。
public List<UseInfor> findAll(){
return userInforService.findAll();
}
@Cacheable(value = "users",key = "#id") //key对应缓存中存取的值 要与参数名对应 下次查询直接从缓存读取
//paramType:表示参数放在哪个地方
@ApiImplicitParam(name = "用户id",required = true,paramType = "path")
@GetMapping("/queryAll/{id}")
public ResultInfo findUserById(@PathVariable Integer id){
return userInforService.findUserById(id);
}
@Transactional(readOnly = true)
//开启缓存
@Cacheable(value = "users",key = "#userQuery.username+'-'+#userQuery.pageNum+'-'+#userQuery.pageSize")
@ApiOperation(value = "分页查询用户信息")
@GetMapping("/queryPage")
public PageInfo<UseInfor> queryPage(UserQuery userQuery){
return userInforService.queryPageByParams(userQuery);
}
@Cacheable(value = "users",key = "#id")
@GetMapping("/queryAll/{id}")
public ResultInfo findUserById(@PathVariable Integer id){
return userInforService.findUserById(id);
}
-
注意:使用缓存的时候,实体类要实现序列化接口!
-
常用的其他注解
十五、springboot整合QuartZ(定时任务)
常用的定时任务有spring task,但是spring task不支持分布式,而QuartZ支持分布式,所以我们在分布式项目中,经常使用QuartZ作为定时任务调度。定时任务调度经常使用在定时发送邮件、发送短信验证等。
- 导入依赖
<!--开启定时调度任务 quartz-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 编写任务类,定义执行的任务
package com.zwf.springboot.quartz;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-08 19:30
*/
//定义一个工作类 编写定时执行的任务 比如发送邮件 发送验证码 等 要实现QuartZ中的job接口以及execute方法
public class QuartzProject implements Job {
//生成日志文件
private Logger logger= LoggerFactory.getLogger(QuartzProject.class);
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//获取触发器
TriggerKey key = jobExecutionContext.getTrigger().getKey();
//日志打印
logger.info("所属组:"+key.getGroup()+"名字:"+key.getName()+"执行时间:"+format.format(new Date()));
}
}
- 定义配置类
package com.zwf.springboot.config;
import com.zwf.springboot.quartz.QuartzProject;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-08 17:29
*/
@Configuration
public class QuartzConfig {
@Bean
public JobDetail getJobDetail(){
//开启job接口实现类 注入newJob中 storeDurably存储持久化
return JobBuilder.newJob( QuartzProject.class).storeDurably().build();
}
@Bean
//自定义触发器1
public Trigger getTrigger(){
//一秒执行一次 永久执行下去 定义简单的计划类
SimpleScheduleBuilder schedule=SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1).repeatForever();
//触发器构建类 实例化触发器 取名 把定义的任务设置在触发器规则中 进行构建
return TriggerBuilder.newTrigger()
.withIdentity("trigger1","group1")
.withSchedule(schedule)
.forJob(getJobDetail()).build();
}
@Bean
//自定义触发器2
public Trigger getTrigger2(){
//设置corn计划类 使用corn表达式 定义CronScheduleBuilder corn表达式可以通过百度查找
return TriggerBuilder.newTrigger()
.withIdentity("trigger2","group2")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *"))
.forJob(getJobDetail())
.build();
}
}
- 常见的corn表达式
关于 cronExpression 表达式有至少 6 个(也可能是 7 个)由空格分隔的时间元素。从左至右,这些元素的定义如下:
1.秒(0–59)
2.分钟(0–59)
3.小时(0–23)
4.月份中的日期(1–31)
5.月份(1–12 或 JAN–DEC)
6.星期中的日期(1–7 或 SUN–SAT)
7.年份(1970–2099)
秒 0-59 , - * /
分 0-59 , - * /
小时 0-23 , - * /
日 1-31 , - * ? / L W C
月 1-12 or JAN-DEC , - * /
周几 1-7 or SUN-SAT , - * ? / L C #
年(可选字段) empty, 1970-2099 , - *
- corn表达式规则
"*" —— 字符可以用于所有字段,在"分"字段中设为"*",表示"每一分钟"的含义。
"?" —— 字符可以用在"日"和"周几"字段,它用来指定"不明确的值"。
这在你需要指定这两个字段中的某一个值而不是另外一个的时候会被用到。在后面的例子中可以看到其含义。
"-" —— 字符被用来指定一个值的范。
比如在"小时"字段中设为"10-12",表示"10 点到 12 点"。
"," —— 字符指定数个值。
比如在"周几"字段中设为"MON,WED,FRI",表示"the days Monday,Wednesday, and Friday"。
一些例子:
"/" —— 字符用来指定一个值的的增加幅度。
比如在"秒"字段中设置为"0/15"表示"第 0, 15, 30,和 45 秒"。
而"5/15"则表示"第 5, 20, 35,和 50"。
在'/'前加"*"字符相当于指定从 0 秒开始。每个字段都有一系列可以开始或结束的数值。
对于"秒"和"分"字段来说,其数值范围为 0 到 59。
对于"小时"字段来说其为 0 到 23,对于“日”字段来说为 0 到 31。
而对于"月"字段来说为 1 到 12。
"/"字段仅仅只是帮助你在允许的数值范围内从开始"第 n"的值。
"L" —— 字符可用在"日"和"周几"这两个字段。它是"last"的缩写,但是在这两个字段中有不同的含义。
"日"字段中的"L"表示"一个月中的最后一天",对于一月就是 31 号,对于二月来说就是 28 号(非闰年)。
"周几"字段中,它简单的表示"7" or "SAT"。
但是如果在"周几"字段中使用时跟在某个数字之后,它表示"该月最后一个星期×"。
比如"6L"表示"该月最后一个周五"。
当使用"L"选项时,指定确定的列表或者范围非常重要,否则你会被结果搞糊涂的。
"W" —— 可用于"日"字段。用来指定历给定日期最近的工作日(周一到周五)。
比如将"日"字段设为"15W",意为: "离该月 15 号最近的工作日"。
因此如果 15 号为周六,触发器会在 14 号即周五调用。
如果 15 号为周日,触发器会在 16 号也就是周一触发。如果 15 号为周二,那么当天就会触发。
如果"日"字段设为"1W",而一号是周六,会于下周一即当月的 3 号触发,它不会越过当月的值的范围边界。
"W"字符只能用于"日"字段的值为单独的一天而不是一系列值的时候。
"L"和"W"可以组合用于“日”字段表示为'LW',意为"该月最后一个工作日"。
"#" —— 字符可用于"周几"字段。该字符表示"该月第几个周×"。
比如"6#3"表示该月第三个周五( 6 表示周五,而"#3"该月第三个)。
再比如: "2#1" 表示该月第一个周一,而"4#5" 该月第五个周三。
注意如果你指定"#5"该月没有第五个"周×",该月是不会触发的。
"C" —— 字符可用于"日"和"周几"字段,它是"calendar"的缩写。
它表示为基于相关的日历所计算出的值(如果有)。如果没有关联的日历,那它等同于包含全部日历。
"日"字段值为"5C",表示"日历中的第一天或者 5 号以后"。
"周几"字段值为"1C",则表示"日历中的第一天或者周日以后"。
对于"月份"字段和"周几"字段来说合法的字符都不是大小写敏感的。
十六、Springboot事务支持
事务定义一般在service层定义,主要对增删改操作进行定义,查询效率一般不管。springboot事务实际上是继承了spring事务的注解方式,可以避免产生的数据安全问题。使用时直接在需要事务的方法上声明@Transactional()
package com.zwf.springboot.controller;
import com.github.pagehelper.PageInfo;
import com.zwf.springboot.pojo.UseInfor;
import com.zwf.springboot.service.UserInforService;
import com.zwf.springboot.vo.ResultInfo;
import com.zwf.springboot.vo.UserQuery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.xml.transform.Result;
import java.io.IOException;
import java.util.List;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:43
*/
@RestController
public class FindAllController {
@Resource
private UserInforService userInforService;
//查询开启事务
@Transactional(readOnly = true)
@GetMapping("/queryAll")
public List<UseInfor> findAll(){
return userInforService.findAll();
}
@Transactional(readOnly = true)
//paramType:表示参数放在哪个地方
@ApiImplicitParam(name = "用户id",required = true,paramType = "path")
@GetMapping("/queryAll/{id}")
public ResultInfo findUserById(@PathVariable Integer id){
return userInforService.findUserById(id);
}
//开启事务
@Transactional(propagation = Propagation.REQUIRED)
@PostMapping("/save")
public ResultInfo saveUser(@RequestBody UseInfor useInfor){
return userInforService.saveUseInforByUse(useInfor);
}
@Transactional(propagation = Propagation.REQUIRED)
@PutMapping("/update")
//@Valid开启字符校验
public ResultInfo updateUserById(@RequestBody @Valid UseInfor useInfor){
return userInforService.updateUseInforByUse(useInfor);
}
@Transactional(propagation = Propagation.REQUIRED)
@DeleteMapping("/delete/{id}")
public ResultInfo deleteUserById(@PathVariable Integer id){
return userInforService.deleteUseInforByUse(id);
}
@Transactional(readOnly = true)
@GetMapping("/queryPage")
public PageInfo<UseInfor> queryPage(UserQuery userQuery){
return userInforService.queryPageByParams(userQuery);
}
}
- 常见的事务配置
配置事务级别和传播类别
propagation
不是必须的,默认值是REQUIRED 有事务使用事务 没事务创建事务
表示事务传播行为, 包括:
REQUIRED,SUPPORTS,MANDATORY,NEVER
REQUIRES_NEW,NOT_SUPPORTED,NESTED
isolation
不是必须的,默认值DEFAULT
表示事务隔离级别(数据库的隔离级别)
timeout
不是必须的,默认值-1(永不超时)
表示事务超时的时间(以秒为单位)
read-only
不是必须的,默认值false不是只读的
表示事务是否只读
rollback-for
不是必须的
表示将被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException'
no-rollback-for
不是必须的
表示不被触发进行回滚的 Exception(s);以逗号分开。
如:'com.foo.MyBusinessException,ServletException'
任何 RuntimeException 将触发事务回滚
十七、SpringBoot全局异常
定义全局异常一般处理自定义异常,通过自定义异常对抛出的异常进行捕获处理,springboot配置自定义异常是继承spring全局异常方式。
- 定义自定义异常
package com.zwf.springboot.exception;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:24
*/
public class ParameterException extends RuntimeException{
private String message; //异常信息
private Integer stateCode=500; //默认响应码
public ParameterException() {
}
public ParameterException(String message) {
super(message);
this.message=message;
}
public Integer getStateCode() {
return stateCode;
}
public void setStateCode(Integer stateCode) {
this.stateCode = stateCode;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "ParameterException{" +
"message='" + message + '\'' +
'}';
}
}
- 定义全局异常
package com.zwf.springboot;
import com.zwf.springboot.exception.ParameterException;
import com.zwf.springboot.vo.ResultInfo;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 把异常信息封装成 返回对象 返回给前台
*/
@ControllerAdvice
public class GlobalExceptionHandle {
//设置参数处理
@ExceptionHandler(value = ParameterException.class)
@ResponseBody
public ResultInfo paramException(ParameterException e){
//把异常中的信息封装成返回对象
ResultInfo resultInfo=new ResultInfo();
resultInfo.setStateCode(e.getStateCode());
resultInfo.setMessage(e.getMessage());
return resultInfo;
}
//默认异常处理
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultInfo defaultException(Exception e){
//把异常中的信息封装成返回对象
ResultInfo resultInfo=new ResultInfo();
resultInfo.setStateCode(500);
resultInfo.setMessage(e.getMessage());
return resultInfo;
}
//参数校验异常
@ExceptionHandler(value = BindException.class)
@ResponseBody
public ResultInfo BindExceptionHandle(BindException e){
//把异常中的信息封装成返回对象
ResultInfo resultInfo=new ResultInfo();
resultInfo.setStateCode(500);
//打印设置的验证异常 注意导入 validate包下的BindException
resultInfo.setMessage(e.getBindingResult().getFieldError().getDefaultMessage());
return resultInfo;
}
}
十七、SpringBoot整合数据校验Valiation
- 导入依赖
<!--导入验证包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 在实体类中使用注解
package com.zwf.springboot.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* @author Mr Zeng
* @version 1.0
* @date 2023-09-07 21:20
*/
@ApiModel(value = "用户信息表标题",description = "用户信息实体类")
public class UseInfor implements Serializable {
@NotNull(message = "主键不能为空")
private Integer id;
@NotBlank(message = "用户名不能为空")
@Length(message = "用户名长度在2到9之间",max = 9,min = 2)
private String username;
@NotBlank(message = "密码不能为空")
private String password;
@Email
private String Email;
public UseInfor() {
}
public UseInfor(Integer id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
public String getEmail() {
return Email;
}
public void setEmail(String email) {
Email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "UseInfor{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", Email='" + Email + '\'' +
'}';
}
}
- 声明在作用的参数上
@PutMapping("/update")
//@Valid开启字符校验
public ResultInfo updateUserById(@RequestBody @Valid UseInfor useInfor){
return userInforService.updateUseInforByUse(useInfor);
}
- 处理数据校验异常(不满意验证条件出现的异常,定义全局异常中,一般抛出BindException)
package com.zwf.springboot;
import com.zwf.springboot.exception.ParameterException;
import com.zwf.springboot.vo.ResultInfo;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 把异常信息封装成 返回对象 返回给前台
*/
@ControllerAdvice
public class GlobalExceptionHandle {
//默认异常处理
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResultInfo defaultException(Exception e){
//把异常中的信息封装成返回对象
ResultInfo resultInfo=new ResultInfo();
resultInfo.setStateCode(500);
resultInfo.setMessage(e.getMessage());
return resultInfo;
}
//参数校验异常
@ExceptionHandler(value = BindException.class)
@ResponseBody
public ResultInfo BindExceptionHandle(BindException e){
//把异常中的信息封装成返回对象
ResultInfo resultInfo=new ResultInfo();
resultInfo.setStateCode(500);
//打印设置的验证异常 注意导入 validate包下的BindException
resultInfo.setMessage(e.getBindingResult().getFieldError().getDefaultMessage());
return resultInfo;
}
}
- 注意:使用时要不要导错包,应该使用javax.validation.*包