SpringBoot

Spring Boot

Spring Boot

一、SpringBoot 简介

1.起源

首先Spring是一个2003 年兴起的轻量级的开源的Java 开发框架,Spring是为了解决企业级应用开发的复杂性而创建的,主要就是简化开发。
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

1、基于POJO的轻量级和最小侵入性编程,所有东西都是bean;

2、通过IOC,依赖注入(DI)和面向接口实现松耦合;

3、基于切面(AOP)和惯例进行声明式编程;

4、通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;

2.发展

开发一个web应用,从最初开始接触Servlet结合Tomcat, 跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot。

人们把一个复杂应用场景衍生成一种规范框架,从而只需要进行各种配置就可以应用,而不需要自己去实现它,这时候强大的配置功能就成为了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,就会感觉到各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。这就是Java企业级应用=>J2EE=>spring=>springboot的过程。

SpringBoot就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,就是进一步简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码就可以开发一个http接口。

3.出现背景

随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配置各种各样的文件,使其慢慢变得不那么简单易用,违背了最初的设计理念,堪称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的就为了让大家更容易的使用 Spring ,简化配置,更容易的集成各种常用的中间件、开源软件。

4.主要内容

Spring Boot是基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的一个工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多配置,多数 Spring Boot 应用只需要很少的 Spring 配置,进一步简化开发。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置直接使用。
SpringBoot其实不是新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
主要优点:

  • 更快地使用SpringBoot
  • 内嵌式容器简化WEB项目
  • 没有冗余的代码生成和XML配置
  • 提供各种默认配置,从而简化了项目的配置

二、创建一个SpringBoot项目

1.方法一:从官网创建

官网配置项目入口:Project=>SpringBoot=>OVERVIEW最下面 =>Quickstart Your Project=> Spring Initializr Spring初始化软件(器)
链接:https://start.spring.io/

在这里插入图片描述
会下载出一个压缩包,解压压缩包,在IDEA中import maven项目,就是这个项目,就完成创建了。

2.方法二:在IDEA中创建

直接选择Spring Initializer创建就可以,也可以先创建一个Empty project,再在里面新建Module,在Module里面选择Spring Initializer进行创建。这里直接Spring Initializer创建了。
在这里插入图片描述
可以选择Spring Web,也可以后面添加
在这里插入图片描述在这里插入图片描述
这个XXXApplication.java就是主启动类
里面psvm 调用了SpringApplication的run方法
这个controller等一定要和主启动类在同级目录,否则扫描不到

在application.properties中配置一下服务器端口,因为我的8080被占用了。
在这里插入图片描述

server.port=8088

现在就可以启动服务了,确实节省了不少配置
在这里插入图片描述如果想要换一下项目启动时候的启动banner,就是这个
在这里插入图片描述可以在网站生成(这种网站很多)
https://www.bootschool.net/ascii
在这里插入图片描述

在resources目录下新建一个banner.txt的文件,将拷贝结果放进去
在这里插入图片描述

在这里插入图片描述
重新启动
在这里插入图片描述
SpringBoot可以很简单的完成了一个web接口的开发,所以我们可以用它来建立我们的微服务项目。

最后将这个项目打成jar包就可以了,后面就是用好多个jar包。这个jar包占用一个端口,后面微服务要占用很多个端口。

打包的时候如果遇到错误,可以在配置打包时,跳过项目运行测试用例

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!--跳过项目运行测试用例-->
        <skipTests>true</skipTests>
    </configuration>
</plugin>

三、运行原理

1.application注解

2.run方法

四、配置

SpringBoot 官方推荐使用yaml文件进行配置,

1.了解yaml与properties

properties只可以存键值对

server.port=8088

properties中文乱码解决
在这里插入图片描述

yaml可以存键值对、对象、数组,而properties只能存键值对

server:
  port: 8088
#yaml可以存对象
User:
  userName: hdat
  userPwd: hdat
#对象行内写法
Admin: {AdminName: hdat,AdminPwd: hdat}

#数组
info:
  - name
  - address
  - age
  - phone
  - sex

#数组
418home: [me,jj,hh,kk,ss,ll]

yaml的语法很严格

  • :冒号后面的空格一定不能丢
  • 他是通过缩进来控制层级关系的
  • key和value的大小写敏感

2.SpringBoot的配置文件

SpringBoot使用一个全局的配置文件 , 配置文件的名称是固定的
application。
这个文件可以是properties文件也可以是yaml文件。

语法结构:

application.properties

  • key = value

application.yaml

  • key : value

配置文件的作用 :修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了,我们需要结合自身情况进行修改使用。

3.配置文件方式给bean注入属性

yaml文件更加强大的地方在于,它可以给我们的实体类直接一 一对应的注入值。
yaml配置文件

首先新建一个实体类User

//@Component 就是把User这个实体类添加到Spring组件中去,即注册Bean到spring这个容器中
@Component
public class User {
    private String userName;
    private String userPwd;
    //有无参构造
    //setter getter 
  }

*之前我们如果想要给bean注入属性值,可以使用@Value

@Component
public class User {
    @Value("hdat")
    private String userName;
    @Value("123")
    private String userPwd;
    //有无参构造
    //setter getter 
  }

测试
在这里插入图片描述

结果
在这里插入图片描述*现在我们可以通过yaml配置文件的方式来给bean注入属性值

@Component
@ConfigurationProperties(prefix = "user")
public class User {
    private String userName;
    private String userPwd;
    //有无参构造
    //setter getter 
  }
@ConfigurationProperties:默认从全局配置文件中获取值,将配置文件中配置的每一个属性的值,映射到这个组件中;告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定参数
prefix = “user” : 将配置文件中的user下面的所有属性一一对应
@PropertySource(value = "classpath:user.properties"):加载指定的配置文件,指定加载user.properties文件
User:
  userName: hdat
  userPwd: hdat

测试
在这里插入图片描述
结果
在这里插入图片描述

五、JSR303数据校验

Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
111111111111111
常见参数

@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       验证 DateCalendar 对象是否在当前时间之前  
@Future     验证 DateCalendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

六、多环境配置

profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境。

1.properties多环境配置

我们可以在类路径下创建多个application.properties文件,例如
application.properties
application-dev.properties 代表开发环境配置
application-text.properties 代表测试环境配置

Springboot并不会直接去启动这些配置文件,而是默认使用application.properties主配置文件

如果我们需要切换配置,需要在主配置文件中添加一个配置来激活

#指定使用dev环境
spring.profiles.active=dev

2.yaml多环境配置

使用yml去实现多环境配置则不需要去创建多个配置文件,更加方便

只需在主配置文件中配置即可,用—来进行分割

#选择激活哪个配置,这里还是指定dev环境,如果不指定就是默认的
spring:
  profiles:
    active: dev
