1、SpringBoot的hello,world
- 使用IDEA创建项目
- 添加web依赖
等待maven下载依赖
- 启动类同级目录下建相应的包,在controller包下创建HelloController.java并添加如下代码
package com.toolate.comtroller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello,world";
}
}
- 启动项目,访问http://localhost:8080/hello/hello
2、原理初探
2.1、自动装配:
- 1、pom.xml
- Spring-boot-dependencies:核心依赖在父工程中
- 我们在写或者引入Springboot依赖的时候,不需要指定版本是因为有这些版本仓库
- 2、启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
-
Springboot会将所有的功能场景都变成一个个的启动器
- 比如spring-boot-starter-web帮我们自动导入web环境所有的依赖
- 我们要使用什么功能,就只需要找到对应的启动器就可以了
-
3、主程序
package com.toolate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
- 4、注解:
@SpringBootApplication
@SpringBootConfiguration:springboot的配置
@Configuration:spring配置类
@Component:说明这也是Spring的组件
@EnableAutoConfiguration:自动配置,自动导入包
@AutoConfigurationPackage:自动配置包
@Import(AutoConfigurationPackages.Registrar.class):自动注册“包注册”
@Import(AutoConfigurationImportSelector.class):自动配置导入选择
@getAutoConfigurationEntry:或者自动配置的实体
//获取所有的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
@ComponentScan:扫描当前主启动类同级的包
@ConditionOnXXX:如果这里面的条件都满足,才会生效
获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
//所有的资源加载到配置类中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
结论:springboot所有的自动配置都是在启动的时候自动扫描并加载,所有的自动配置类都在spring.factories 里,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了启动器,我们自动装配就生效,然后就配置成功
- springboot在启动的时候,从类路径下META-INF/spring.factories 获取指定的值
- 将自动配置的类导入容器,自动配置类就会生效,帮我们进行自动配置
- 以前我们需要自动配置的东西,现在springboot帮我们做了
- 整个JavaEE,解决方案和自动配置的都在spring-boot-cutoconfigure-2.4.2.RELEASE.jar下
- 它会把所有需要导入的组件,以类名的方式(全限定名)返回,这些组件就会被添加到容器
- 容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置@Configuration、JavaConfig
- 有了自动配置类,免去了我们手动编写配置文件的工作
- 关于Springboot,谈谈你的理解:
- 自动装配
- run();
- 判断应用类型是普通项目还是web项目
- 推断并设置main方法的定义类,找到运行的主类
- 监听器:获取上下文,处理Bean
3、SpringBoot配置
3.1、配置文件
名称是固定的:修改SpringBoot自动配置的默认值
- application.properties:key=value,只能保存键值对
- application.yaml:key:空格value,对空格的要求十分高,可以注入到配置类中
3.2、 yaml语法:
# key-value
name: toolate
# 对象
student:
name: toolate
age: 20
# 行内写法
student: {name: toolate,age: 20}
# 数组
pets:
- cat
- dog
pets: [cat,dog]
3.3、yaml配置文件
- Person.java
@Component
@ConfigurationProperties(prefix = "person")//与yaml中的配置
@Data
public class Person {
private String name;
private Integer age;
private boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
@ConfigurationProperties(prefix = “person”)//与yaml中的配置
- Dog.java
@Component
@Data
public class Dog {
private String name;
private Integer age;
}
- application.yaml
person:
name: toolate
age: 1
happy: true
birth: 2021/1/17
maps: {k1: v1,k2: v2}
lists: [code,music,game]
dog:
name: 旺财
age: 3
- pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
这个依赖是为了解决爆红问题
- 测试代码
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
person:
name: toolate${random.uuid}
age: ${random.int}
happy: true
maps: {k1: v1,k2: v2}
lists: [code,music,movie]
hello: happy
dog:
name: ${person.hello:hello}_旺财
age: 3
- ${person.hello:hello}:person.hello值存在取自己的值,不存的话取hello
- #{random.xxx}:取随机值
3.4、properties配置文件
@Component
//加载指定的配置文件
@PropertySource(value = "classpath:toolate.properties")
public class Person {
//spel表达式取出配置文件的值
@Value("${name}")//需要一个一个内容读取
private String name;
private Integer age;
private boolean happy;
private Data birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
- 新建一个application.properties在里面赋值,并通过@PropertySource(value = “classpath:application.properties”)来加载指定的配置文件
- 使用@Value("${name}")需要一个一个内容读取
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据检验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
结论:
- 配置文件yml还是properties他们都能获取到值;
- 如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value;
- 如果说,我们专门编写了一个JavaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties;
3.5、松散绑定:yml中的last-name和类中的lastName是一样的,即用-来将小写转换为大写
3.6、JSR303数据校验 :
可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
空检查
- @Null 验证对象是否为null
- @NotNull 验证对象是否不为null, 无法查检长度为0的字符串
- @NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
- @NotEmpty 检查约束元素是否为NULL或者是EMPTY.
Booelan检查
- @AssertTrue 验证 Boolean 对象是否为 true
- @AssertFalse 验证 Boolean 对象是否为 false
长度检查
- @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
- @Length(min=, max=) Validates that the annotated string is between min and max included.
日期检查
- @Past 验证 Date 和 Calendar 对象是否在当前时间之前
- @Future 验证 Date 和 Calendar 对象是否在当前时间之后
- @Pattern 验证 String 对象是否符合正则表达式的规则
数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null
- @Min 验证 Number 和 String 对象是否大等于指定的值
- @Max 验证 Number 和 String 对象是否小等于指定的值
- @DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度
- @DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度
- @Digits 验证 Number 和 String 的构成是否合法
- @Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。
- @Range(min=, max=) 检查数字是否介于min和max之间.
- @Range(min=10000,max=50000,message=“range.bean.wage”)
private BigDecimal wage; - @Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)
- @CreditCardNumber信用卡验证
- @Email 验证是否是邮件地址,如果为null,不进行验证,算通过验证。
- @ScriptAssert(lang= ,script=, alias=)
- @URL(protocol=,host=, port=,regexp=, flags=)
3.7、多配置文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.yaml
- application-dev.yaml
server:
port: 8081
- application-test.yaml
server:
port: 8082
- application.yaml
spring:
profiles:
active: test
在application.yaml中可以选择一个配置文件,spring.profiles.active中填写的值为配置文件application-后面的值
在我们这配置文件中配置的东西,都存在一个固有的规律
- xxxAutoConfiguration:默认值 xxxProperties和配置文件绑定,我们就可以使用自定义的配置类
每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置
3.8、再探自动装配原理
以HttpEncodingAutoConfiguration 为例
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}
static class LocaleCharsetMappingsCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;
LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}
@Override
public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}
}
@Override
public int getOrder() {
return 0;
}
}
}
//表示这是一个配置类
@Configuration(proxyBeanMethods = false)
//自动配置属性,指定配置哪个类 eg:ServerProperties.class
@EnableConfigurationProperties(ServerProperties.class)
//Spring的底层注解,根据不同的条件,来判断当前配置或者类是否生效
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)//判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的;即使我们配置文件中不配置spring.http.encoding.enabled=true,也是默认生效的;
@conditionalOnXXXX是通过一些条件来判断是否加载这个类
总结:
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有SpringBoot默认写好的自动配置类
- 我们再来看这个自动配置类中到底配置了哪些组件(只要我们要用的组件有,我们就不需要再来配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们就可以在配置文件中指定这些属性的值;
xxxxAutoConfigurartion:自动配置类,给容器中添加组件
xxxxProperties:封装配置文件中相关属性,springboot配置修改这些属性
4、SpringBoot Web开发
4.1、静态资源导入问题
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//如果自定义一个目录:如:spring.mvc.static-pattern=/hello/,class path:/hxh/,源码会失效
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
在SpringBoot中,我们可以使用以下的方式处理静态资源
- webjars:localhost:8080/webjars/
- resources包下新建package:resources>static>public(优先级) localhost:8080/
4.2、模版引擎-Thymeleaf
- 导入thymeleaf的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 在要显示的html页面加入thymeleaf的相关命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
- 编写相关的controller
@Controller
public class IndexController {
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("msg","Hello,Thymeleaf");
return "test";
}
}
- 在显示的html中取出相应的值
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--所有的html元素都可以被thymeleaf替换接管,th:元素名-->
<h1 th:text="${msg}"></h1>
</body>
</html>
- 访问接口
4.3、Thymeleaf语法
- ${…}:获取变量值
- *{…}:选择表达式
- #{…}:获取国际化内容
- @{…}:定义URL
- ~{…}:片段引用表达式
- Literals(字面量)
Text literals: ‘one text’ , ‘Another one!’ ,…
Number literals: 0 , 34 , 3.0 , 12.3 ,…
Boolean literals: true , false
Null literal: null
Literal tokens: one , sometext , main ,… - Text operations:(文本操作)
String concatenation: +
Literal substitutions: |The name is ${name}| - Arithmetic operations:(数学运算)
Binary operators: + , - , * , / , %
Minus sign (unary operator): - - Boolean operations:(布尔运算)
Binary operators: and , or
Boolean negation (unary operator): ! , not - Comparisons and equality:(比较运算)
Comparators: > , < , >= , <= ( gt , lt , ge , le )
Equality operators: == , != ( eq , ne ) - Conditional operators:条件运算(三元运算符)
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue) - Special tokens:
No-Operation: _ //分割表达式
@Controller
public class IndexController {
@RequestMapping("/test")
public String index(Model model){
model.addAttribute("users", Arrays.asList("张三","李四","王五"));
return "test";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3 th:each="user:${users}" th:text="${user}"></h3>
</body>
</html>
4.4、SpringMVC
- 扩展试图解析器
编写一个配置类@Configuration继承WebMvcConfigurer
不要标注@EnableWebMvc
//拓展SpringMVC
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/toolate").setViewName("test");
}
}
实现了页面跳转
5、 Druid
配置mysql相关参数,切换数据源为Druid
spring:
datasource:
username: "root"
password: "123456"
url: jdbc:mysql://localhost:3306/mybatis?userUnicode=true&chctacterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
- 编写配置类DruidConfig
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
HashMap<String, String> initParameters = new HashMap<>();
initParameters.put("loginUsername","admin");
initParameters.put("loginPassword","123456");
//允许访问
initParameters.put("allow","");
bean.setInitParameters(initParameters);
return bean;
}
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
HashMap<String, String> initParameters = new HashMap<>();
//这些不进行统计
initParameters.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
访问 http://localhost:8080/druid
填上在配置类中配置好的用户名和密码
自带的数据监控功能,非常强大
6、整合Mybatis
6.1、创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
6.2、编写UserMapper
@Mapper
@Repository
public interface UserMapper {
List<User> queryUserList();
User queryUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
注意:在SpringBoot中Mapper类加上一个@Mapper的注解就能把这个接口给Spring容器管理,不再需要写对应的实现类
6.3、UserMapper.xml
<?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.toolate.mapper.UserMapper">
<select id="queryUserList" resultType="User">
select * from mybatis.user;
</select>
<select id="queryUserById" resultType="User" parameterType="int">
select * from mybatis.user where id = #{id};
</select>
<insert id="addUser" parameterType="User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="User">
update user set name = #{name},pwd=#{pwd}, where id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>
</mapper>
6.4、配置文件
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&useSSL=false&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.toolate.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
6.5、UserController
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping("/queryUserList")
public List<User> queryUserList(){
List<User> userList = userMapper.queryUserList();
return userList;
}
@GetMapping("/queryUserById/{id}")
public User queryUserById(@PathVariable("id")int id){
User user = userMapper.queryUserById(id);
return user;
}
}