SpringBoot

SpringBoot

简介

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

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整合了所有的框架 。

微服务

微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须建成一系列小服务组合,可以通过http方式进行通信。

单体应用架构

单体应用架构(all in one)是指,我们将一个应用中的所有服务都封装在一个应用中。无论是ERP、CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。

微服务架构

所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来,把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些可以整合多个功能元素,所以微服务架构是对功能元素进行赋值,而没有对整个应用进行复制,这样做的好处是:

  • 节省了调用资源
  • 每个功能元素的服务都是一个可替换的,可独立升级的软件代码
  • 程序核心:高内聚(在划分模块时,要把功能关系紧密的放到一个模块中)
  • 低耦合(模块之间的联系越少越好,接口越简单越好

第一个入门程序

创建一个maven工程

导入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>


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

    </dependencies>

问题

如果遇到maven加载缓慢问题,可以尝试在命令行输入mvn help:system,重启idea

如果没解决,请参阅https://blog.csdn.net/l13501058595/article/details/108718025

建立包结构

在这里插入图片描述

注意:controller一定要和applicationBoot在一个包下

applicationBoot.java

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

applicationController.java

@RestController
public class applicationController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello SpringBoot2";
    }
}

SpringBoot特点

依赖管理

依赖管理    
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
</parent>

父项目的父项目
 <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
  </parent>

几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制

修改版本号

注意:一个pom文件只能有一个properties标签,如果爆红,就是已经存在

1.首先查看spring-boot-dependencies里面规定当前依赖的版本用的key
2.在当前项目里重写配置
<properties>
        <mysql.version>5.1.43</mysql.version>
    </properties>

开发导入starter场景启动器

只要引入starter,这个场景的所有常规需要的依赖我们都自动引入

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

无需写版本依赖

  1. 引入依赖默认都可以不写版本
  2. 引入非版本仲裁的jar,要写版本号

自动配置

自动配好tomcat,springMVC,web常用的配置,默认的包扫描(和主程序必须放在同一个包下),各种配置都有默认值(修改的话统一在application.properties里面修改),

解决未和主程序放在同一个包下的问题
@SpringBootApplication(scanBasePackages ="文件路径范围包括主程序和包" )

原理初探

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中
  • 我们再写或者引入一些spring boot依赖的时候,不需要指定版本,就因为有这些版本仓库

启动器

spring boot的启动场景。我们使用什么功能,就只需要找到对应的启动器

主程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传在这里插入图片描述

@SpringBootApplication
	作用:标注在哪个类上,就说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用
	@SpringBootConfiguration
		作用:SpringBoot的配置类,标注在哪个类上,表示的哪个就是SpringBoot的配置类
		@Configuration:这里的@Configuration,说明这相当于是一个Spring的xml配置文件
			@Component:这里的@Component,说明启动类本身也是一个组件,负责启动应用
@EnableAutoConfiguration
	开启自动配置功能(以前我们需要手动配置,现在SpringBoot可以帮我们自动配置)
	@EnableAutoConfiguration注解“通知”SpringBoot开启自动配置功能,这样自动配置功能才能生效
    @Import:Spring底层注解,给容器中导入一个组件
    @Import({AutoConfigurationImportSelector.class}):给容器导入组件
    AutoConfigurationImportSelector:自动配置导入选择器

结论:springboot所有自动配置都是在启动的时候扫描并加载spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功。

SpringApplication
  1. 推断应用的类型是普通项目还是web项目
  2. 查找并加载所有可用初始化器,设置到initializers属性中
  3. 找到所有的应用程序监听器,设置到listeners属性中
  4. 推断并设置所有main方法的定义类,找到运行的主类

javaConfig @Configure @Bean

yaml

配置文件

springboot使用一个全局的配置文件,配置文件的名称是固定的。

  • application.properties
    • 语法:key=value
  • application.yaml
    • 语法:key:空格 value
作用