server:
  port: 8082
---
spring:
  profiles: dev
server:
  port: 8083
---
spring:
  profiles: text
server:
  port: 8084

另外:
如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件,挺意外的,我一开始感觉会是走yaml

3.配置文件加载位置

可以在项目中不同的位置创建application.properties文件,有优先级

  • 根目录下的config目录中
  • 根目录下
  • 类路径下的config目录中
  • 类路径下(默认)

springboot 启动会扫描这四个位置的application.properties或者application.yml文件来作为Spring boot的默认配置文件

七、自动配置原理

八、Springboot Web开发

1.静态资源导入

静态资源映射规则一
SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面
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/**")) {
       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));
   }
}

所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源
Webjars本质就是以jar包的方式引入我们的静态资源 , 我们以前要导入一个静态资源文件,直接导入即可。

使用SpringBoot需要使用Webjars,我们可以去官网:https://www.webjars.org

如果我们要使用jQuery,我们只要要引入jQuery对应版本的pom依赖即可

<dependency>
    <groupId>org.webjars.npm</groupId>
    <artifactId>jquery</artifactId>
    <version>3.6.1</version>
</dependency>

可以看一下webjars目录结构,看他是如何访问Jquery.js文件的
在这里插入图片描述Jquery.js在src下

在这里插入图片描述只要是静态资源,SpringBoot就会去对应的路径寻找资源,我们这里访问:/webjars/**(http://localhost:8088/webjars/jquery/3.6.1/src/jquery.js)
在这里插入图片描述就会去/resources/webjars/下面去找资源
资源位置就是/resources/webjars/jquery/3.6.1/src/jquery.js

静态资源映射规则二
如果要是使用自己的静态资源该怎么导入
在staticPathPattern有第二种映射规则 :/** , 访问当前的项目任意资源,它会去找 resourceProperties 这个类

// 进入方法
public String[] getStaticLocations() {
    return this.staticLocations;
}
// 找到这个返回值
private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
// 看看这个值里面是什么
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { 
    "classpath:/META-INF/resources/",
    "classpath:/resources/", 
    "classpath:/static/", 
    "classpath:/public/" 
};

ResourceProperties 可以设置和我们静态资源有关的参数;这里面指向了它会去寻找资源的文件夹,即上面数组的内容
所以在数组中的四个目录下存放的静态资源可以被识别到
在这里插入图片描述

resources下的resource就是类路径下的
对应的就是"classpath:/resources/"

比如我们访问 http://localhost:8088/hello.js , 他就会去这些文件夹中寻找对应的静态资源文件
在这里插入图片描述访问优先级
“classpath:/resources/” > “classpath:/static/”(默认) > “classpath:/public/”

自定义静态资源映射规则

一旦自己定义了静态文件夹的路径,原来的自动配置就都会失效了

if (!this.resourceProperties.isAddMappings()) { //这里不是很理解这个逻辑
       // 已禁用默认资源处理
       logger.debug("Default resource handling disabled");
       return;
   }

通过配置文件来指定,哪些文件夹是我们放静态资源文件的,在application.properties中配置

spring.resources.static-locations=classpath:/myhello/,classpath:/hello/

2.首页配置

index.html可以在public,static,resources中

网站图标的使用

Spring Boot在配置的静态内容位置中查找 favicon.ico。如果存在这样的文件,它将自动用作应用程序的favicon。
1、关闭SpringBoot默认图标

#关闭默认图标
spring.mvc.favicon.enabled=false

2、自己放一个图标在静态资源目录下,命名favicon.ico

3.Thymeleaf模板引擎

SpringBoot是以jar的方式打包,不是war,而且还是内嵌的Tomcat,所以,他现在默认是不支持jsp的。

其实jsp就是一个模板引擎,而SpringBoot推荐我们使用Thymeleaf模板引擎

模板引擎的原理思想都是一样的
在这里插入图片描述
模板引擎的原理就是比如我们写了一个页面模板,里面有些值是动态的,这些动态的值用了一些表达式来表示。而这些值,就是在后端封装了并想要在前端展示或返回给前端的那些数据。现在把这个模板和这些数据交给模板引擎,模板引擎就会解析页面中的这些表达式,然后把这些数据填充到指定的位置上去,最终生成一个有后端返回数据的前端页面输出出来。

Thymeleaf的使用

官网的没找到好使的,spring官网上没链接了,去GitHub上找
https://github.com/thymeleaf/thymeleaf

<!--thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>

Thymeleaf 自动配置类
ThymeleafProperties
在这里插入图片描述页面放在templates下
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

Thymeleaf的语法
官方文档
https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-texts

如何使用Thymeleaf,首先导入命名空间的约束,方便提示,然后只需要在html标签上使用th:就可以了
后端存入数据

@Controller
public class text {
    @RequestMapping("/test")
    public String texts(Model model){
        model.addAttribute("names","hdat");
        return "test";
    }
}

前端展示数据

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div th:text="${names}"></div>
</body>
</html>

在这里插入图片描述
这个就是thymeleaf的简单使用,还有很多其他语法比如条件判断,遍历输出等

遍历输出

@Controller
public class text {
    @RequestMapping("/test")
    public String texts(Model model){
        model.addAttribute("names","hdat");
        model.addAttribute("users", Arrays.asList("hdat1","hdat2"));
        return "test";
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div th:text="${names}"></div>
<hr>
<div th:each="user:${users}" th:text="${user}"></div>
</body>
</html>

在这里插入图片描述

4.MVC自动配置原理

Spring MVC Auto-configuration
// Spring Boot为Spring MVC提供了自动配置,它可以很好地与大多数应用程序一起工作。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
// 自动配置在Spring默认设置的基础上添加了以下功能:
The auto-configuration adds the following features on top of Spring’s defaults:
// 包含视图解析器
Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
// 支持静态资源文件夹的路径,以及webjars
Support for serving static resources, including support for WebJars 
// 自动注册了Converter:
// 转换器,这就是我们网页提交数据到后台自动封装成为对象的东西,比如把"1"字符串自动转换为int类型
// Formatter:【格式化器,比如页面给我们了一个2019-8-10,它会给我们自动格式化为Date对象】
Automatic registration of Converter, GenericConverter, and Formatter beans.
// HttpMessageConverters
// SpringMVC用来转换Http请求和响应的的,比如我们要把一个User对象转换为JSON字符串,可以去看官网文档解释;
Support for HttpMessageConverters (covered later in this document).
// 定义错误代码生成规则的
Automatic registration of MessageCodesResolver (covered later in this document).
// 首页定制
Static index.html support.
// 图标定制
Custom Favicon support (covered later in this document).
// 初始化数据绑定器:帮我们把请求数据绑定到JavaBean中!
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).

/*
如果您希望保留Spring Boot MVC功能,并且希望添加其他MVC配置(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己
的@configuration类,类型为webmvcconfiguer,但不添加@EnableWebMvc。如果希望提供
RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义
实例,则可以声明WebMVCregistrationAdapter实例来提供此类组件。
*/
If you want to keep Spring Boot MVC features and you want to add additional MVC configuration 
(interceptors, formatters, view controllers, and other features), you can add your own 
@Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide 
custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or 
ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

