文章目录
特别感谢B站的狂神说Java
前景介绍,马丁弗列写的论文
SpringBoot创建失败最终解决方案
狂神说SpringBoot01:Hello,World!
狂神说SpringBoot02:运行原理初探
狂神说SpringBoot03:yaml配置注入
狂神说SpringBoot04:JSR303数据校验及多环境切换
狂神说SpringBoot05:自动配置原理
狂神说SpringBoot06:自定义starter
狂神说SpringBoot07:整合JDBC
狂神说SpringBoot08:整合Druid
狂神说SpringBoot09:整合MyBatis
狂神说SpringBoot10:Web开发静态资源处理
狂神说SpringBoot11:Thymeleaf模板引擎
狂神说SpringBoot12:MVC自动配置原理
狂神说SpringBoot13:页面国际化
狂神说SpringBoot14:集成Swagger终极版
狂神说SpringBoot15:异步、定时、邮件任务
狂神说SpringBoot16:富文本编辑器
狂神说SpringBoot17:Dubbo和Zookeeper集成
狂神说SpringBoot18:集成SpringSecurity
1.自动装配原理示意图
2.静态资源导入研究
总结:
- 在springboot,我们可以使用一下方式处理静态资源
- webjars
- public ,static, /**,resources
- 优先级:resource>static(默认)>public
E:\Environment\apache-maven-3.8.1\maven-repo\org\springframework\boot\spring-boot-autoconfigure\2.3.7.RELEASE\spring-boot-autoconfigure-2.3.7.RELEASE.jar!\org\springframework\boot\autoconfigure\web\servlet\WebMvcAutoConfiguration.class
3.首页配置和头像
在本丢资源目录下resource/static(默认)/public中新建一个index.html就可以被识别为首页
4.模板引擎
Thymeleaf中文文档
结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了!我们将html放在我们的templates目录下即可!
<!--Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
使用thymeleaf
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--所有的html元素都可以被thymeleaf替换接管 th:元素名-->
<div th:text="${msg}"></div>
</body>
</html>
5.MVC配置原理
//如果你想diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//全面扩展SpringMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// public interface ViewResolver 实现了视图解析器接口的类,我们就可以把它看做视图解析器
@Bean
public ViewResolver MyViewResolver(){
return new MyViewResolver();
}
//自定义了一个自己的视图解析器
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
在springboot中,有非常多的xxxxAutoConfiguration帮助我们进行扩展配置,只要看见了这个东西,我们就要注意了
6.搭建一个员工管理网页
7.自己的一些任务(One month)
8.SpringSecurity
- 引入 Spring Security 模块
- 编写 Spring Security 配置类
参考官网:https://spring.io/projects/spring-security - 编写基础配置类
- 定制请求的授权规则
- 定义认证规则
//AOP : 拦截器
@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()
.loginPage("/toLogin")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login");
http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
// 开启注销功能
http.logout().logoutSuccessUrl("/");
//开启记住我功能
http.rememberMe().rememberMeParameter("rememberme");
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("kuangshen").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
}
9.Shiro
从外部来看Shiro,即从应用程序角度来观察如何使用Shio完成工作:
- Subject 用户
- SecurityManager 管理用户
- Realm 连接数据
- 导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
- 编写Shiro配置类
@Configuration
public class ShrioConfig {
//ShiroFilterFactoryBean : Step3
@Bean
public ShiroFilterFactoryBean getShrioFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
return bean;
}
//DefaultWebSecurityManager : Step2
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建 realm 对象, 需要自定义类:Step1
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
- 自定义UserRealm
//自定义的 UserRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
一个小Demo:
- 导入依赖
- springboot-mybatis整合
- shiro-thymelea整合
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kuang</groupId>
<artifactId>shiro-springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro-springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--shiro-thymeleaf整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<!--引入mybatis,这是Mybatis官方提供的适配SpringBoot的,而不是SpringBoot自己的-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
-
整合MyBatis
1. 编写实体类
2. 编写mapper接口、mapper.xml、application.yml配置mybatis(别名,mapper.xml文件位置)
3. 编写service接口,serviceImpl类
-
编写Shiro配置类
@Configuration
public class ShrioConfig {
//ShiroFilterFactoryBean : Step3
@Bean
public ShiroFilterFactoryBean getShrioFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anno: 无需认证就可以访问
authc: 必须认证了才可以访问
user: 必须拥有 记住我 功能才能用
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//用户授权,正常情况下没有授权会跳转到授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
//拦截
//filterMap.put("/user/add","authc");
//filterMap.put("/user/update","authc");
filterMap.put("/user/*","authc");
//设置登录请求
bean.setLoginUrl("/toLogin");
//设置未授权页面
bean.setUnauthorizedUrl("/noauth");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//DefaultWebSecurityManager : Step2
@Bean("securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建 realm 对象, 需要自定义类:Step1
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合 ShiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
- 自定义UserRealm
//自定义的 UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 => doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
// 拿到User对象
User currentUser = (User) subject.getPrincipal();
// 设置当前用户的权限
System.out.println(currentUser.getName() + "的权限为 " + currentUser.getPerms());
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了 => 认证AuthenticationToken");
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
//连接真实的数据库
User user = userService.queryUserByName(userToken.getUsername());
if(user == null){
//没有这个人
return null; //抛出异常 UnknownAccountException
}
// 登录成功 将用户信息存入session
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",user.getName());
// 密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
github无法登陆
10.开源项目分析
11.Swagger
学习目标:
了解Swagger的作用和概念
了解前后端分离
在SpringBoot中集成Swagger
1.Swagger简介
前后端分离时代:
-
后端:后端控制层,服务层,数据访问层
-
前端:前端控制层,视图层
Swagger : -
号称世界上最流行的API框架;
-
RestFul Api文档在线自动生成工具 => Api文档与Api定义同步更新
-
直接运行,可以在线测试API接口
-
支持多种语言:(Java , Php…)
在项目中使用Swagger需要Springbox
swagger2
ui
2.SpringBoot集成Swagger
- 新建一个SpringBoot - web 工程
- 导入相关依赖
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
- 编写一个Hello工程
- 配置Swagger ==> Config
@Configuration
@EnableSwagger2 //开启Swagger
public class SwaggerConfig {
}
- 测试运行:http://localhost:8080/swagger-ui.html
3.配置Swagger信息
@Configuration
@EnableSwagger2 //开启Swagger
public class SwaggerConfig {
//配置了Swagger 的Docket的bean实例
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
//配置Swagger信息=apiInfo
public ApiInfo apiInfo(){
//作者信息
Contact contact = new Contact("Daniel","https://www.baidu.com","denglianqing@qq.com");
return new ApiInfo("Dainel的SwaggerAPI文档",
"天道酬勤",
"v1.0",
"urn:tos",
contact, "Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
4.Swagger配置扫描接口
Docket.select()
//配置了Swagger的Docket的bean实例
//enable是否启动swagger,如果为False则Swagger不能在浏览器访问
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//RequestHandlerSelectors,配置要扫描接口的方式
//basePackage:指定要扫描的包
//any():扫描全部
//none():不扫描
//.withClassAnnotation():扫描类上的注解
//.withMethodAnnotation():扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("com.kuang.controller"))
//paths()过滤什么路径
/*.paths(PathSelectors.ant("/kuang/**"))*/
.build();
}
我只希望我的Swagger在生产环境中使用,在发布的时候不使用?
判断是不是生产环境 flag=false
注入enable(flag)
//配置了Swagger 的Docket的bean实例
@Bean
public Docket docket(Environment environment){
//设置要显示的Swagger环境
Profiles profiles = Profiles.of("dev","test");
//通过 environment.acceptsProfiles 判断是否处在自己设定的环境中
boolean flag = environment.acceptsProfiles(profiles);
...
}
5.配置API文档的分组
.groupName("狂神")
如何配置多个分组;多个Docket实例即可
6.接口注释
总结:
- 我们可以通过Swagger给一些比较难理解的属性或者接口,增加注释信息;
- 接口文档实时更新
- 可以在线测试
Swagger是一个优秀的工具,几乎所有的大公司都有使用它
【注意点】在正式发布的时候,关闭Swagger!!出于安全考虑,而且节省运行的内存。
12.任务
1.异步任务
- 给要实现异步任务加上注解
@Async
@Service
public class AsyncService {
@Async
public void hello(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("数据正在处理...");
}
}
- 在main方法开启异步功能
@EnableAsync
@EnableAsync // 开启异步注解功能
@SpringBootApplication
public class Springboot09TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot09TaskApplication.class, args);
}
}
2.邮件任务
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
- 配置邮箱和密码
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
- 测试发送邮件(组合邮件,带附件)
@SpringBootTest
class Springboot09TaskApplicationTests {
@Autowired
JavaMailSenderImpl mailSender;
@Test
void contextLoads2() throws MessagingException {
//一个复杂的邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
//组装
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
//正文
helper.setSubject("Test");
helper.setText("<h2 style='color:red'> 这是一封测试邮件 </h2>",true);
//附件
helper.addAttachment("data1",new File("D:\\testdata\\2017-01-01.xls"));
helper.setTo("dishifu@126.com");
helper.setFrom("514906666@qq.com");
mailSender.send(mimeMessage);
}
3.定时任务
TaskScheduler 任务调度者
TaskExecutor 任务执行者
@EnableScheduling 开启定时功能的注解
@Scheduled 什么时候执行
Cron表达式
- 编写定时服务
@Service
public class ScheduledService {
//在一个特定的时间执行这个方法
//cron 表达式
//秒 分 时 日 月 周几
/*
30 17 17 * * ? 每天10点15分30 执行一次
30 0/5 10,18 * * ? 每天10点和18点,每隔五分钟执行一次
*/
@Scheduled(cron = "30 17 17 * * ?")
public void Hello(){
System.out.println("hello,被执行了!");
}
}
- 在启动类开启定时功能
@EnableAsync // 开启异步注解功能
@EnableScheduling //开启定时功能的注解
@SpringBootApplication
public class Springboot09TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot09TaskApplication.class, args);
}
}
13.分布式Dubbo+Zookeeper+SpringBoot
HTTP SpringCloud(生态)
RPC两个核心模块:通讯、序列化介绍
序列化:数据传输需要转换
Netty:30天~
Dubbo~18年重启! Dubbo 3.x RPC Error Exception
专业的事,交给专业的人来做~
- 需要导入的依赖:
<!--导入依赖:Dubbo+zookeeper-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--zkclient-->
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
</dependency>
<!--日志会冲突-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.2</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
- 配置注册中心的地址,以及服务发现名,和要扫描的包
#服务应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#哪些服务要被注册
dubbo.scan.base-packages=com.mi.service
- 在想要被注册的服务上面~增加一个注解@Service
14.聊聊现在和未来
回顾以前,架构
三层架构 + MVC
架构 ---> 解耦
开发框架
Spring
IOC AOP
IOC:控制反转
约泡:
泡温泉,泡茶。。。。泡友
附近的人,打招呼,加微信,聊天-------->约泡
浴场:温泉,茶庄,泡友
直接进温泉,就有人和你一起了
原来我们都是自己一步步操作,现在交给容器了,我们需要什么就去拿就可以了
AOP:面向切面编程 (本质,动态代理)
为了解决什么?不影响业务本来的情况下,实现动态增加功能,大量应用在日志,事务...等等方面
Spring是一个轻量级的java开源框架,容器
目的:解决企业开发的复杂性问题
Spring是春天,觉得他是春天,也十分复杂,配置文件
SpringBoot
SpringBoot并不是新东西,就是Spring的升级版!
新一代JavaEE的开发标准,开箱即用!->拿过来就可以用!
它自动帮我们配置了非常多的东西,我们拿来即用!
特性:约定大于配置
随着公司的体系越来越大,用户越来越多
微服务架构--->新架构
模块化,功能化!
用户模块,支付模块,签到模块,娱乐模块
人过于多:一台服务器解决不了;再增加服务器!横向
假设A服务器占用98%资源,B服务器只占用了10%。---负载均衡;
将原来的整体项目,分成模块化,用户就是一个单独的项目,签到也是一个单独的项目,项目和项目之间需要通信,如何通信?
用户非常多,而签到十分少! 给用户多一点服务器,给签到少一点服务器!
微服务架构问题?
分布式架构会遇到的四个核心问题?
1. 这么多服务,客户端该如何去访问?
2. 这么多服务,服务之间如何进行通信?
3. 这么多服务,如何治理呢?
4. 服务挂了,怎么办?
解决方案:
SpringCloud,是一套生态,就是解决以上分布架构的4个问题
想使用SpringCloud,必须掌握SpringBoot,因为SpringCloud是基于SpringBoot;
1.Spring Cloud Netflix,出来了一套解决方案
Api网关,zuul组件
Feign--->HttpClient--->HTTP的通信方式,同步并阻塞
服务注册与发现,Eureka
熔断机制,Hystrix
2018年年底,Netflix宣布无限期停止维护。生态不再维护,就会脱节。
2.Apache Dubbo zookeeper,第二套解决系统
Api:没有!要么找第三方插件,要么自己实现
Dubbo是一个高性能的基于Java实现的,RPC通信框架!
服务注册与发现,zookeeper:动物园管理者(Hadoop,Hive)
没有:借助了Hystrix
不完善,Dubbo
3. SpringCloud Alibaba 一站式解决方案
目前,又提出了一种方案:
服务网格:下一代微服务标准。Server Mesh
代表解决方案:Istio
万变不离其宗,一通百通
1.API网关,服务路由
2.HTTP,RPC框架,异步调用
3.服务注册与发现,高可用
4.熔断机制,服务降级
如果,你们基于这四个问题,开发一套解决方案,也叫SpringCloud!
为什么要解决这个问题?本质:网络不可靠
程序员,不能停下学习的脚步