微服务是一种架构风格,使用微服务架构,将应用程序构建为独立的组件,并将每个应用程序进程作为一项服务运行;
2、原理初探自动配置:
pom.xml
- spring-boot-dependencies:在父工程中存在许多核心依赖
启动器
-
org.springframework.boot spring-boot-starter-web -
spring-boot-starter-xxx:启动器(springboot的启动场景)
- 比如spring-boot-starter-web,就会帮我们自动导入web环境所有的依赖
-
springboot会将所有的功能场景,都变成一个个启动器
主程序
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
-
注解
-
@SpringBootApplication //标注这个类是一个springboot的应用程序 @SpringBootConfiguration @Configuration //spring配置类 @Component //说明这是一个spring的组件 @EnableAutoConfiguration //自动配置 @AutoConfigurationPackage //自动配置包 @Import(AutoConfigurationPackages.Registrar.class) //自动配置包 @Import(AutoConfigurationImportSelector.class) public String[] selectImports(AnnotationMetadata annotationMetadata); getCandidateConfigurations(annotationMetadata, attributes); //获取所有配置
-
AutoConfigurationImportSelector实现了ImportSelector接口中的String[] selectImports(AnnotationMetadata importingClassMetadata);方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NisSIXMy-1655535420734)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654652698158.png)]
selectImports(AnnotationMetadata importingClassMetadata);方法中通过getAutoConfigurationEntry()的getCandidateConfigurations(annotationMetadata, attributes);方法获取候选的配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uC12UDVf-1655535420735)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654653365022.png)]
getCandidateConfigurations()方法会获取到标注了EnableAutoConfiguration注解的类的配置;
EnableAutoConfiguration注解被SpringBootApplication注解所继承,所以启动类下的所有资源被导入;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XdJl1qfS-1655535420736)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654652883414.png)]
同时该方法中的Assert.notEmpty();断言中所说META-INF/spring.factories是自动配置的核心文件;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOMACx2u-1655535420737)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654654241653.png)]
自动配置的核心文件是被通过loadFactoryNames()所调用的loadSpringFactories()方法来加载;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IrJL2Feq-1655535420737)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654654570093.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yySTzkcJ-1655535420738)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1654654656031.png)]
总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eg4ZVsDS-1655535420738)(D:wangyedownloadSpringboot自动配置原理.png)]
3、主程序SpringApplication
这个类主要做了下面四个事情
- 推断应用是普通项目还是web项目
- 查看并加载所有可用初始化器,设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OKyPoQp0-1655535420738)(C:Users阳光就在天上DesktopflagSpringApplication.png)]
4、yaml通过yaml配置文件给实体类赋值
- 在springboot项目中的resources目录下新建一个文件 application.yml
- 编写一个实体类 Dog;
@Component //注册bean到容器中
public class Dog {
private String name;
private Integer age;
//有参无参构造、get、set方法、toString()方法
}
- 我们可以通过@Value给bean注入属性值
@Component //注册bean
public class Dog {
@Value("旺财")
private String name;
@Value("18")
private Integer age;
}
- 我们也可以通过yaml配置的方式进行注入
dog: name: 旺财 age: 1
- 通过yaml配置注入,需要在实体类上加@ConfigurationProperties(prefix = “dog”)注解
@Component //注册bean
@ConfigurationProperties(prefix = "dog")
public class Dog {
@Value("旺财")
private String name;
@Value("18")
private Integer age;
}
- idea提示,springboot配置注解处理器没有找到,通过文档,还需要导入一个依赖
5、JSR303校验org.springframework.boot spring-boot-configuration-processor true
Springboot中可以用@validated来校验数据,
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person {
@Email(message="邮箱格式错误") //name必须是邮箱格式
private String name;
}
使用数据校验,可以保证数据的正确性;
常见参数:
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@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=) string is between min and max included.
日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则
6、自动装配原理
- SpringBoot启动会加载大量的自动配置类
- 若自动配置类中有我们所需要的组件,就不需要手动配置了
- 给容器中添加组件的时候,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性的值即可;
- xxxxAutoConfigurartion:自动配置类;给容器中添加组件
- xxxxProperties:封装配置文件中相关属性;
在WebMvcAutoConfigurationAdapter 中有一个方法:addResourceHandlers 添加资源处理
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
// 已禁用默认资源处理
logger.debug("Default resource handling disabled");
return;
}
// 缓存控制
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// webjars 配置
if (!registry.hasMappingForPattern("/webjars*.xml
true
- 编写Controller测试
@RestController
public class UserController {
@Autowired
private UserMapper userMapper;
@GetMapping ("/queryList")
public List queryUserList(){
List userList = userMapper.queryUserList();
for (User user : userList) {
System.out.println(user);
}
return userList;
}
}
12、SpringSecurity
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
- WebSecurityConfigurerAdapter:自定义Security策略
- AuthenticationManagerBuilder:自定义认证策略
- @EnableWebSecurity:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
12.1、认证和授权- 准备前端页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCmQVtnG-1655535420740)(C:Users阳光就在天上AppDataRoamingTyporatypora-user-images1655278919643.png)]
- 引入 Spring Security 依赖
org.springframework.boot spring-boot-starter-security
- 编写 Spring Security 配置类
@EnableWebSecurity //开启
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//定义授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
}
- 定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}
- 测试一下:发现首页都进不去了!因为我们目前没有登录的角色,因为请求需要登录的角色拥有对应的权限才可以!
- 在configure()方法中加入以下配置,开启自动配置的登录功能!
// 开启自动配置的登录功能 // /login 请求来到登录页 // /login?error 重定向到这里表示登录失败 http.formLogin();
-
测试一下:发现,没有权限的时候,会跳转到登录的页面!
-
定制认证规则
- 重写configure(AuthenticationManagerBuilder auth)方法
同时我们要将前端传过来的密码进行某种方式加密,否则就无法登录
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("client").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
从jdbc中拿数据的配置:
- 测试,发现,登录成功,并且每个角色只能访问自己认证下的规则!
- 开启自动配置的注销的功能
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//开启自动配置的注销的功能
// /logout 注销请求
http.logout();
}
- 我们在前端,增加一个注销的按钮
注销
- 我们可以去测试一下,登录成功后点击注销,发现注销完毕会跳转到登录页面!
- 注销成功后,依旧可以跳转到首页的设置
// .logoutSuccessUrl("/"); 注销成功来到首页
http.logout().logoutSuccessUrl("/");
- 我们现在又来一个需求:用户没有登录的时候,导航栏上只显示登录按钮,用户登录之后,导航栏可以显示登录的用户信息及注销按钮!还有就是,比如admin这个用户,它只有 vip2,vip3功能,那么登录则只显示这两个功能,而vip1的功能菜单不显示!
我们需要结合thymeleaf中的一些功能导入thymeleaf-springsecurity整合依赖:
org.thymeleaf.extras thymeleaf-extras-springsecurity5 3.0.4.RELEASE
- 修改前端页面
- 导入命名空间
-
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
- 修改导航栏,增加认证判断
未登录导航栏为请登录,登陆后导航栏为用户名和注销
若不支持可能是因为spirngboot版本过高问题,
- 如果注销404了,就是因为它默认防止csrf跨站请求伪造,因为会产生安全问题,我们可以将请求改为post表单提交,或者在spring security中关闭csrf功能;
http.csrf().disable(); //关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
- 继续将下面的角色功能块认证完成!
12.3、定制登录页
- 刚才的登录页配置后面指定 loginpage
http.formLogin().loginPage("/toLogin");
- 然后前端也需要指向我们自己定义的 login请求
登录
- 我们登录,需要将这些携带的信息发送到哪里,我们也需要配置,login.html 配置提交请求及方式,方式必须为post:
- 这个请求提交上来,我们还需要验证处理,我们可以查看formLogin()方法的源码!我们配置接收登录的用户名和密码的参数(默认接收的参数是username,password),我们可以在这里修改,和前端传给参数名一致;
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLogin")
.loginProcessingUrl("/login"); // 登陆表单提交请求
- 在登录页增加记住我的多选框
记住我
- 后端验证处理
//定制记住我的参数!
http.rememberMe().rememberMeParameter("remember");
12.4、完整配置代码
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限会直接到登陆页
http.formLogin();
//关闭跨站请求伪造csrf功能
http.csrf().disable();
//注销
http.logout().logoutSuccessUrl("/");
//开启记住我功能
http.rememberMe();
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("client").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
}