// 如果您想完全控制Spring MVC,可以添加自己的@Configuration,并用@EnableWebMvc进行注释。
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc.

5.扩展SpringMVC

6.自定义starter

九、员工管理系统

1.Thymeleaf静态资源加载

静态资源放在static目录下
页面放在templates下

跳转到首页
方法一
controller
方法二
扩展MVC添加视图控制

@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}

修改页面中的静态资源连接
首先在页面中添加命名空间
然后修改链接

<link href="asserts/css/bootstrap.min.css}" rel="stylesheet">

改为

<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">

在thymeleaf 中超链接用 @{}

然后关闭模板引擎的缓存。在调试的时候,需要关闭缓存,否则不能看到实时信息

spring:
  #关闭吗模板引擎的缓存
  thymeleaf:
    cache: false

2.国际化

file Encodiing 修改所有编码为UTF-8

在这里插入图片描述
resources下新建i18n目录

在这里插入图片描述
然后在下面新建配置文件

在这里插入图片描述

可以使用可视化配置,需要先去下载个插件Resource Bundle Editor
在Plugins中搜索Resource Bundle Editor插件然后安装
如果显示
在这里插入图片描述
可以把网络换成手机热点试试
安装完成之后
在这里插入图片描述
现在在properties的那个页面左下角,就可以选择
在这里插入图片描述
选择Resource Bundle就可以进行可视化编辑了
在这里插入图片描述
写完配置文件后,需要实现一下LocaleResolver接口
新建一个类
在这里插入图片描述
地区解析器 可视化配置

public class MyLocaleResolver implements LocaleResolver {
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault();//如果没有就使用默认的
        //如果请求链接携带了国际化参数
        if (!StringUtils.isEmpty(language)) {
            //zh_CN
            String[] split = language.split("_");
            //国家 地区
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

前面切换按钮,让他跳转当前页并携带参数(选择那种语言)

<a class="btn btn-sm" th:href="@{/index(l=zh_CN)}">中文</a>
<a class="btn btn-sm" th:href="@{/index(l=en_US)}">English</a>

现在需要把这个配置类放到Bean中
在MVC扩展文件MyMvcConfig中配置

//自定义国际化的组件生效
    @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }

总结:
1.配置i18n文件
2.如果进行按钮切换,就自定义一个组件LocaleResolver
3.把自己写的这个组件配置到Spring容器中

3.登录拦截器

创建一个类,继承HandlerInterceptor
在这里插入图片描述

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //登录成功之后,应该有用户的session
        Object loginUser = request.getSession().getAttribute("loginUser");
        if (loginUser == null) {
            request.setAttribute("msg","404");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else {
            return true;
        }
    }
}

true是放行,false是不放行