修改springboot自动配置的默认值,因为springboot在底层给我们自动配置好了;

基本语法

注意:对空格的要求极其高

#存储对象
#方式一
student:
  name: XZY_SUNSHINE
  age: 17
#方式二
student1: {name: lalala,age: 7}

#数组
#方式一
array:
  - 1
  - 2
  - 3
  - 4
#方式二
array1: [cat,pig,dog]

练习

application.yaml

person:
  id: 1
  name: SUNSHINE
  dog:
    name: 旺财
    age: 3

pojo.Person

package com.XZY_SUNSHINE.pojo;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private int id;
    private String name;
    private Dog dog;

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", dog=" + dog.toString() +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Person() {
    }

    public Person(int id, String name, Dog dog) {
        this.id = id;
        this.name = name;
        this.dog = dog;
    }
}

pojo.Dog

package com.XZY_SUNSHINE.pojo;

import org.springframework.stereotype.Component;

@Component
public class Dog {
    private String name;
    private int age;

    public Dog() {
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

@Configurationproperties(prefix = “person”)
  • 将配置文件中配置的每一个属性的值,映射到这个组件中。
  • 告诉springboot将本类中的属性和配置文件中相关的配置进行绑定

注意:只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能

Properties

练习

properties

name=sunshine

person

@PropertySource(value = "classpath:person.properties")
public class Person {
    private int id;
    @Value("${name}")
    private String name;
    private Dog dog;

yaml和Properties

区别

在这里插入图片描述

松散绑定

例子:yaml中的属性名为last-name,和lastName的效果是一样的

JSR303校验

我们可以在字段增加一层过滤器验证,可以保证数据的合法性

属性
//校验类型(message="错误提示")
//1、@Null                       校验对象是否为null
//2、@NotNull                    校验对象是否不为null
//3、@NotBlank                   校验字符串去头尾空格后的长度是否大于0或是否为null
//4、@NotEmpty                   校验字符串是否为null或是否为empty
//
//5、@AssertTrue                 校验Boolean是否为true
//6、@AssertFalse                校验Boolean是否为false
//
//7、@UniqueElements             校验数组/集合的元素是否唯一
//8、@Size(min,max)              校验数组/集合/字符串长度是否在范围之内
//9、@Length(min,max)            校验数组/集合/字符串长度是否在范围之内
//10、@Range(min,max)            校验Integer/Short/Long是否在范围之内
//11、@Min(number)               校验Integer/Short/Long是否大于等于value
//12、@Max(number)               校验Integer/Short/Long是否小于等于value
//13、@Positive                  校验Integer/Short/Long是否为正整数
//14、@PositiveOrZero            校验Integer/Short/Long是否为正整数或0
//15、@Negative                  校验Integer/Short/Long是否为负整数
//16、@NogativeOrZero            校验Integer/Short/Long是否为负整数或0
//
//17、@DecimalMin(decimal)       校验Float/Double是否大于等于value
//18、@DecimalMax(decimal)       校验Float/Double是否小于等于value
//19、@Digits(integer,fraction)  校验数字是否符合整数位数精度和小数位数精度
//
//20、@Past(date)                校验Date/Calendar是否在当前时间之前
//21、@PastOrPresent(date)       校验Date/Calendar是否在当前时间之前或当前时间
//22、@Future(date)              校验Date/Calendar是否在当前时间之后
//23、@FutureOrPresent(date)     校验Date/Calendar是否在当前时间之后或当前时间
//
//24、@Email                     校验字符串是否符合电子邮箱格式
//25、@URL(protocol,host,port)   校验字符串是否符合URL地址格式
//26、@CreditCardNumber          校验字符串是否符合信用卡号格式
//
//27、@Pattern(regexp)           校验字符串是否符合正则表达式的规则
使用

依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
@Validated
public class Person {
    private int id;
    @Email(message = "hello")
    private String name;

多环境配置

通过debug=true,查看配置哪些生效,哪些不生效

配置文件优先级

在这里插入图片描述

练习

server:
  port: 8080
spring:
  profiles:
    active: dev
---
server:
  port: 8081
spring:
  profiles: dev
---
server:
  port: 8082
spring:
  profiles: test

自动装配原理

  • springboot会启动大量的自动配置类
  • 我们看我们需要的功能有没有在springboot默认写好的自动配置类中
  • 我们再来看这个自动配置类到底配置了哪些组件
  • 给容器中自动配置类添加组件时,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可。

@Conditional

  • @ConditionalOnJava:只有运行指定版本的 Java 才会加载 Bean
  • @ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication:只有运行在(不在)web 应用里才会加载这个 bean
  • @ConditionalOnCloudPlatform:只有运行在指定的云平台上才加载指定的 bean
  • @ConditionalOnJndi:只有指定的资源通过 JNDI 加载后才加载 bean
  • @ConditionalOnExpression(“${test.express}==true”) :可通过spring提供的spEL表达式灵活配置,当表达式为true的时候,才会实例化一个Bean
  • @ConditionalOnSingleCandidate(UserService.class) :表示ioc容器中只有一个UserService类型的Bean,才生效
  • @ConditionalOnResource:指定的静态资源⽂件存在 才加载

web开发

静态资源

在springboot中我们可以使用以下方式处理静态资源

  • webjars
  • public,static,/**,resources

优先级:resources>static>public

模板引擎

导入依赖

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

使用

在这里插入图片描述

在这里插入图片描述

扩展MVC

如果你想diy一些定制化功能,只要写个组件,然后将它交给springboot,springboot就会帮我们自动装配

在这里插入图片描述

结论

springboot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果用户自己配置@bean),如果有就用用户配置的,没有则用自动配置的。如果组件可以存在多个,比如视图解析器,就将用户配置的和自己默认的组合起来。

员工管理系统

pojo

//employee
package com.XZY_SUNSHINE.pojo;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;
@Data
@NoArgsConstructor
public class Employee {
    private Integer id;
    private String name;
    private String email;
    private Department department;
    private Date date;

    public Employee(Integer id, String name, String email, Department department) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.department = department;
        this.date = new Date();
    }
}
//Department
package com.XZY_SUNSHINE.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String name;

}

模拟数据库

//employeeDao
package com.XZY_SUNSHINE.dao;

import com.XZY_SUNSHINE.pojo.Department;
import com.XZY_SUNSHINE.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;

@Repository
public class employeeDao {
    private static HashMap<Integer, Employee> employees=new HashMap<Integer,Employee>();
    static {
        employees.put(1001,new Employee(1001,"AA","A213123@qq.com",new Department(101,"教学部")));
        employees.put(1002,new Employee(1002,"BB","B213123@qq.com",new Department(102,"教研部")));
        employees.put(1003,new Employee(1003,"CC","C213123@qq.com",new Department(103,"市场部")));
        employees.put(1004,new Employee(1004,"DD","D213123@qq.com",new Department(104,"运营部")));
        employees.put(1005,new Employee(1005,"EE","E213123@qq.com",new Department(105,"后勤部")));
    }
    @Autowired
    private departmentDao departmentDao;
    private static int initid=1006;
    public void save(Employee employee){
        if (employee.getId()==null){
            employee.setId(initid++);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(),employee);
    }
    public Collection<Employee> getEmployees(){
        return employees.values();
    }
    public Employee getEmployeeById(int id){
        return employees.get(id);
    }

}
//DepartmentDao
package com.XZY_SUNSHINE.dao;

import com.XZY_SUNSHINE.pojo.Department;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.HashMap;

@Repository
public class departmentDao {
    private static HashMap<Integer, Department> departments=new HashMap<Integer,Department>();
    static {
        departments.put(101,new Department(101,"教学部"));
        departments.put(102,new Department(102,"教研部"));
        departments.put(103,new Department(103,"市场部"));
        departments.put(104,new Department(104,"运营部"));
        departments.put(105,new Department(105,"后勤部"));
    }
    private static int initid=106;
    public void save(Department department){
        if (department.getId()==null){
            department.setId(initid++);
        }
        departments.put(department.getId(),department);
    }
    public Collection<Department> getDepartment(){
        return departments.values();
    }
    public Department getDepartmentById(int id){
        return departments.get(id);
    }
}

首页实现

@Configuration
public class MyMvcConfig implements WebMvcConfigurer{
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/").setViewName("index");
    }
}
spring:
  thymeleaf:
    cache: false

国际化实现

//login.properties
login.btn=登录
login.password=密码
login.remember=请记住我
login.tip=请登录
login.title=登录
login.username=用户名



//login_zh_CN.properties
login.btn=登录
login.password=密码
login.remember=请记住我
login.tip=请登录
login.title=登录
login.username=用户名


//login_en_US.properties
login.btn=Sign in
login.password=Password
login.remember=Remember me
login.tip=Please sign in
login.title=Sign in
login.username=Username
//自定义LocaleResovler
public class MyLocaleResolver implements LocaleResolver {
    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        String language= httpServletRequest.getParameter("lang");
        System.out.println(language);
        Locale locale=Locale.getDefault();
        if (!StringUtils.isEmpty(language)){
            String[] strings = language.split("_");
            locale=new Locale(strings[0],strings[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }
}

//注入spring容器
@Bean
public LocaleResolver localeResolver(){
    return new MyLocaleResolver();
}

在这里插入图片描述

messages:
    basename: i18n.login

员工功能实现

@Controller
@RequestMapping("/employee")
public class employController {
    @Autowired
    private employeeDao employeeDao;
    @Autowired
    private departmentDao departmentDao;
    @RequestMapping("/employees")
    public String get_employees(Model model){
        Collection<Employee> employees = employeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "/emp/list";
    }
    @GetMapping("/add")
    public String add(Model model){
        Collection<Department> departments = departmentDao.getDepartment();
        model.addAttribute("departments",departments);
        return "redirect:/emp/add";
    }
    @PostMapping("/add")
    public String add(Employee employee,Model model){
        employeeDao.save(employee);
        Collection<Employee> employees = employeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "/emp/list";
    }
    @RequestMapping("/delete/{id}")
    public String delete(@PathVariable("id") int id,Model model){
        employeeDao.deleteEmployeee(id);
        Collection<Employee> employees = employeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "/emp/list";
    }
    @GetMapping("/update/{id}")
    public String update(@PathVariable("id") int id,Model model){
        Employee employee = employeeDao.getEmployeeById(id);
        model.addAttribute("employee",employee);
        Collection<Department> departments = departmentDao.getDepartment();
        model.addAttribute("departments",departments);
        return "/emp/update";
    }
    @PostMapping("/update")
    public String update(Employee employee,Model model){
        employeeDao.updateEmployee(employee);
        Collection<Employee> employees = employeeDao.getEmployees();
        model.addAttribute("employees",employees);
        return "/emp/list";
    }
}
//commons.html

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<div class="container-fluid" th:fragment="sidebar">
<a th:class="${active=='main'?'nav-link active':'nav-link'}"
   
   
//dashboard.html
   
<a class="nav-link" th:href="@{/employee/employees}">
<div th:replace="~{common/commons::sidebar(active='main')}"></div>
    
    
//list.html
    
<table class="table table-striped table-sm">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
            <th>Department</th>
            <th>Date</th>
        </tr>
    </thead>
    <tbody>
        <tr th:each="emp:${employees}" >
            <td th:text="${emp.getId()}"></td>
            <td th:text="${emp.getName()}"></td>
            <td th:text="${emp.getEmail()}"></td>
            <td th:text="${emp.getDepartment().getName()}"></td>
            <td th:text="${#dates.format(emp.date,'yyyy-MM-dd')}"></td>
            <td>
                <a class="btn btn-lm btn-primary" th:href="@{/employee/update/}+${emp.getId()}">编辑</a>
                <a class="btn btn-lm btn-danger" th:href="@{/employee/delete/}+${emp.getId()}">删除</a>
            </td>
        </tr>

    </tbody>
    </table>
    
    
//update.html
    
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
    <form th:action="@{/employee/update}" method="post" >
        <input type="hidden" name="id" th:value="${employee.getId()}" >
        <div class="form-group" ><label>姓名</label>
            <input  class="form-control"  type="text" name="name" th:value="${employee.getName()}">
        </div>
        <div class="form-group" ><label>邮箱</label>
            <input  class="form-control"  type="email" name="email" th:value="${employee.getEmail()}">
        </div>
        <div class="form-group" ><label>部门</label>
            <select class="form-control" name="department.id">
                <option  th:selected="${dept.getId()==employee.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
            </select>
        </div>
        <div class="form-group" >
            <label >生日</label>
            <input  class="form-control"  type="text" name="date" th:value="${#dates.format(employee.getDate(),'yyyy-MM-dd')}">
        </div>
        <button class="btn btn-primary" type="submit">添加</button>
    </form>
    </main>

    
 //add.html
 
    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
        <form th:action="@{/employee/add}" method="post" >
            <input type="hidden" name="id" >
            <div class="form-group" ><label>姓名</label>
                <input  class="form-control"  type="text" name="name">
            </div>
            <div class="form-group" ><label>邮箱</label>
                <input  class="form-control"  type="email" name="email">
            </div>
            <div class="form-group" ><label>部门</label>
                <select class="form-control" name="department.id">
                    <option  th:each="dept:${departments}" th:text="${dept.getName()}" th:value="${dept.getId()}"></option>
                </select>
            </div>
            <div class="form-group" >
                <label >生日</label>
                <input  class="form-control" placeholder="2021-02-02" type="text" name="date">
            </div>
            <button class="btn btn-primary" type="submit">添加</button>
        </form>
    </main>

错误实现

如果想要定制错误页面,就把状态码为name,把html文件放在templates的error目录下

Spring Data

对于数据访问层,无论是SQL(关系型数据库),还是NOSQL(非关系型数据库),SpringData都可以进行统一管理。

整合JDBC

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
 <dependency>
     <groupId>com.mysql</groupId>
     <artifactId>mysql-connector-j</artifactId>
     <version>8.0.32</version>
</dependency>
@Autowired
private JdbcTemplate jdbcTemplate;
@RequestMapping("/addUser")
public String addUser(){
    String sql="insert into mybatis.user (id, name, pwd) VALUES (5,'zzz','12345')";
    jdbcTemplate.execute(sql);
    return "addIsOk";
}

整合druid

<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>druid</artifactId>
     <version>1.2.16</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
spring:
  datasource:
    initial-size: 10
        min-idle: 10
        maxActive: 200
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        connectionErrorRetryAttempts: 3
        breakAfterAcquireFailure: true
        timeBetweenConnectErrorMillis: 300000
        asyncInit: true
        remove-abandoned: true
        remove-abandoned-timeout: 1800
        transaction-query-timeout: 6000
        filters: stat,wall,log4j2
        connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
        web-stat-filter:
          enable: true
        stat-view-servlet:
          enable: true
@Configuration
public class myDruidConfig {
    
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource dataSource(){
        return new DruidDataSource();
    }
    //后台管理
    @Bean
    public ServletRegistrationBean statViewServlet(){
//        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet());
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*" );
        HashMap<String, String> map = new HashMap<>();
        map.put("loginUsername","admin");
        map.put("loginPassword","123456");
        map.put("allow","");
        bean.setInitParameters(map);
        return bean;
    }
    //过滤不统计的请求
    @Bean
    public FilterRegistrationBean a(){
        FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>(new WebStatFilter());
        HashMap<String, String> map = new HashMap<>();
        map.put("exclusions","*.js");
        bean.setInitParameters(map);
        return bean;
    }
}

整合mybatis

最后一定要在主程序类上加

@MapperScan("com.XZY_SUNSHINE.Mapper")

配置

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>
mybatis:
  type-aliases-package: com.XZY_SUNSHINE.pojo
  mapper-locations: classpath:mybatis/*.xml
//pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class user {
    private int id;
    private String name;
    private String pwd;
}
//userMapper接口
@Repository
@Mapper
public interface userMapper {
    List<user> userList();
    void addUser();
    void updateUser(@Param("id") int id);
    void deleteUser(@Param("id") int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 配置文件的根元素 -->
<mapper namespace="com.XZY_SUNSHINE.Mapper.userMapper">
    <select id="userList" resultType="user">
        select * from mybatis.user
    </select>
</mapper>
@Autowired
    private userMapper usermapper;
    @RequestMapping("/users")
    public List<user> userList(){
        List<user> users = usermapper.userList();
        return users;
    }

SpringSecurity

依赖

<!--Thymeleaf,这里是基于3.X开发的-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <!--Thymeleaf和security整合包-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

config文件

@EnableWebSecurity
public class webSecurityConfig 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("/login");
        //注销功能实现
        http.logout().logoutSuccessUrl("/").deleteCookies();
        //记住我功能实现
        http.rememberMe().rememberMeParameter("remember");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("vip").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
                .and()
                .withUser("custom").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
    }
}

html文件

<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    
    
    
 <!--登录-->
    <div sec:authorize="isAuthenticated()">
        <a class="item">
            用户名:<span sec:authentication="name"></span>
        </a>
        <a class="item" th:href="@{/logout}">
            <i class="address card icon"></i> 注销
        </a>
    </div>
    <!--未登录-->
    <div sec:authorize="!isAuthenticated()">

        <a class="item" th:href="@{/login}">
            <i class="address card icon"></i> 登录
        </a>
    </div>
    
    
    
    <div class="column" sec:authorize="hasRole('vip2')">

controller

public class RouterController {
    @RequestMapping(value = {"/","/index"})
    public String index(){
        return "index";
    }
    @GetMapping("/login")
    public String login1(){
        return "views/login";
    }
    @PostMapping("/login")
    public String login2(){
        return "index";
    }
    @RequestMapping("/logout")
    public String logout(){
        return "index";
    }
    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id")int id){
        return "views/level1/"+id;
    }
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id")int id){
        return "views/level2/"+id;
    }
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id")int id){
        return "views/level3/"+id;
    }
}

Shiro

简介

Shrio是一个强大且易用的Java安全框架。可以完成身份认证、授权、密码和会话管理

功能

在这里插入图片描述

  • Authentication:身份认证/登录,验证用户是不是拥有相应的身份。
  • Authorization:授权即权限认证,验证某个已认证的用户是否拥有某个权限;及判断用户能否做事情
  • Session Manager:会话管理,即用户登录后就是一次会话。
  • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
  • Web Support:Web支持,可以集成到web环境中。
  • Caching:缓存。
  • Concurrency:shrio支持多线程应用的并发验证。在一个线程中开启另一个线程,能把权限自动传播过去。
  • Testing:提供测试支持
  • Run as:允许一个用户假装为另一个用户的身份进行访问
  • Remember me:记住我。

快速开始

  1. 通过 SecurityUtils 获取当前执行的用户

    SubjectSubject currentUser = SecurityUtils.getSubject();

  2. 通过 当前用户拿到 Session,shiro的session

    Session session = currentUser.getSession();

  3. 用 Session 存值取值

    session.setAttribute(“someKey”, “aValue”);

    String value = (String) session.getAttribute(“someKey”

  4. 判断用户是否被认证

    currentUser.isAuthenticated()

  5. 执行登录操作

    currentUser.login(token);

  6. 打印其标识主体

    currentUser.getPrincipal()

  7. 注销

    currentUser.logout();

环境搭建

依赖
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.10.0</version>
</dependency>
userRealm
package com.XZY_SUNSHINE.config;


import com.XZY_SUNSHINE.Service.UserServiceImpl;
import com.XZY_SUNSHINE.pojo.user;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;

import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserServiceImpl userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了-->授权方法");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        user user = (user) subject.getPrincipal();
        info.addStringPermission(user.getPerms());
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了-->认证方法");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        user user = userService.queryUserByName(token.getUsername());
        if (user==null){
            return null;
        }
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    }
}

ShiroConfig
package com.XZY_SUNSHINE.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.config.ShiroConfiguration;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShrioConfig{
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        Map<String, String> map= new LinkedHashMap<>();
        map.put("/add","perms[user:add]");
        map.put("/update","perms[user:update]");
        map.put("/user/*","authc");
        bean.setFilterChainDefinitionMap(map);
        bean.setLoginUrl("/login");
        return bean;
    }
    @Bean(name="manager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager manger = new DefaultWebSecurityManager();
        manger.setRealm(userRealm);
        return manger;
    }
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
    @Bean
    public ShiroDialect shiroDialect(){
        return  new ShiroDialect();
    }
}

controller
@PostMapping("/login")
    public String post_login(String username,String password,Model model){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try{
            subject.login(token);
        }catch (UnknownAccountException e){
            model.addAttribute("msg","用户名错误");
            return "/user/login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "user/login";
        }
        return "index";
    }
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
首页
<h1 th:text="${msg} "></h1>
<a th:href="@{/login}" shiro:notAuthenticated>登录</a>
<a th:href="@{/user/add}" shiro:hasPermission="user:add">add</a>
<a th:href="@{/user/update}" shiro:hasPermission="user:update">update</a>
</body>
</html>

Swagger

简介

  • API文档和PAPI定义同步更新
  • 直接运行,可以在线测试API接口;
  • 支持多种语言

使用

  1. 导入jar包:swagger2,ui

    <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>3.0.0</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>3.0.0</version>
            </dependency>
    
  2. 配置swagger

    //application.properties
    spring.mvc.pathmatch.matching-strategy=ant_path_matcher
    
    //swagger2config
    
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        public Docket docket(){
            return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
        }
        public ApiInfo apiInfo(){
            Contact contact = new Contact("XZY_SUNSHINE", "https://baidu.com", "2602235712@qq.com");
            return new ApiInfo(
                "XZY's Swagger",
                "永远相信美好的事情即将发生!",
                "beta",
                "bingo",
                    contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList<>()
            );
    
        }
    
    }
    
  3. 测试运行-》http://localhost:8080/swagger-ui.html

配置扫描接口

Docket.select()
public class SwaggerConfig {
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
                .select()
                //RequestHandlerSelectors;配置扫描接口的方式
                //basepackage:指定要扫描的包
                //any():扫描全部
                //none():都不扫描
                //withClassAnnotation():扫描类上的注解 例子:
                //withMethodAnnotation():扫描方法上的注解 例子:GetMapping.class
                
//                .apis(RequestHandlerSelectors.withClassAnnotation())
                
                
                //paths():过滤路径
//                .paths()
                .build();
    }
配置开关
//根据环境来配置开关


    Profiles profiles = Profiles.of("dev");
    //通过enviroment.acceptsProfiles判断是否处在自己设定的环境中
    boolean flag = environment.acceptsProfiles(profiles); //通过此处判断

return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
                .enable(flag)//是否启用swagger
配置API文档分组
.groupName(name)
注释
@ApiModel("用户实体类")
public class User {
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("密码")
    private String password;
    
    
    
 @ApiOperation("Post测试")
    @PostMapping("/postt")
    public User postt(@ApiParam("用户名:")User user){
        return user;
    }

任务

异步任务

Service
@Service
public class AsyncTest {
    @Async
    public void hello(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("hello");
    }
}
Controller
@RestController
public class TestController {
    @Autowired
    AsyncTest asyncTest;

    @RequestMapping("/hello")
    public String hello(){
        asyncTest.hello();
        return "OK";

    }
主程序类
@EnableAsync
@SpringBootApplication
public class SpringBoot05TestApplication {

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

}

邮件发送

依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
配置文件
spring.mail.username=2602235712@qq.com
spring.mail.password=ctnlwnvbugsudhgf
spring.mail.host=smtp.qq.com
spring.mail.properties.mail.smtp.ssl.enable=true
Test
@SpringBootTest
class SpringBoot05TestApplicationTests {
    @Autowired
    JavaMailSenderImpl mailSender;

    @Test
    void contextLoads() {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("测试邮件");
        message.setText("可忽略");
        message.setFrom("2602235712@qq.com");
        message.setTo("2602235712@qq.com");
        mailSender.send(message);
    }
    @Test
    void contextLoads2() throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message,true);
        helper.setSubject("测试邮件");
        helper.setFrom("2602235712@qq.com");
        helper.addAttachment("1.jpg",new File(FilePathName));
        helper.setTo("**********@qq.com");
        helper.setText("<h1 style='color:red'>可忽略</h1>",true);
        mailSender.send(message);
    }

定时执行任务

在主程序类上开启注解
@EnableScheduling
在方法上开启注解
@Scheduled(cron = "* 31 21 * * * ")//秒 分 时 日 月 周几
    public void luck(){
        System.out.println("被执行了。。。。。");
    }

集成Redis

在SpringBoot2.x之后,原来使用的jedis被替换为lettuce。

  • jedis:采用的直连,多个线程操作的话,是不安全的。想要避免不安全,使用连接池
  • lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况。

导入依赖

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

配置文件

spring.redis.host=localhost
spring.redis.port=6379

测试

@Autowired
    RedisTemplate redisTemplate;
    @Test
    void contextLoads3(){
        //opsForValue()操作字符串,
        //opsForList()操作列表
        redisTemplate.opsForValue().set("msg","哈哈哈");
        System.out.println(redisTemplate.opsForValue().get("msg"));
        //获取连接
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushAll();
//        connection.flushDb();
    }

配置自己的Redis

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //json序列化配置
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        //string序列化配置
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //key采用string的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        //hash的key也采用string 的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        //value的序列化方式采用jackson
        template.setValueSerializer(serializer);
        //hash的value也采用jackson方式
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
}

分布式

简介

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。

分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统,分布式系统是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。其目的是利用更多的机器,处理更多的数据。

架构演变

在这里插入图片描述

单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。用于简化增删改查工作量的数据访问框架。

在这里插入图片描述

缺点:

  • 性能拓展比较难
  • 协同开发问题
  • 不利于升级维护
垂直应用架构

通过切分业务来实现各个模块的独立部署,降低了维护和部署难度,团队各司其职更易管理,性能扩展也很方便,更有针对性。

在这里插入图片描述

分布式应用架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

在这里插入图片描述

流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实现管理集群容量,提高集群利用率,此时,用于提高利用率的资源调度和治理中心(SOA)是关键。

在这里插入图片描述

RPC

简介

RPC是指远程过程调用,是一种进程间通信方式,它是一种技术的思想,而不是规范。它允许程序调用另一个地址空间的过程或函数,而不用程序员显示编码这个远程调用的细节。及程序员无论是调用本地的函数还是远程的函数,本质上编写的代码基本相同。

核心模块

通讯,序列化

Dubbo

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

在这里插入图片描述

  • 服务提供者(Provider):暴露服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的服务。
  • 服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者
  • 监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XZY-SUNSHINE

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值