SpringBoot介绍
SpringBoot是由Pivotal团队研发的,SpringBoot并不是一门新技术,只是将之前常用的Spring,SpringMVC,data-jpa等常用的框架封装到了一起,帮助你隐藏这些框架的整合细节,实现敏捷开发。SpringBoot就是一个工具集。
SpringBoot特点:
- SpringBoot项目不需要模板化的配置。
- SpringBoot中整合第三方框架时,只需要导入相应的starter依赖包,就自动整合了。
- SpringBoot默认只有一个.properties的配置文件,不推荐使用xml,后期会采用.java的文件去编写配置信息。
- SpringBoot工程在部署时,采用的是jar包的方式,内部自动依赖Tomcat容器,提供了多环境的配置。
- 后期要学习的微服务框架SpringCloud需要建立在SpringBoot的基础上。
创建SpringBoot项目
选择构建项目的类型
选择构建项目的类型 |
---|
项目的描述
项目的描述 |
---|
指定SpringBoot版本和需要的依赖
指定SpringBoot版本和需要的依赖 |
---|
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 将上述内容修改为下面的效果 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
编写了Controller
@RestController
public class TestController {
@GetMapping("/test")
public String test(){
return "Hello SpringBoot!";
}
}
测试
效果 |
---|
好了一个最简单的spring boot项目就创建好了。
定制Banner
我们在启动Spring Boot项目的时候,在控制台会默认输出一个启动图案,如下:
当然,这个图案如果你需要的话是可以自己修改的,修改方式很简单:
1.在src/main/resources下新建一个banner.txt文档
2.通过https://www.bootschool.net/ascii网站生成需要的字符,将字符拷贝到步骤1所创建的txt文档中。
Spring Boot的配置文件
Spring Boot使用一个全局的配置文件application.properties或者application.yml,配置文件放在src/main/resources目录下。
YAML语法
-
k:(空格)v:表示一对键值对(空格必须有);
-
以空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的
字符串:默认不用加上单引号或者双引号;
# “”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
# ‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
对象、Map:键值对:
friends:
lastName: zhangsan
age: 20
行内写法:
friends: {lastName: zhangsan,age: 18}
数组:
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
我们在配置文件中配置的这些属性怎么使用呢?
属性的使用
在变量中通过@Value直接注入就行了,如下:
@Value(value = "${friends.lastName}")
private String lastName;
@Value("${friends.age}")
private Integer age;
刚刚说的这种方式我们在实际项目中使用的时候工作量略大,因为每个项目要注入的变量的值太多了,这种时候我们可以使用基于类型安全的配置方式,就是将properties属性和一个Bean关联在一起,这样使用起来会更加方便。我么来看看这种方式怎么实现。
@Data
@Component
//prefix是指前缀,location指定要注入文件的位置。
@ConfigurationProperties(prefix = "friends",locations = "classpath:friends.properties")
public class Friends {
private String lastName;
private Integer age;
}
为什么指定这样配置它就能生效呢?让我们一起去看看底层实现原理
启动类 @SpringBootApplication
每个spring Boot都有一个启动类XXXApplication,打开启动类可以看到,启动类上有个注解@SpringBootApplication。之所以那些配置能生效,就是因为@SpringBootApplication这个注解在启动的时候自动帮我们做了许多事情。
接下来让我们一起来看看这个@SpringBootApplication注解,帮我们做了哪些事情把。
进入@SpringBootApplication注解,这个注解是个复合注解,里面还包含了其他注解。
@Target({ElementType.TYPE})//表示该注解的作用目标为,类,接口或者枚举
@Retention(RetentionPolicy.RUNTIME)//表示在运行时有效
@Documented//表示它的注解在生成文档的时候会保留下来
@Inherited//如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解
@SpringBootConfiguration//底层是一个@Configuration注解 表示它时一个配置类
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
其中有两个注解是比较重要的:
-
@ComponentScan:这个就是扫描注解的意思,默认扫描当前类所在的包及其子包下包含的注解,将@Controller,@Service,@Component,@Repository等注解加载到IOC容器中;
-
@EnableAutoConfiguration:这个注解表明启动自动装配,里面包含连个比较重要的注解@AutoConfigurationPackage和@Import。
-
@AutoConfigurationPackage:和@ComponentScan一样,也是将主配置类所在的包及其子包里面的组件扫描到IOC容器中,但是区别是@AutoConfigurationPackage扫描@Enitity、@MapperScan等第三方依赖的注解,@ComponentScan只扫描@Controller,@Service,@Component,@Repository这些常见注解。所以这两个注解扫描的对象是不一样的。
-
@Import(AutoConfigurationImportSelector.class):是自动装配的核心注解,AutoConfigurationImportSelector.class中有个selectImports方法
selectImports方法还调用了getCandidateConfigurations方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IlpJWTPA-1631056465454)(C:\Users\Administrator\Desktop\笔记\SpringBoot\Pictures\20200721145641124.png)]
getCandidateConfigurations方法中,我们可以看下断言,说找不到META-INF/spring.factories,由此可见,这个方法是用来找META-INF/spring.factories文件的
我们可以定位到这个方法所在的类处于spring-boot-autoconfigure-.jar包中,其中spring.factories文件是一组组的key=value的形式,包含了key为EnableAutoConfiguration的全类名,value是一个AutoConfiguration类名的列表,以逗号分隔。
项目启动时,它会找到所有JavaConfig配置类全限定名对应的class,然后将所有自动配置类加载到IOC容器中。
那么这些类是如何获取默认属性值的呢?以ServletWebServerFactoryAutoConfiguration为例,它是Servlet容器的自动配置类
该类上开启了@EnableConfigurationProperties(ServerProperties.class)注解,最终找到了ServerProperties类。
至此,我们大致可以了解。在全局配置的属性如:server.port等,通过@ConfigurationProperties注解,绑定到对应的XxxxProperties配置实体类上封装为一个bean,然后再通过@EnableConfigurationProperties注解导入到Spring容器中。
多环境配置
在实际开发中一般都会有这三种环境:本地环境(自己),开发环境(团队开发),生产环境(线上)。
在application.yml文件中添加一个配置项:
spring:
profiles:
active: 环境名 #指定使用哪一个环境
在resource目录下,创建多个application-环境名.yml文件即可
开发环境 application-dev.yml
server:
# 项目端口
port: 8080
servlet:
# 项目路径
context-path: /
生产环境 application-prov.yml
server:
# 项目端口
port: 8081
servlet:
# 项目路径
context-path: /
本地环境application-local.yml
server:
# 项目端口
port: 8082
servlet:
# 项目路径
context-path: /
SpringBoot常用注解
controller层:
- @Controller :用来响应页面,表示当前的类为控制器。
- @RestController :是@ResponseBody和@Controller的结合,表明当前类是控制器且返回的是一组数据,不是页面。
- @Autowired:引入其他的类,接口。
- @RequestMapping:作用:URL映射。访问接口的地址
service层:
- @service:用于标注业务层组件
dao层:
- @Repository:是用来注解接口,表明该类是用来执行与数据库相关的操作(即dao对象)。
config配置:
- 1.@Configuration:表明这是一个配置类。@Configuration注解等价 与xml里面的标签
- @Bean:是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名
例如:
@Configuration
public class ExampleConfiguration {
@Value("com.mysql.jdbc.Driver")
private String driverClassName;
@Value("jdbc://xxxx.xx.xxx/xx")
private String driverUrl;
@Value("${root}")
private String driverUsername;
@Value("123456")
private String driverPassword;
@Bean(name = "dataSource")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(driverUrl);
dataSource.setUsername(driverUsername);
dataSource.setPassword(driverPassword);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
这样,在项目中,这个dataSource就是我们在ExampleConfiguration中配的DataSource。
@Autowired
private DataSource dataSource;
启动类:
- @SpringBootApplication:启动注解,这个注解在前面已经讲过了,这里就不赘述了。
- @MapperScan(“com.Vm.server”) :扫描指定包中的mapper接口,并注入spring容器。
- @EnableScheduling: 开启spring自带的定时服务
public class ScheduledTasks {
@Scheduled(fixedRate = 1000 * 30) //每30秒执行一次
public void reportCurrentTime(){
System.out.println ("Scheduling Tasks Examples: The time is now " + dateFormat().format (new Date ()));
}
}
@Component
:通用的注解,可标注任意类为 Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。
静态资源
Spring Boot的静态资源 默认都会存在 resources/static 目录,很多小伙伴也知道静态资源只要放到这个目录下,就可以直接访问,除了这里还有没有其他可以放静态资源的位置呢?为什么放在这里就能直接访问了呢?
静态资源存放的目录
首先,在 Spring Boot 中,默认情况下,一共有5个位置可以放静态资源,五个路径分别是如下5个:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /
系统默认创建了 classpath:/static/
, 正常情况下,我们只需要将我们的静态资源放到这个目录下即可,也不需要额外去创建其他静态资源目录。
如果想要使用其它静态资源目录,就需要自己创建。前四个目录好理解只需要在resources目录下创建对应的包,然后将静态资源放入包中即可访问。第5个 /
是啥意思呢?这里第5个 /
其实就是表示 webapp 目录中的静态资源也不被拦截。
如果同一个文件分别出现在五个目录下,那么优先级也是按照上面列出的顺序。
访问静态资源
现在我们知道了静态资源可以存放在哪些静态目录下,那么如何访问这些静态资源呢?
例如我在 classpath:/static/
目录下放了一张名为1.png 的图片,那么我的访问路径是:
http://localhost:8080/1.png #这里大家注意,请求地址中并不需要 static,如果加上了static反而多此一举会报404错误。
很多人会觉得奇怪,为什么不需要添加 static呢?资源明明放在 static 目录下。其实Spring Boot 自动帮我们做了资源路径映射。请求地址如果是 http://localhost:8080/1.png
实际上系统会去 /static/1.png
目录下查找相关的文件。
资源路径映射底层原理
通过上面我们知到Spring Boot 自动帮我们做了资源路径映射,那它是如何实现的呢?
首先我们在 WebMvcAutoConfiguration 类中看到了 addResourceHandlers这个方法
addResourceHandlers()调用了两个方法getStaticPathPattern() 和getStaticLocations()方法。
我们进入getStaticPathPattern() 这个方法,发现它最终指向的是WebMvcProperties类的staticPathPattern属性,并在构造器中初始化为**/****
我们再进入getStaticLocations()这个方法,发现它最终指向的是Resources类的staticLocations属性,并在构造器中初始化为CLASSPATH_RESOURCE_LOCATIONS。而CLASSPATH_RESOURCE_LOCATIONS中有**”classpath:/META-INF/resources/“, “classpath:/resources/“,”classpath:/static/“, “classpath:/public/“,**
这样大伙就知道了为什么Spring Boot 中支持这5个静态资源位置。
自定义配置
如果我们并不想将资源放在系统默认的这五个位置上,也可以自定义静态资源位置和映射,自定义的方式也有两种,可以通过 application.properties 来定义,也可以在 Java 代码中来定义,下面分别来看。
application.properties
在配置文件中定义的方式比较简单,如下:
spring.resources.static-locations=classpath:/
spring.mvc.static-path-pattern=/**
第一行配置表示定义资源位置,第二行配置表示定义请求 URL 规则。以上文的配置为例,如果我们这样定义了,表示可以将静态资源放在 resources目录下的任意地方,我们访问的时候当然也需要写完整的路径,例如在resources/static目录下有一张名为1.png 的图片,那么访问路径就是 http://localhost:8080/static/1.png
,注意此时的static不能省略。
Java 代码定义
@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/aaa/");
}
}
这里代码基本和前面一致,比较简单,不再赘述。
码云地址:https://gitee.com/li-jie-jie/springboot0-study/tree/master
本地项目地址:D:\javalab\MyIDEAJavaSpring-Boot\spring-study\exception
路径映射
一般我们可以通过编写一个controller去跳转到页面。当然现在几乎都是前后端分离项目,页面跳转一般是由前端负责。(所以了解即可)
controller去跳转
@Controller
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
hello.html存放在templates目录下
启动项目访问http://localhost:8081/hello这样我们就可以访问到hello.html这个页面了。
路径映射
有时我们的项目有动态的页面模板,==它并不需要去渲染数据。==但是又不可以直接访问,一般我们可以通过编写一个controller去跳转到这个页面。像这种情况其实我们没有必要去写controller,我们可以直接使用路径映射来访问。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
}
启动项目访问http://localhost:8081/hello这样我们就可以访问到hello.html这个页面了。
SpringBoot整合json
主要介绍如何在Spring Boot中整合几种常用的JSON方案,主要介绍Spring Boot本身提供和Jackson和GSON,以及另一种比较常用的fastjson的整合。
1. SpringBoot整合Jackson
Jackson是Spring Boot中默认的,所以我们不需要导入额外的依赖,只需要导入spring-boot-starter-web就可以使用了。
依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String address;
}
controller测试
@RestController()
@RequestMapping("/json")
public class UserController {
@GetMapping("/jackson")
private List<User> jacksonTest() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
users.add(new User(i, "Jackson" + i, "杭州"));
}
return users;
}
}
启动项目效果:
真正在项目中不仅仅是返回json还有上传json,不管是是上传json还是返回json都需要用到HttpMessageConverter。但是我们发现上面我们压根就没有用到HttpMessageConverter,这是因为spring mvc自动配置了jackson和gson的HttpMessageConverter 而spring boot又做了自动化配置。
HttpMessageConverter的作用主要有两个:1.将前端传过来的json字符串反序列化为java对象,2.将后端返回的java对象序列化为json字符串。下面我们对他们进行简单的使用
实体类增加birthday属性:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String address;
private Date Birthday;
}
修改controller
@RestController()
@RequestMapping("/json")
public class UserController {
@GetMapping("/jackson")
public List<User> jacksonTest() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
users.add(new User(i, "Jackson" + i, "杭州",new Date()));
}
return users;
}
}
运行效果:
我们可以看到时间的格式都是:2021-04-26T05:58:21.497+00:00样的,有时我们不想要这种时间格式。
通过注解@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)修改时间格式
修改实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String address;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date Birthday;
}
运行效果
这样更方便更灵活,但是这样有个不好的地方,就是所有实体类的时间我们都要去给它加上注解。有没有统一的方法呢?
那就需要我们自己定义HttpMessageConverter来给所有的时间统一格式化。
去掉实体类的注解,自定义HttpMessageConverter
@Configuration
public class WebMvcConfig {
@Bean
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter= new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
converter.setObjectMapper(objectMapper);
return converter;
}
}
设置时间格式实在ObjectMapper中设置的,那么我们可不可以只提供一个ObjectMapper呢?答案是可以的。
修改WebMvcConfig
@Configuration
public class WebMvcConfig {
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper().setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
}
2. Spring Boot 整合GSON
GSON也是Spring Boot中提供了自动化配置的,我们需要排除Jackson的依赖并引入GSON依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
实体类和controller和jackson一样,
运行效果:
自定义HttpMessageConverter
@Configuration
public class WebMvcConfig {
@Bean
public GsonHttpMessageConverter gsonHttpMessageConverter() {
GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create());
return converter;
}
}
同样我们可以只提供一个Gson
@Configuration
public class WebMvcConfig {
@Bean
Gson gson(){
return new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
}
}
3. SpringBoot整合fastjson
Spring Boot 中并没有提供fastjson的默认支持,所以我们需要自行导入依赖,
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
必须配置FastJsonHttpMessageConverter
@Configuration
public class WebMvcConfig {
@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter(){
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
// 处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(fastMediaTypes);
converter.setFastJsonConfig(config);
return converter;
}
}
SpringBoot 类型转换器
有时前端会传一下日期给后端,后端直接接受是会报错的。那么后端如何用一个日期对象来接受他呢?
package cn.itxiaoliu;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@RestController
public class UserController {
@GetMapping("/hello")
public void hello(Date birth){
System.out.println(birth);
}
}
启动项目,访问:http://localhost:8081/date?birth=1996-06-14
此时会出现参数类型错误
再新建DateConverter.java
@Component
public class DateConverter implements Converter<String, Date> {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date convert(String source) {
if(source!=null&&!"".equals(source)){
try {
return sdf.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
启动项目,访问:http://localhost:8081/date?birth=1996-06-14
成功