在MyMvcConfig中配置

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/index.html","/","/index","/user/login","/css/*","/js/**","/img/**");
    }

3.提取公共页面

1.在被提取的组件中添加 (这里写一个公共的页面,只放一些公共的组件,resources->commons->commons.html)

th:fragment="sidebar"

2.在使用的页面中复用

th:replace="~{commons/commons::sidebar}"

3.如果要传递参数,使用()传参

十、整合JDBC

1.SpringData

对于数据访问层,无论是 SQL(关系型数据库) 还是 NOSQL(非关系型数据库),Spring Boot 底层都是采用 Spring Data 的方式进行统一处理。

Spring Data 也是 Spring 中与 Spring Boot、Spring Cloud 等齐名的知名项目。

2.整合JDBC

新建项目
勾选
在这里插入图片描述在这里插入图片描述
这两个一定要勾选

数据库配置

server:
  port: 8088
spring:
  datasource:
    username: root
    password: 2595
    url: jdbc:mysql://localhost:3308/jdoa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver

很多xxxTemplate : 这些事是springboot 已经配置好的一些模板bean,拿来即用

配置完这一些东西后,我们就可以直接去使用了,因为SpringBoot已经默认帮我们进行了自动配置 ,可以去测试类测试一下

    @Autowired
    DataSource dataSource;
    @Test
    void contextLoads() {
        System.out.println(dataSource.getClass());//默认数据源com.zaxxer.hikari.HikariDataSource
        //获取数据库连接
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        System.out.println("connection"+connection);
        try {
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
HikariDataSource 号称 Java WEB 当前速度最快的数据源,相比于传统的 C3P0 、DBCP、Tomcat jdbc 等连接池更加优秀

可以使用 spring.datasource.type 指定自定义的数据源类型,值为要使用的连接池实现的完全限定名。

2.JdbcTemplate

有了数据源(com.zaxxer.hikari.HikariDataSource),就可以拿到数据库连接(java.sql.Connection),有了连接,就可以使用原生的 JDBC 语句来操作数据库

即使不使用第三方第数据库操作框架,如 MyBatis等,Spring 本身也对原生的JDBC 做了轻量级的封装,即JdbcTemplate。数据库操作的所有 CRUD 方法都在 JdbcTemplate 中。

Spring Boot 不仅提供了默认的数据源,同时默认已经配置好了 JdbcTemplate 放在了容器中,程序员只需自动注入即可使用,JdbcTemplate 的自动配置是依赖 org.springframework.boot.autoconfigure.jdbc 包下的 JdbcTemplateConfiguration 类

JdbcTemplate提供的几个方法:

  • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句(数据定义语言);
  • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
  • query方法及queryForXXX方法:用于执行查询相关语句;
  • call方法:用于执行存储过程、函数相关语句。

编写一个Controller,注入 JdbcTemplate,进行数据库连接访问测试

@RestController
public class JDBCController {
    @Autowired
    JdbcTemplate jdbcTemplate;
    //查询数据库的所有信息
    //没有实体类的话,数据库中的数据可以通过map获取出来
    @RequestMapping("/userList")
    public List<Map<String,Object>> userList(){
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

在这里插入图片描述
关于@RestController注解

在Spring中@RestController的作用等同于@Controller + @ResponseBody。
@Controller注解:在一个类上添加@Controller注解,表明了这个类是一个控制器类。
@ResponseBody:表示方法的返回值直接以指定的格式写入Http response body中,而不是解析为跳转路径。如果想要方法直接返回结果,而不是跳转页面,这就要用到@ResponseBody注解了。

如果要求方法返回的是json格式数据,而不是跳转页面,可以直接在类上标注@RestController,而不用在每个方法中标注@ResponseBody,简化了开发过程。

十一、自定义数据源 使用DruidDataSource

1.简介

程序需要数据,数据来源于数据库,就要操作数据库,为了提高性能操作数据库的时候,就要使用数据库连接池。

Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等 DB 池的优点,同时加入了日志监控。它可以很好的监控 DB 池连接和 SQL 的执行情况,天生就是针对监控而生的 DB 连接池。

Spring Boot 2.0 以上默认使用 Hikari 数据源,可以说 Hikari 与 Driud 都是当前 Java Web 上最优秀的数据源,这里使用Spring Boot 集成 Druid 数据源,实现数据库监控

2.配置

1.添加依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.13</version>
</dependency>

2.切换数据源 通过 spring.datasource.type 指定数据源。

server:
  port: 8088
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3308/jdoa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

最后一行这个type就是切换到Druid数据源

3.测试一下
得到
class com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper

4.切换成功,就可以设置数据源连接初始化大小、最大连接数、等待时间、最小连接数 等设置项

    type: com.alibaba.druid.pool.DruidDataSource
    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

5.要使用一些私有化属性,需要自己将DruidDataSource和yaml配置文件进行绑定,然后在yaml中就可以配置DruidDataSource的一些私有化属性了

新建一个配置类DruidConfig,把DruidDataSource注入到Bean,不自动注入了,然后配置ConfigurationProperties使得全局配置文件中前缀为 spring.datasource的属性值注入到DruidDataSource的同名参数中,这样就可以通过在yaml全局配置文件中配置一些Druid的属性并生效。

@Configuration
public class DruidConfig {

     /*
       将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
       绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
       @ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
       前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource DruidDataSource(){
        return new DruidDataSource();
    }
}

然后就绑定起来了。DruidDatasource的私有属性就可以在yaml配置文件的spring.datasource下配置并生效了。

7.Druid数据源监控
因为SpringBoot 内置了servlet容器,所以没有web.xml,替代方法ServletRegistrationBean

	@Bean
	   public ServletRegistrationBean statViewServlet(){
	       ServletRegistrationBean<StatViewServlet> statViewServletServletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
	       //后台登录的用户名密码配置
	       HashMap<String,String> initParameter = new HashMap<>();
	       initParameter.put("loginUsername","admin");
	       initParameter.put("loginPassword","123456");//key是固定的
	       initParameter.put("allow","localhost");//允许谁可以访问  如果为空,就是所有人都可以访问 一般是具体一个人
	       statViewServletServletRegistrationBean.setInitParameters(initParameter);//设置初始化参数
	       return statViewServletServletRegistrationBean;
	   }

这时候会报错,还是要加一下log4j,不加看来是不行

<!-- log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

重启服务,访问localhost:8088/druid进入到登录页面
在这里插入图片描述
登录进入主页
在这里插入图片描述

还可以为Druid配置监控过滤器 filter

@Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean<Filter> filterFilterRegistrationBean = new FilterRegistrationBean<>();
        filterFilterRegistrationBean.setFilter(new WebStatFilter());//WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
        //过滤那些请求
        Map<String,String> initParameters = new HashMap<>();
        initParameters.put("exclusions","*.js,*.css,/druid/*");//不统计这些请求
        filterFilterRegistrationBean.setInitParameters(initParameters);
        return filterFilterRegistrationBean;
    }

十二、整合Mybatis

1.导入 MyBatis 所需要的依赖

<!-- mybatis-spring-boot-starter -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

2.配置数据库连接

server:
  port: 8088
spring:
  datasource:
    username: root
    password: 2595
    url: jdbc:mysql://localhost:3308/jdoa?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

3.创建一个实体类User

public class User {
    private int deptNo;
    private String deptName;
    private String location;
    //略
    }

4.创建mapper目录,就是dao层

在这里插入图片描述

5.在mapper中写mapper接口UserMapper

@Mapper//这个注解表示这是一个Mybatis的Mapper接口  或者在主启动类上加@MapperScan("com.hdat.xxx.mapper")注解,这个包下的所有就接口就被扫描进去了
@Repository
public interface UserMapepr {
    List<User> queryAll();
}

6.写mapper接口的映射文件

这个在resources目录下面写
在resources目录下新建mybatis目录,mybatis下面新建mapper目录
在mapper目录下面写mapper接口的映射文件UserMapper.xml
这个文件的主题内容可以到mybatis官网去找
链接https://mybatis.net.cn/getting-started.html

在这里插入图片描述

把这个拿过来放到xml文件中,并修改命名空间namespace

<?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.hdat.springboot04mybatis.mapper.UserMapepr">
    <select id="queryAll" resultType="User" useCache="true"><!--可以用缓存-->
        select * from dept;
    </select>
</mapper>

这样mapper接口就可以映射到这个xml配置文件了

但是现在resultType中的User类还无法被识别到,需要去设置别名,并且保证这个文件可以被Spring识别

核心配置文件中整合mybatis

#整合mybatis
mybatis:
  type-aliases-package: com.hdat.springboot04mybatis.pojo #别名
  mapper-locations: classpath:mybatis/mapper/*.xml #Spring识别

到现在整合mybatis就整合上了,dao层就结束了

我们就可以在service层去调用dao层了

现在先不用service层,直接写个controller去调用一下dao层测试一下

@RestController
public class UserController {
    @Autowired
    private UserMapepr userMapepr;

    @RequestMapping("/user")
    public String queryAll(){
        List<User> users = userMapepr.queryAll();
        System.out.println(users.toString());
        return "200";
    }
}

访问:http://localhost:8088/user

在这里插入图片描述在这里插入图片描述

总结就是 导入依赖->配置xml文件-yaml整合mybatis->业务层调用数据层->controller层调用业务层

十三、SpringSecurity

1.网站安全问题

安全问题非常重要,如果存在安全问题,再好的功能都是花拳绣腿。所以一定要做好安全问题。
在着手一个项目的时候,就要先考虑好安全问题,否则越往后越难受。

比较知名的两个安全框架 SpringSecurity、Shiro
主要就是做两个事,认证和授权

2.SpringSecurity简介

官网:https://spring.io/projects/spring-security

authentication and access-control
身份验证和访问控制

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security是一个功能强大、高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实上的标准。

Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。像所有的Spring项目一样,Spring Security的真正强大之处在于它可以轻松地进行扩展以满足定制需求


权限 一般会细分为功能权限,访问权限,和菜单权限。
Spring Security 是基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

认证(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

授权 (Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。

这两个概念是通用的,而不是只在Spring Security 中存在。

SpringSecurity在用户认证方面,支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。
SpringSecurity在用户授权方面, 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

3.SpringSecurity使用

SpringSecurity可以实现强大的Web安全控制,对于安全控制,仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理。

需要用到的几个类:

WebSecurityConfigurerAdapter:自定义Security策略(过时了好像)

AuthenticationManagerBuilder:自定义认证策略

@EnableWebSecurity:开启WebSecurity模式

1、添加Spring Security 依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、编写配置类

@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //首页所有人可以访问   功能页只有对应有权限的人才能访问
        //authorizeHttpRequests 授权Http请求  antMatchers ant匹配器
        http.authorizeHttpRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");
        //没有权限就跳转到登录页面上  这里跳转的是spring security的登录页面
        http.formLogin();
        //注销  注销后是否返回主页   这里有个疑问 他可以配置是否清除所有cookie,他是把浏览器所有的都删了还是只删除自己的 不配置注销会不会清除cookie
        http.logout().logoutSuccessUrl("/");
    }
    //认证
}

然后再去访问,点击功能页会跳转到登录页面

formLogin不需要配置RequestMapping,他自己就去找login页面了
所以这里全局只能有一个login.html,如果是其他登录的话,可以把链接放到这个页面。

注销访问链接
在这里插入图片描述

//认证
    //AuthenticationManagerBuilder 身份验证管理器构建器
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //这里应该是从数据库中读取 用jdbcAuthentication
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("hdat").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vip1","vip2","vip3");
    }

然后登录,这里登录提交给
在这里插入图片描述
login可以不在router中,这个是spring security的login
后面想要使用自己的页面 就需要把这个改为自己

然后不同的用户登上去就会有不同的访问权限了。

有个好处就是,给用户授权,不再需要像之前那样把用户信息放到session中去判断了,更安全。
(有时间测试postman携带session请求)

正常使用数据库
在这里插入图片描述有时间了可以弄一下

3.不同用户页面展示不同内容
导入依赖

<!--thymeleaf springsecurity5-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>

使用的页面添加命名空间

xmlns:sec="http://www.thymeleaf.org/extras/spring-security"

使用的时候就会有提示了

登录成功 显示注销按钮 没有登录 显示登录按钮

<!--未登录-->
<div sec:authorize="!isAuthenticated()">
    <a class="item" th:href="@{/toLogin}">
        <i class="address card icon"></i> 登录
    </a>
</div>
<!--已经登录-->
<div sec:authorize="isAuthenticated()">
    <!--展示登录者的用户名-->
    <a class="item">
        用户名:<span sec:authentication="name"></span>
    </a>
</div>
<!--已经登录-->
<div sec:authorize="isAuthenticated()">
    <!--展示注销-->
    <a class="item" th:href="@{/logout}">
        <i class="address card icon"></i> 注销
    </a>
</div>
</div>

如果注销不了,需要关闭csrf功能
http.csrf().disable();

<div class="column" sec:authorize="hasRole('vip1')">
   <div class="ui raised segment">
       <div class="ui">
           <div class="content">
               <h5 class="content">Level 1</h5>
               <hr>
               <div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
               <div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
               <div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
           </div>
       </div>
   </div>
</div>

只需要在要控制显示的div上添加sec:authorize=“hasRole(‘vip1’)”
就可以赋予这个div权限了,不同权限的用户只能看到对应权限的div

authorize – 授权 / 批准

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
4.记住我

//记住我
http.rememberMe();

5.不使用spring security的登录页

//没有权限就跳转到登录页面上
http.formLogin().loginPage("/toLogin");

login的表单提交给/toLogin
在这里插入图片描述
如果这里还是想要提在这里插入图片描述
交给springsecurity的login
在这里插入图片描述那么就要改配置为

http.formLogin().loginPage("/toLogin")
                .usernameParameter("userName")
                .passwordParameter("userPwd")
                .loginProcessingUrl("/login");

这样跳转的是自己的login页面,登录处理进的是springsecurity的login页面,这个我们看不到

需要注意的是参数问题 表单的参数如果不是人家默认的username或者password,那就要自己配置一下。

这里更改默认的login后会出现问题,注销不了了

http.csrf().disable();

需要关闭一下csrf就可以了

5.记住我的自定义

<input type="checkbox" name="rememberme">记住我
//记住我  cookie默认保存两周  自定有接收前端的参数
http.rememberMe().rememberMeParameter("rememberme");

在这里插入图片描述

十四、Shiro

1.Shiro简介

Apache Shiro是一个java的安全框架,可以非常容易的开发出足够好的应用,其不仅可以用在javase环境,也可以用在javaee环境。
它可以完成认证,授权,加密,会话管理,web集成,缓存等。

Shiro的三个主要对象
Subject 用户
SecurityManager 所有用户管理
Realm 连接数据

在这里插入图片描述

2.SpringBoot 整合Shiro

1.添加依赖

<!--shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.9.1</version>
        </dependency>

Authorizing – 授权

2.自定义类UserRealm 继承AuthorizingRealm
这个也写到config下

public class UserRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证");
        return null;
    }
}

3.写配置类ShiroConfig

@Configuration
public class ShiroConfig {
    //shiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterBean;
    }

    //DefaultWebSecurityManager 2
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }

    //创建realm对象,需要自定义类 1
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
}

@Autowire注解按照类型,即注解的字段的类型寻找该类型的实例bean,这种方式成为byType。这种方式会引发歧义,比如UserDAO整个框架可能有N多个该对象,那么Spring框架会采用一定规则寻找bean(转换为byName寻找,失败后报错)

@Qualifier(“XXX”) Spring的Bean注入配置注解,该注解指定注入的Bean的名称,Spring框架使用byName方式寻找合格的bean,这样就消除了byType方式产生的歧义。

4.访问权限,未登录无权访问,跳转到登录页面

@Configuration
public class ShiroConfig {
    //shiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterBean.setSecurityManager(defaultWebSecurityManager);
        //设置登录请求   无访问权限就跳到登录页面
        shiroFilterBean.setLoginUrl("/toLogin");
        //添加Shiro的内置过滤器
        /*
             anon:无需认证就可以访问
             authc:必须认证才可以访问
             user:必须拥有记住我功能才能访问
             perms:拥有对某个资源的权限才能访问
             role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/add","authc");
        filterMap.put("/update","anon");
        shiroFilterBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterBean;
    }

    //DefaultWebSecurityManager 2
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }

    //创建realm对象,需要自定义类 1
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

}

在这里插入图片描述add跳转到登录页,update正常访问

5.用户认证
登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p th:text="${loginmsg}" style="color: red"></p>
<form th:action="@{/login}">
    用户名:<input type="text" name="userName">
    密码:<input type="password" name="userPwd">
    <input type="submit">
</form>
</body>
</html>

写个登录的Controller

@RequestMapping("/login")
    public String login(String userName,String userPwd,Model model){
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken usernamePasswordtoken = new UsernamePasswordToken(userName, userPwd);
        //执行登录的方法
        try {
            subject.login(usernamePasswordtoken);
            return "index";
        } catch (UnknownAccountException e) {    //用户名不存在
            model.addAttribute("loginmsg","用户名或密码错误");
            return "login";
        } catch (IncorrectCredentialsException e) {    //密码不存在
            model.addAttribute("loginmsg","用户名或密码错误");
            return "login";
        }
    }

从前端获取到用户提交的用户名和密码,然后封装成Token令牌,然后执行用户登录的方法,把令牌传进去。
现在在UserRealm的对象的doGetAuthenticationInfo中就可以获取到这个TOKEN,然后在UserRealm中进行验证

//认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证");
        //从数据库中获取用户名和密码
        String userName = "hdat";
        String userPwd = "123";
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        if (!usernamePasswordToken.getUsername().equals(userName)) {
            return null; //抛出异常UnknownAccountException
        }
        //密码的验证由Shiro来做
        return new SimpleAuthenticationInfo("",userPwd,"");
    }

6.用户认证整合mybatis连接数据库 (完整)

*添加依赖

		<!--shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.9.1</version>
        </dependency>
        <!--mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- druid-spring-boot-starter -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>
        <!-- log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

*总配置文件配置数据库连接Druid数据源整合Mybatis

server:
  port: 8088
spring:
  thymeleaf:
    cache: false
  datasource:
    username: root
    password: 2595
    url: jdbc:mysql://localhost:3308/jdoa?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
#整合mybatis
mybatis:
  type-aliases-package: com.hdat.springboot06shiro.pojo #别名
  mapper-locations: classpath:mybatis/mapper/*.xml #Spring识别

这里面需要改的地方就是别名

*新建pojo目录并新建实体类User

public class User {
    private String userName;
    private String passWord;
    //省略
}

*新建mapper目录并新建UserMapper接口

@Mapper
@Repository
public interface UserMapper {
    User login(User user);
}

@Repository用在持久层的接口上,这个注解是将接口的一个实现类交给spring管理。
@Repository该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。

*在resources下新建mybatis目录下新建mapper并新建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.hdat.springboot06shiro.mapper.UserMapper">
    <select id="login" resultType="User"><!--可以用缓存-->
        select username,password from user where username=#{userName} and password = #{passWord};
    </select>
</mapper>

这里的命名空间要改

*新建service目录下新建UserService接口

public interface UserService {
    User login(User user);
}

*新建UserServiceImpl实现类

@Service
public class UserServiceImpl implements UserService{
    @Autowired
    private UserMapper userMapper;

    @Override
    public User login(User user) {
        return userMapper.login(user);
    }
}

@Service就把这个实现类放到spring容器中了,在使用的地方自动注入这个实现类就可以了
@Autowired自动注入的是mapper下的UserMapper接口的实现类,那个接口已经使用@Repository将接口的一个实现类交给spring管理了,在这里可以通过自动注入得到。

*测试一下连接

	@Autowired
    private UserServiceImpl userService;

    @Test
    void contextLoads() {
        User user = new User();
        user.setUserName("hdat");//先整个假的
        user.setPassWord("123");
        User hdat = userService.login(user);
        System.out.println(hdat.toString());
    }

测试成功,可以查询到结果,整合mybatis连接数据库完毕!

整体逻辑:前面注入UserMapperImpl实现类,调用里面的login方法,UserMapperImpl实现类中注入了UserMapper接口的实现类,在他的login方法中调用的是UserMapper接口实现类的login方法。
控制层->业务层->持久层
controller->service->dao
MVC
M:model业务模型 service->dao
V: view视图
C:controller控制器

Shiro中使用数据库中的数据进行验证

UserRealm中

public class UserRealm extends AuthorizingRealm {


    @Autowired
    private UserServiceImpl userService;
    
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证");
        //获取从controller传过来的包含用户名和密码的token
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        User user = new User();
        user.setUserName(usernamePasswordToken.getUsername());
        user.setPassWord(String.valueOf(usernamePasswordToken.getPassword()));
        User login = userService.login(user);

        if (login == null) {
            return null; //抛出异常UnknownAccountException
        }
        //这里可以进行加密
        //密码的验证由Shiro来做
        return new SimpleAuthenticationInfo("",login.getPassWord(),"");
    }
}

7.Shiro请求授权

给页面设置访问权限
ShiroConfig

@Configuration
public class ShiroConfig {
    //shiroFilterFactoryBean 3
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterBean.setSecurityManager(defaultWebSecurityManager);
        //设置登录请求   无访问权限就跳到登录页面
        shiroFilterBean.setLoginUrl("/toLogin");
        //添加Shiro的内置过滤器
        /*
             anon:无需认证就可以访问
             authc:必须认证才可以访问
             user:必须拥有记住我功能才能访问
             perms:拥有对某个资源的权限才能访问
             role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap<>();
        //登录拦截
        filterMap.put("/add","authc");
        filterMap.put("/update","authc");
        //给页面设置访问权限  访问者无权限就跳转到未授权页面
        filterMap.put("/add","perms[user:add]");
        filterMap.put("/update","perms[user:update]");
        shiroFilterBean.setFilterChainDefinitionMap(filterMap);

        //访问者无权限就跳转到未授权页面
        shiroFilterBean.setUnauthorizedUrl("/noauth");
        return shiroFilterBean;
    }
    //...
}

添加无权限跳转的控制器
controller

@RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){
        return "无权限";//这里跳转到无权限页面或者弹窗都行
    }

进行授权
UserRealm.java

public class UserRealm extends AuthorizingRealm {


    @Autowired
    private UserServiceImpl userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //拿到当前登录的这个对象
        Subject subject = SecurityUtils.getSubject();
        //return new SimpleAuthenticationInfo(login,login.getPassWord(),"");
        //从这个地方取到登录者的信息login
        //拿到User对象,当前登录者信息  currentUser = login
        User currentUser = (User)subject.getPrincipal();
        //拿到登陆者在数据库中的权限并把这个权限授给当前用户
        simpleAuthorizationInfo.addStringPermission(currentUser.getPerms());
        return simpleAuthorizationInfo;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证");
        //从数据库中获取用户名和密码

        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        User user = new User();
        user.setUserName(usernamePasswordToken.getUsername());
        user.setPassWord(String.valueOf(usernamePasswordToken.getPassword()));
        User login = userService.login(user);

        if (login == null) {
            return null; //抛出异常UnknownAccountException
        }
        //这里可以进行加密
        //密码的验证由Shiro来做
        return new SimpleAuthenticationInfo(login,login.getPassWord(),"");
    }
}

这里认证return里面要改一下,把从数据库中查到的对象login加上

在授权方法中通过当前用户对象subject取到这个login对象,然后获取到里面用户的权限,把那个权限授权给当前用户。

现在就完成了授权,用户就拥有自己在数据库中的权限了,可以访问对应权限的页面。

8.不同权限用户看到对应权限内容
添加依赖

<!-- thymeleaf-extras-shiro -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.1.0</version>
</dependency>

配置 整合Shiro thymeleaf
ShiroConfig

//ShiroDialect 整合Shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

页面配置权限

xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
<div shiro:hasPermission="user:add">
    <a th:href="@{/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
    <a th:href="@{/update}">update</a>
</div>

未登录的时候展示登录

<div th:if="${session.loginUser} == null">
    <a th:href="@{/toLogin}">登录</a>
</div>

登录的时候保存登陆者信息到session
UserRealm

if (login == null) {
    return null; //抛出异常UnknownAccountException
}
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser",login);//把登陆者放到session里面

十五、前后端分离

1.前后端任务

  • 前端:前端控制层、视图层
    • 伪造前端数据,json格式,不使用后端依然可以运行
  • 后端:后端控制层、服务层、数据访问层
  • 前后端通过API进行交互
  • 前后端相对独立且松耦合

2.存在的问题

前后端是由不同的人写的,接口变动沟通不及时容易出问题

3.问题的解决

定义schema [ 计划的提纲 ],并实时跟踪最新的API,降低集成风险

4.问题的解决的发展

一开始用的方法:指定Word计划文档
后来:前端使用postman测试后端接口,后端提供接口

十六、Swagger

1.简介

  • 号称世界上最流行的API框架

  • Restful风格 Api 文档在线自动生成器 => API 文档 与API 定义同步更新

  • 直接运行,在线测试API

  • 支持多种语言 (如:Java,PHP等)

  • 官网:https://swagger.io/

2.SpringBoot 集成 Swagger2

http://t.csdn.cn/AKudh

3.配置Swagger信息

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    //配置Swagger的Docket的Bean实例
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo());
    }
    //配置Swagger的信息 apiInfo
    private ApiInfo apiInfo(){
        //作者信息    http://hdat.cn 测试时候不能访问,如果后期能够访问了,页面内容与本人无关 这个链接就是测试瞎写的
        Contact contact = new Contact("hdat", "http://hdat.cn", "huaidanatu@163.com");
        return new ApiInfo(
                "hdat Api Documentation",
                "my Api Documentation",
                "1.0",
                "http://hdat.cn",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList());
    }
}

4.Swagger配置要扫描的接口

Docket.select()

@Bean
public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            //RequestHandlerSelectors 配置要扫描接口的方式
            //basePackage 指定要扫描的包
            .select().apis(RequestHandlerSelectors.basePackage("com.hdat.springboot07swagger.controller")).build();
}

在这里插入图片描述

//RequestHandlerSelectors 配置要扫描接口的方式
//basePackage("包") 指定要扫描的包
//any() 扫描全部
//none() 不扫描
//withClassAnnotation 扫描类上的注解  参数是一个注解的反射对象 .withClassAnnotation(RestController.class)
//withMethodAnnotation 扫描方法上的注解

过滤路径

.select().apis(RequestHandlerSelectors.basePackage("com.hdat.springboot07swagger.controller"))
//过滤路径
.paths(PathSelectors.ant("/hello"))
.build();

5.配置是否启动Swagger

//配置Swagger的Docket的Bean实例
@Bean
public Docket docket(){
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            //配置是否启动swagger
            .enable(false)

在这里插入图片描述

6.配置在生产环境中使用swagger,发布环境不使用

我的思路是
将配置文件与实体类绑定,去获取配置文件中的信息

添加依赖

<!-- spring-boot-configuration-processor -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

创建实体类Swagger

@Component
@ConfigurationProperties(prefix = "swagger")
public class Swagger {
    private Boolean enable;
    //...
}
  • @ConfigurationProperties是springboot提供读取配置文件的一个注解
  • @component是spring中的一个注解,它的作用是实现bean的注入

在不同的总配置文件中配置

swagger:
  enable: false

在开发环境配置文件配置true,发布环境配置文件中配置false

SwaggerConfig中注入Swagger对象,获取配置文件中的布尔值

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Autowired
    private Swagger swagger;
    //配置Swagger的Docket的Bean实例
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                //配置是否启动swagger
                .enable(swagger.getEnable())
    }

结束

狂神的思路是
设置哪些环境可以使用Swagger,然后监听当前环境的profile是否符合设置,
返回一个布尔值

@Bean
public Docket docket(Environment environment){
    //设置在什么环境下使用Swagger
    Profiles profiles = Profiles.of("dev", "test");
    //获取当前项目的环境environment,监听当前环境的Profiles是否符合设置,返回一个布尔值
    boolean enableSwagger = environment.acceptsProfiles(profiles);
    return new Docket(DocumentationType.SWAGGER_2)
            .apiInfo(apiInfo())
            //配置是否启动swagger
            .enable(enableSwagger)
}

7.配置API文档的分组

多个Docket实例即可
在这里插入图片描述
一个Bean配置一个分组
.groupName(“分组”)

在这里插入图片描述

8.Model中显示实体类信息

新建一个User实体类

@ApiModel("User(用户实体类)")
public class User {
    @ApiModelProperty("用户名")
    private String userName;
    @ApiModelProperty("密码")
    private String userPwd;
    //..
}

然后在controller中写接口,返回类型就是这个User

//只要接口的返回值是实体类,就会被Swagger扫描
@PostMapping("/user")
public User user(){
    return new User();
}

在这里插入图片描述

  • @ApiModel(“User(用户实体类)”)注解是给这个实体类加描述的
  • @ApiModelProperty(“用户名”)注解是给类中的参数加描述的

可以不使用两个注解

9.controller的描述信息添加

@ApiOperation("admin接口")
@PostMapping("/admin")
public String admin(@ApiParam("管理员用户名") String adminName){
    return "adminName"+adminName;
}

在这里插入图片描述

10.swagger测试功能

在这里插入图片描述

十七、异步任务

1.写一个业务类,里面这个方法让他延时3秒

@Service
public class AsyncService {
    public void hello(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("数据处理完成...");
    }
}

2.写一个控制类,让他去调用这个延时3秒的方法

@RestController
public class AsyncController {
    @Autowired
    AsyncService service;

    @RequestMapping("/hello")
    public String hello(){
        service.hello();
        return "ok";
    }
}

现在访问/hello ,那么会等待3秒之后,才会return ok 并输出数据处理完成

3.使用异步

在方法上使用@Async注解告诉spring这是一个异步的方法,然后在主启动类上加上@EnableAsync使能异步功能就可以了

@Service
public class AsyncService {
    //告诉spring这是一个异步的方法
    @Async
    public void hello(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("数据处理完成...");
    }
}
//使能异步功能
@EnableAsync
@SpringBootApplication
public class SpringBoot09TaskApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot09TaskApplication.class, args);
    }

}

现在的效果就是访问/hello 然后页面直接返回 ok 等待3秒之后,才会输出数据处理完成

十八、邮件任务

1.添加依赖

<!--spring-boot-starter-mail-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2.在邮箱中开启POP3/STMP服务

得到一个授权码

3.总配置文件中配置邮箱的用户名密码

spring:
  mail:
    username: 3273103853@qq.com
    password:
    host: smtp.qq.com
    #QQ 的需要开启一个加密验证
    properties:
      mail:
        smtp:
          ssl:
            enable: true

这个是发送邮件的邮箱的账密

4.测试发送一个简单邮件

@Autowired
JavaMailSenderImpl mailSender;

@Test
void contextLoads() {
    SimpleMailMessage mailMessage = new SimpleMailMessage();
    mailMessage.setSubject("你好啊阿土");
    mailMessage.setText("hdat");
    mailMessage.setFrom("3273103853@qq.com");
    mailMessage.setTo("3273103853@qq.com");
    mailSender.send(mailMessage);
}

5.测试发送一个复杂邮件

@Test
void contextLoads1() throws MessagingException {
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    //组装   true是开启多文件发送
    MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true,"utf-8");
    mimeMessageHelper.setSubject("你好啊阿土");
    mimeMessageHelper.setText("<p style='red'>hdat</>",true);
    //附件
    mimeMessageHelper.addAttachment("1.jpg", new File("C:\\Users\\Administrator\\Desktop\\壁纸\\1.jpg"));

    mimeMessageHelper.setFrom("3273103853@qq.com");
    mimeMessageHelper.setTo("3273103853@qq.com");
    mailSender.send(mimeMessage);
}

十九、定时任务

1.主启动类上使能使能调度功能

//使能调度功能
@EnableScheduling
@SpringBootApplication
public class SpringBoot09TaskApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBoot09TaskApplication.class, args);
    }

}

2.方法上使用@Scheduled配置cron表达式设置定时

@Service
public class ScheduledService {
    //cron 表达式
    // 秒 分 时 日 月 周几
    @Scheduled(cron = "30 13 22 * * ?")
    public void hello(){
        System.out.println("hello 被执行了");
    }
}

二十、SpringBoot整合Redis

1.导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

或者是创建项目的时候勾选

2.自定义配置

spring:
  redis:
    host: localhost
    port: 6379

3.测试连接

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("myname","hdat");
        System.out.println(redisTemplate.opsForValue().get("myname"));

    }

首先要打开redis,再运行
在这里插入图片描述在这里插入图片描述
连接正常

现在用的是人家默认的RedisTemplate

4.自定义RedisTemplate

redisconfig

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }
}

这个可以在源码RedisAutoConfiguration中找到

5.json和String的序列化配置

@Configuration
@SuppressWarnings("all")
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        //固定模板
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        //json 序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //String的序列化配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        //key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value序列化方式此阿勇Jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

6.测试自己写的redisTemplate

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;
    
    @Test
    void contextLoads1() throws JsonProcessingException {
        //真实的开发一般都是使用json来传递对象
        User hdat = new User("hdat", 21);
        String josnHdat = new ObjectMapper().writeValueAsString(hdat);
        redisTemplate.opsForValue().set("myname",hdat);
        System.out.println(redisTemplate.opsForValue().get("myname"));
    }

7.redisUtil工具类

http://t.csdn.cn/4SoMk

7.redisUtil工具类测试

    @Autowired
    private RedisUtil redisUtil;

    @Test
    void contextLoads2() throws JsonProcessingException {
        //真实的开发一般都是使用json来传递对象
        User hdat = new User("hdat", 21);
        String josnHdat = new ObjectMapper().writeValueAsString(hdat);
        redisUtil.set("myname",hdat);
        System.out.println(redisUtil.get("myname"));
    }    

二一、分布式

1.什么是分布式系统(distributed system)

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统.
分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。
分布式系统是建立在网络之上的软件系统。

只有当单个节点的处理能力无法满足日益增长的计算、存储任务的时候,且硬件的提升(加内存、加磁盘、使用更好的CPU)高昂到得不偿失的时候,应用程序也不能进一步优化的时候,我们才需要考虑分布式系统。因为,分布式系统要解决的问题本身就是和单机系统一样的,而由于分布式系统多节点、通过网络通信的拓扑结构,会引入很多单机系统没有的问题,为了解决这些问题又会引入更多的机制、协议,带来更多的问题。

2.网站架构发展

1.单一应用架构(1-10人)
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
单一应用架构适用于小型网站,小型管理系统,将所有功能都部署到一个功能里,简单易用。
在这里插入图片描述缺点:

1、性能扩展比较难

2、协同开发问题

3、不利于升级维护

2.垂直应用架构(10-1000人)
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职更易管理,性能扩展也更方便,更有针对性。
在这里插入图片描述缺点:公用模块无法重复利用,开发性的浪费

3.分布式服务架构(1000-10000人)
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
在这里插入图片描述
4.流动计算架构(10000+人)
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)[ Service Oriented Architecture]是关键。
在这里插入图片描述

3.RPC

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间通信方式,他是一种技术的思想,而不是规范。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。为什么要用RPC呢?就是无法在一个进程内,甚至一个计算机内通过本地调用的方式完成的需求,比如不同的系统间的通讯,甚至不同的组织间的通讯,由于计算能力需要横向扩展,需要在多台机器组成的集群上部署应用。RPC就是要像调用本地的函数一样去调远程函数
在这里插入图片描述

RPC两个核心模块:通讯,序列化.
在这里插入图片描述
在这里插入图片描述
如何给老婆解释什么是RPC

3.Dubbo

Apache Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
https://dubbo.apache.org/zh/

dubbo基本概念
在这里插入图片描述

  • 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。

  • 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

  • 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者

  • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

调用关系

服务容器负责启动,加载,运行服务提供者。

服务提供者在启动时,向注册中心注册自己提供的服务。

服务消费者在启动时,向注册中心订阅自己所需的服务。

注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Dubbo环境搭建

dubbo官方文档中,推荐我们使用Zookeeper 注册中心

暂时跳过

posted on 2022-10-16 12:13  霍志杰  阅读(132)  评论(0)    收藏  举报  来源

导航