SpringBoot

SpringBoot

微服务阶段

  • javase: OOP
  • mysql:持久化
  • html+css+js+jquery+框架
  • javaweb:MVC 三层架构 的网站
  • ssm:简化了开发流程 配置也相对复杂
  • Spring:
  • SpringBoot:内嵌Tomact 微服务架构
  • springCloud

在这里插入图片描述

SpringBoot

SpringBoot 是基于 Spring 的开发 SpringBoot 本身不提供Spring 框架的核心特性以及扩展性功能 只是用于快速开发一代基于Spring 框架的应用程序 他不是用来替代Spring的解决方法, 而是和Spring紧密结合用于提升Spring开发体验的

Spring Boot 约定大于配置 默认帮我们进行了很多设置 多数SpringBoot 应用只需要很少的Spring配置

同时集成了大量的常用第三方库配置(Redis,MongoDB,Jpa,RabbitMQ,Quartz …) Spring Boot 中这些第三方库几乎可以零配置

SpringBoot 其实不是什么新的框架 它默认配置了很多框架的使用方法 就像maven整合了所有的jar包 SpringBoot 整合了所有的框架

优点:

  • 为所有Spring开发者更快的入门
  • 开箱即可 提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码生成和XML配置的要求
微服务

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

单体应用架构(all in one)

将一个应用中的所有应用服务都封装在一个应用中

  • 易于开发和测试 也十分方便部署 当需要扩展时 只需要将 war 复制多份 然后放在多个服务器上 再做个负载均衡就可以了
  • 缺点 : 哪怕要修改一个非常小的地方 都需要停掉整个服务 重新打包 部署 war包
微服务架构

all in one 的架构方式 我们把所有的功能单元放在一个应用里面 然后我们把整个应用部署到服务器上 如果负 载能力不行 我们将整个应用进行水平复制 进行扩展 然后在负载均衡

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

好处:

  • 节省了调用资源
  • 每个功能元素的服务都是一个可替代的 可独立升级的软件代码

怎么样构建微服务

  • 构建一个个功能独立的微服务应用单元 可以使用 SpringBoot 可以帮我们快速构建一个应用
  • 大型分布式网络服务的调用 这部分有SpringCloud 来完成 实现分布式
  • 在分布式中间 进行流式数据计算 批处理 使用 spring cloud data flow
  • spring 为我们提供了从开始构建应用到大型分布式应用全流程方案

Hello Word:

  • jdk 1.8
  • maven 3.6.1
  • springboot
  • IDEA
方式一:

在 官网 直接下载后 导入 idea 开发

Spring 官网:

在这里插入图片描述

项目结构:

在这里插入图片描述

HelloworldApplication :程序的主入口 不能修改 所有的包都要创建在 它的同级目录下 不然无法访问

helloController

@RestController
public class helloController {
    @RequestMapping("/hello")
    public String Hello(){
        //调用业务接收前端的参数
        return "hello world!!!";
    }
}

运行:

在这里插入图片描述

在这里插入图片描述

  • 项目元数据:创建时输入的 Project Metadata 部分 也就是Maven 项目的基本元素 包括:groupid artifactld version name description 等等

  • parent:继承 spring-boot-starter-parent 的依赖管理 控制版本与打包等内容

  • dependencies:项目具体依赖 这里包括 spring-boot-strater-web 用于实现 HTTP 接口 (该接口包含 Spring MVC )

    • 使用Spring MVC 构建Web (包括Restful)应用程序的入门者 使用Tomact 作为默认嵌入式容器

    spring-boot-starter-test :用于编写单元测试的依赖包

  • build:构建配置部分 默认使用 spring-boot-maven-plugin 配合 spring-boot-starter-parent 就可以把 Spring Boot 应用打包成 JAR 运行。

方式二:

使用idea 创建一个springboot 项目

在这里插入图片描述

helloController

//SpringBoot 完成一件事: 自动装配

@RestController
@RequestMapping("/hello")
public class helloController {
    @ResponseBody
    @RequestMapping("/hello")
    public String Hello(){
        //调用业务接收前端的参数
        return "hello world!!!";
    }
}

application.properties

# 更改项目访问的端口号
server.port=8081

banner.txt

                        [{+?]
                              ~++++{.
           !{{{{[}}{{{{{_.   .{++++{   {{}{
         {++++++{++++++?}]++?{{_++{  -{++++}
        {+rr+r\+{+++++{$$ +++++++[{<<+++++{
        }++++++{+++++++{<{}+{$k {+++++}}{^
        }{++[{++++++++++++++{   _++++++++{,
          {++++++++++++++++++]}?+++++++++++{
            {{++++++++++++++++++++++++++++++}
               {{+++++++++++++++++++++++_++++{
                 {+++++++++++++++++++}}}}}}}++I
                {+++++++++++++++++++[}}}}}}}_+{
               []++vY+++++++++++++++}}}}}}}}++{
               {++++X$X|+++++++++++++}}}}}[+++{
               {_++++X%$$qXXv\|XXX)++++++++++-^
         .     :{++++++XX$$$$$qXv++++++++++++{
                l-++++++++++++++++++++++++++{
                 I{++++++++++++++++++++++++{
                  C0{}++++++++++++++++++-{0C
                 COOOOO0{{[+++++++++_{{0OOO0C
                COOOOOOOOOOOOOOUCOOOOOOOOOOOOn<<<"
            "<<CLOOOOOOOOOOOOOOOOOOOOOOOOOOOOC   ;<<<<<<
          <<<. COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOC     <<<<
       !<<.   COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOCz
      <;!>    COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOC
             {QOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOL   '
             COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOY"< <
             COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOC<<<<<<`
            'COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOC
            [COOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOC
            ]CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
                       <<             ^<^
                       <<             ^<^
                       <<             ^<^
                 $$$$$$$$>      O$$$$$$$z

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zhang</groupId>
    <artifactId>SpringBoot-study-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringBoot-study-01</name>
    <description>SpringBoot-study-01</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <!--版本号不用写 它会继承父依赖-->
    <dependencies>

        <!--启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>aliyunmaven</id>
            <url>https://maven.aliyun.com/repository/public</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>nexus-aliyun</id>
            <name>Nexus-aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <enabled>true</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>
</project>

运行:

在这里插入图片描述

原理

自动配置

pom.xml
  • spring-boot-dependencies: 核心依赖在父工程中
  • 我们在或 引入一些SpringBoot 依赖的时候不需要指定版本 因为有版本仓库管理
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.5</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
启动器
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
  • 就是springboot的启动场景
  • 比如 spring-boot-starter-web 就会帮我们导入 web 所需要的所有环境
  • spring boot 会将所有的功能场景变成一个个 启动器 starter
  • 我们要什么功能 只需要找到对应的启动器即可
主程序
// @SpringBootApplication :标注这个类是一个 springboot 的应用 程序 
@SpringBootApplication
public class SpringBootStudy01Application {
    // 将spring boot 应用启动
    public static void main(String[] args) {
        SpringApplication.run(SpringBootStudy01Application.class, args);
    }
}

注解:

@SpringBootConfiguration   : springboot 的配置
	@Configuration :spring 配置类
		@Component: 说明也是一个spring的组件

@EnableAutoConfiguration : 自动配置
	@AutoConfigurationPackage : 自动配置包
		@Import(AutoConfigurationPackages.Registrar.class) :自动配置 包注册
	@Import(AutoConfigurationImportSelector.class) : 自动配置导入选择

SpringApplication.run:

该方法·主要分为两部分 一部分是 springApplication 的实例 二是run方法 的执行

SpringApplication:

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

SpringBoot 配置文件

配置文件:使用一个 全局的配置文件

  • application.properties
    • 语法: key = value
  • application.yml
    • 语法: key: 空格 value

配置文件的作用:

修改springboot 的默认配置

yaml

标记语言:以前的配置文件 大多数是用xml 来配置

xml 配置

<server>
	<port>8081</port>
</server>

yaml 配置:

server:
	prot: 8081

application.yaml

替代 application.properties

# 对空格要求十分严格
# 可以注入到配置类中
# 普通的 k-v
name: xiaotao

# 对象
student:
  name: xiaotao
  age: 18

# 行内写法
student1: {name: xiaotao,age: 18}

# 数组
pets:
  - cat
  - dog
  - pig

pers: [cat,dog,pig]

实体类:

Dog

@Component
@Data
public class Dog {
    @Value("旺财")
    private String name;
    @Value("3")
    private Integer age;
}

Person

@Component
@Data
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String,Object> map;
    private List<Object> list;
    private Dog dog;
}

application.yaml

# 通过 yaml 给实体类赋值
person:
  name: xiaotao
  age: 18
  happy: true
  birth: 2021/10/31
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
    - girl
  dog:
    name: 旺财
    age: 2

测试

@SpringBootTest
class SpringBootStudy01ApplicationTests {

    @Autowired
    private Person person;

    @Test
    void contextLoads() {
        System.out.println(person);
    }

}

在这里插入图片描述

pom.xml

添加依赖即可解决

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

运行:

image-20211031124942773

# 通过 yaml 给实体类赋值
person:
  name: xiaotao${random.uuid}   # 产生一个随机的 uuid
  age: ${random.int}
  happy: true
  birth: 2021/10/31
  map: {k1: v1,k2: v2}
  list:
    - code
    - music
    - girl
  dog:
    name: ${person.hello:hello}_旺财   # 如果 person.hello 存在则取 对应值 否则 取 hello
    age: 2

image-20211031130915987

image-20211031130937378

yaml 及 properties 对比

image-20211031131203537

松散绑定

image-20211031132034167

在这里插入图片描述

在这里插入图片描述

JSR303 数据校验

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

Bean Validation 中内置的 constraint

3145530-8ae74d19e6c65b4c

Hibernate Validator 附加的 constraint

3145530-10035c6af8e90a7c

多环境配置

application.properties

在这里插入图片描述

application.yaml

使用yaml 一个配置文件即可以

通过 --- 即可区分不同配置环境

server:
  port: 8081
spring:
  profiles:
    active: dev

---
server:
  port: 8082
spring:
  profiles: dev

---
server:
  port: 8083
spring:
  profiles: test

自动装配原理

  • SpringBoot 启动会加载大量的自动配置类

  • 我们看我们需要的功能有没有在SpringBoot 默认写好的自动配置类中

  • 看这个自动配置类中到底配置了哪些组件· 只要我们要用到的组件存在其中 我们就不需要手动配置了

  • 给容器中自动配置类添加组件的时候 会从·properties类中获取属性 我们只需要在配置文件中指定这些属性的值即可

    xxxAutoConfigurartion: 自动配置类 给容器添加组件

    xxxProperties : 封装配置文件中相关属性

debug = true  # 开启日志 查看哪些自动配置类生效 哪些没有生效

Springboot Web 开发

自动配置

SpringBoot 到底帮我们配置了什么? 我们能不能修改? 能修改什么? 能不能扩展?

  • xxxAutoConfiguraion 向容器中自动配置组件
  • xxxProperties : 自动配置类 装配配置文件中自定义的一些内容

要解决的问题:

  • 导入静态资源
  • 首页
  • jsp 模板引擎 Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 拦截器
  • 国际化
静态资源

https://www.webjars.org/

方式一: 以 maven 的方式引入静态资源 (不推荐)

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

方式二: 放在 classpath:static classpath:public classpath:resources 文件夹下面

Thymeleaf
在 templates 目录下的所有页面 只能通过controller 来跳转
需要模板引擎的支持 thymeleaf

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

引入命名空间

<html lang="en" xmlns:th="www.thymeleaf.arg">

在这里插入图片描述

首页

所有的静态资源都需要使用 thylemeaf 接管

url : @{}

页面国际化
  • 我们需要配置 I18n 文件
  • 我们需要在项目中进行按钮自动切换 需要自定义一个组件 LocaleResolver
  • 将组件 配置到 Spring 容器中托管 @Bean
  • #{}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8vNNGdtH-1690941414317)(http://qiniu.xiaotao.cloud/QQ截图20211105001121.png)]

CRUD
数据库

无论是 sql(关系型数据库) 还是 nosql(非关系型数据库)springboot 都是采用 SpringData 的方式进行统一管理

JDBC

JDBCController

@RestController
public class JDBCController {
    @Autowired
    JdbcTemplate jdbcTemplate;

    @GetMapping("/userList")
    public List<Map<String,Object>> userList(){
        String sql = "select * from stuinfo";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }

    @GetMapping("/addUser")
    public String addUser(){
        String sql = "insert into stuinfo values('T123100','小灰灰','女',19,'重庆')";
        int i = jdbcTemplate.update(sql);
        return "addUser";
    }

    @GetMapping("/update")
    public String update(){
        String sql = "update stuinfo set stu_name = '小涛',stu_sex = '女',stu_age = 18,stu_Address = '重庆'  where stu_id = 'T123005';";
        int i = jdbcTemplate.update(sql);
        return "update";
    }

    @GetMapping("/del/{id}")
    public String del(@PathVariable("id") String id){
        String sql = "delete from stuinfo where stu_id = ?";
        int i = jdbcTemplate.update(sql,id);
        return "del";
    }
}

Druid

application.yaml

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUniocde=true&characterEncoding=utf-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource    # 指定使用 Druid 数据源

    #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


DriverConfig

@Configuration
public class DriverConfig {
    /**
     * 使自定义配置 druid 生效
     * @return
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

    // 后台监控 运行访问 http://localhost:8080/druid
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        // 后台需要有人登录 账号 密码 配置
        HashMap<String,String> initParameters = new HashMap<>();
        // 增加配置
        // 登录的 key 是固定的  loginUsername  loginPassword
        initParameters.put("loginUsername","admin");
        initParameters.put("loginPassword","123456");

        // 允许谁能访问  值为空 代表 任何人都可以
        initParameters.put("allow","");

        // 禁止谁访问
        //initParameters.put("xiaotao","192.168.42.233");

        bean.setInitParameters(initParameters);  // 设置初始化参数
        return bean;
    }
}

后台监控

访问: http://localhost:8080/druid

在这里插入图片描述

在这里插入图片描述

执行一次查询

在这里插入图片描述

监控到

在这里插入图片描述

过滤
//filter
public FilterRegistrationBean webStatFilter(){
    FilterRegistrationBean bean = new FilterRegistrationBean();

    HashMap<String,String> initParameters = new HashMap<>();

    initParameters.put("exclusions","*.js,*.css,/druid/*");   // 这些不统计

    bean.setInitParameters(initParameters);
    bean.setFilter(new WebStatFilter());
    return bean;
}

Mybatis
  • 导入包
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.3.2</version>
</dependency>

  • 配置文件
<?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.zhang.mapper.StuInfoMapper">
    <select id="queryStuList" resultType="StuInfo">
        select * from stuinfo
    </select>
</mapper>

  • mybatis配置
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 整合mybatis
mybatis.type-aliases-package=com.zhang.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

  • mapper service controller
SpringSecurity (安全) 权限
  • 在 web 开发中 安全第一位
  • 安全应该在设计之初考虑
  • 漏洞 隐私泄露
shiro SpringSecurity

两个都是用于安全的框架 很像 除了类不一样

认证 授权

  • 功能权限
  • 访问权限
  • 菜单权限
  • 原来 拦截器 过滤器 …
  • 现在框架

springSecurity 是针对 spring 项目的安全框架 样式 SpringBoot底层安全模块默认的选形 他可以实现强大的web 安全控制 外面只需要 引入

进行少量的配置 即可实现强大的web安全管理

  • WebSecurityConfigurerAdapter:自定义 Security 模式
  • :自定义认证策略
  • @EnableWebSecurity:开启WebSecurity 模式 @Enablexxx : 开启莫个功能

SpringSecurity 的两个主要目标是“认证” 和 “授权”(访问控制)

认证(Authentication)

授权(Authorization)

SpringSecurity

导入包:

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

在这里插入图片描述

SecurityConfig

/**
 * 权限 管理  通过 AOP 横切
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 授权
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 首页所有人可以访问 功能页只有对应有权限的人才能访问
        // 链式编程
        // antMatchers("/"):代表哪些页面
        // permitAll():所有人都可以访问
        // hasRole("vip1") : 指定人可以访问
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3");

        // 没有权限默认会到登录页面  需要开启登录的页面 会走默认的页面  http://localhost:8080/login
        // loginPage("/toLogin") : 定制自己的登录页面
        // 框架默认使用的是 username  password 要使用 不同的可以加上下面的属性
        http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");


        // 开启注销功能
        // 他会有防止网站攻击: 只能使用 post 请求
        // 不加需要 post 请求 才能注销
        http.csrf().disable();  // 关闭 csrf 功能 可以使用 get 请求   (解决不能注销问题)
        http.logout().logoutSuccessUrl("/");
        
        //开启记住我 cookie 默认保存两周  自定义接收前端参数
        http.rememberMe().rememberMeParameter("remember");
    }

    /**
     * 认证
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 从内存中获取
        // 需要进行密码加密
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("xiaotao").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");

        // 也可以从数据库中获取
        // auth.jdbcAuthentication().withUser("").password("").roles("");

    }
}

RouterController

@Controller
public class RouterController {
    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String login(){
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") Integer id){
        return "views/level1/"+id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") Integer id){
        return "views/level2/"+id;
    }

    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") Integer id){
        return "views/level3/"+id;
    }
}

实现有什么权限看到那个页面:

<!--security thymeleaf 整合-->
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

在这里插入图片描述

 <!--登录注销-->
<div class="right menu">
    <!--如果未登录-->
    <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>
            <!--角色:<span sec:authentication="principal.getAuthorities()"></span>-->
        </a>
        <!--注销-->
        <a class="item" th:href="@{/logout}">
            <i class="sign-out icon"></i> 注销
        </a>
    </div>
</div>

<!--sec:authorize="hasRole('vip1')" : 假如有 vip1 权限显示-->
<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>

shiro
  • Apache shiro 是一个java 的安全权限框架
  • shiro 可以非常容易的开发出足够好的应用 不仅可以在 javaSE 环境 也可以在 javaEE 环境
  • shiro 可以完成 认证 授权 加密 会话管理 web集成 缓存

在这里插入图片描述

subject: 用户

SecurityManager:管理所有用户

Realm:连接数据

快速开始
  • 导入依赖
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.8.0</version>
</dependency>

<!-- configure logging -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

  • log4j.properties
log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=INFO

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

  • shiro.ini
[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

  • Quickstart.java
public class Quickstart {
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);


    public static void main(String[] args) {

        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        // 获取当前的用户对象 subject
        Subject currentUser = SecurityUtils.getSubject();

        // 通过当前用户得到 session
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("subject=>session====> [" + value + "]");
        }

        // 测试当前用户是否被认证
        if (!currentUser.isAuthenticated()) {
            // 通过当前 用户名和密码 设置一个 令牌(token)
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);   // 设置记住我
            try {
                currentUser.login(token);  // 执行了登录操作
            } catch (UnknownAccountException uae) {   // 用户名不存在
                log.info("There is no user with username of " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) { // 密码错误
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            } catch (LockedAccountException lae) {  // 账户被锁定
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            // ... catch more exceptions here (maybe custom ones specific to your application?
            catch (AuthenticationException ae) {  // 认证失败
                //unexpected condition?  error?
            }
        }

        //获取当前用户的信息
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //判断当前用户是否具有该权限
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //test a typed permission (not instance-level)
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //a (very powerful) Instance Level permission:
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        //注销
        currentUser.logout();
        // 结束系统
        System.exit(0);
    }
}


SpringBoot 集成 shiro
  • 导入依赖
<dependencies>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--shiro 整合 Spring 的包 -->
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.7.1</version>
    </dependency>
    <!--shiro 整合 thymeleaf 的包 -->
    <dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
    <!--log4j-->
    <dependency>
        <groupId>com.att.inno</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.13</version>
    </dependency>
    <!-- mybatis-spring-boot-starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!--thymeleaf-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

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

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

</dependencies>

在这里插入图片描述

IndexController

@Controller
public class IndexController {
    @RequestMapping({"/","/index","index.html"})
    public String index(Model model){
        model.addAttribute("msg","hello shiro");
        return "index";
    }

    @RequestMapping("/user/add")
    public String add(){
        return "/user/add";
    }

    @RequestMapping("/user/update")
    public String update(){
        return "/user/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 封装用户登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);   // 执行了登录方法
            return "index";
        } catch (UnknownAccountException uae) {   // 用户名不存在
            model.addAttribute("msg","用户名错误");
            return "login";
        } catch (IncorrectCredentialsException ice) { // 密码错误
            model.addAttribute("msg","密码错误");
            return "login";
        } catch (LockedAccountException lae) {  // 账户被锁定
            model.addAttribute("msg","账户被锁定");
            return "login";
        }
    }

    /**
     * 未授权页面
     * @return
     */
    @RequestMapping("/unauth")
    @ResponseBody
    public String unauthorized(){
        return "没有授权不能访问此页面";
    }
}

ShiroConfig

@Configuration
public class ShiroConfig {
    // 第三步  ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加shiro 的内置过滤器
        /**
         * anon: 无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有记住我 才能实现
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        // 授权
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

        // 拦截
        /*filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");*/
        // 可以使用通配符
        filterMap.put("/user/*","authc");
        factoryBean.setFilterChainDefinitionMap(filterMap);

        // 设置登录页面
        factoryBean.setLoginUrl("/toLogin");

        // 设置未授权页面
        factoryBean.setUnauthorizedUrl("/unauth");
        return factoryBean;
    }

    // 第二步 DefaultWebSecurityManager
    @Bean(name = "securityManager")                            //@Qualifier("userRealm") userRealm: 为下面的方法名 或 name 都可以
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联 realm
        securityManager.setRealm(userRealm);

        return securityManager;
    }

    // 第一步 创建 realm 需要自定义类
    @Bean(name = "userRealm")     // 或 @Bean(name = "userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }

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

}

UserRealm

/**
 * 自定义 realm
 */
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了 ==》 授权 doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //获取到当前登录的对象
        Subject subject = SecurityUtils.getSubject();
        User currentUSer = (User) subject.getPrincipal();
        //设置当前用户的权限
        info.addStringPermission(currentUSer.getPerms());

        return info;
    }

    /**
     * 认证
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了 ==》 认证 doGetAuthenticationInfo");

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        // 用户名  数据库中获取
        User user = userService.queryUserByName(userToken.getUsername());
        if(user==null){
            return null;   //抛出异常
        }

        // 把当前登录用户存入session
        Subject currentSubject = SecurityUtils.getSubject();
        currentSubject.getSession().setAttribute("loginUser",user);

        // 密码认证 shiro 会自己实现
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");  //这里必须存入 user 上面才能获取
    }
}

UserMapper

@Mapper
@Repository
public interface UserMapper {
    User queryUserByName(String name);
}

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.arg"
      xmlns:shiro="http://www.thymeleaf.arg/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <h1>首页</h1>
  <p th:if="${session.loginUser==null}">
      <a th:href="@{/toLogin}">登录</a>
  </p>
  <h3 th:text="${msg}"></h3>
<hr/>
  <div shiro:hasPermission="user:add">
      <a th:href="@{/user/add}">add</a>
  </div>
  <div shiro:hasPermission="user:update">
      <a th:href="@{/user/update}">update</a>
  </div>
</body>
</html>

application.yaml

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?useUniocde=true&characterEncoding=utf-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource    # 指定使用 Druid 数据源

    #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


Swagger

学习目标:

  • 了解Swgger 的作用及概念
  • 了解前后端分离
  • 在SpringBoot中集成Swagger
简介

前后端分离:

Vue + SpringBoot

后端时代:前端只用管理静态页面 html ===》后端 模板引擎 jsp

前后端分离时代:

后端:控制层 服务层 数据访问层

前端:控制层 试图访问层

​ 伪后端数据 json

前后端如何交互 ====》 API

前后端相对独立 松耦合

前后端甚至可以部署在不同的服务器上

问题:

前后端继承联调 前后端人员无法做到“及时协商 今早解决” 最后导致问题爆发

解决方案:

首先制定 schema 【计划提纲】 实时更新最新API 降低集成风险

  • 原来:指定 word 计划文档
  • 前后端分离:
    • 前端测试后端接口: postman
    • 后端提供接口 需要实时更新最新的消息及改动
Swagger
  • 世界上 最流行的API 框架
  • RestFul Api 文档在线自动生成工具 =》 Api 文档与 api 定义同时更新
  • 直接运行 可以在线测试 Api 接口
  • 支持多种语言 :java php …
SpringBoot 集成 Swagger

在项目中使用Swagger需要springfox

导入依赖

  • swagger
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>

  • ui
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>

Config 配置:

@Configuration
@EnableSwagger2     // 开启 swagger2
public class SwaggerConfig {
    
}

测试运行: swagger-ui.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xNLsWfZ2-1690941414320)(http://qiniu.xiaotao.cloud/QQ截图在这里插入图片描述
.png)]

配置 swagger 基本信息
@Configuration
@EnableSwagger2     // 开启 swagger2
public class SwaggerConfig {
    // 配置了 Swagger的Docket 的bean 实例
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo());
    }

    // 配置 swagger 信息 apiInfo
    public ApiInfo apiInfo(){
        // 作者信息
        Contact contact = new Contact("小涛","https://www.xiaotao.cloud/","2177393158@qq.com");
        return new ApiInfo(
                "xiaotao 的 SwaggerApi ",
                "即使再小的帆也能远航!",
                "1.0",
                "https://www.xiaotao.cloud/",
                contact,
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0",
                new ArrayList()
        );

    }
}

Swagger 配置扫描接口
// 配置了 Swagger的Docket 的bean 实例
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                // RequestHandlerSelectors: 配置要扫描接口的方式
                    // .basePackage("com.zhang.controller"): 指定要扫描的包
                    // .any():  扫描全部
                    // .none(): 不扫描
                    // .withClassAnnotation(RestController.class): 扫描类上的注解 参数是一个注解的反射对象
                    // .withMethodAnnotation(GetMapping.class): 扫描方法上的注解
                .apis(RequestHandlerSelectors.basePackage("com.zhang.controller"))
                // paths: 过滤什么路径
                .paths(PathSelectors.ant("/zhang/**"))
                .build();
    } 

配置是否启动 Swagger
return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(true)  // 默认为 true 改为false 则不能使用
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.zhang.controller"))
                .build();

实现在开发的时候使用swagger 上线时不使用swagger
@Bean
    public Docket docket(Environment environment){
        // 实现在开发的时候使用swagger 扫描 上线时不使用swagger
        //设置要显示的环境
        Profiles profiles = Profiles.of("dev", "test");
        // 通过 environment.acceptsProfiles 判断是否处于自己设定的环境中
        boolean flag = environment.acceptsProfiles(profiles);
        
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(flag)   // flag : true false
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.zhang.controller"))
                .build();
    }

配置Api分组:

.groupName("xiaotao")

配置 多个分组:

@Bean
public Docket docket1(){
    return new Docket(DocumentationType.SWAGGER_2).groupName("A");
}

@Bean
public Docket docket2(){
    return new Docket(DocumentationType.SWAGGER_2).groupName("B");
}

@Bean
public Docket docket3(){
    return new Docket(DocumentationType.SWAGGER_2).groupName("C");
}

在这里插入图片描述

接口注释

实体类

@ApiModel("用户实体类")
public class User {
    @ApiModelProperty("用户名")
    public String username;
    @ApiModelProperty("密码")
    public String password;
}

controller

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "hello word";
    }

    // 只要接口中 返回值存在 实体类 他就会被 扫描到 swagger 中
    @PostMapping("/user")
    public User user(){
        return new User();
    }

    //Operation 接口
    @ApiOperation("Hello 控制类")
    @GetMapping("/hello2")
    public String hello2(@ApiParam("用户名") String username){
        return "hello"+username;
    }

    @ApiOperation("post 测试控制类")
    @PostMapping("/postt")
    public User postt(@ApiParam("用户") User user){
        return user;
    }
}

总结:

  • 我们可以使用 swagger 给一些比较难理解的属性或接口 增加注释信息
  • 接口文档实时更新
  • 可以在线测试
  • 注: 在正式发布时 关闭swagger 这样更加安全 节省内存
任务
异步任务
@Service
public class AsyncService {

    // 告诉 spring 这是一个 异步请求
    @Async
   public void hello(){
       try {
           Thread.sleep(3000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println("数据正在处理.....");
   }
}

@RestController
public class AsyncController {
    @Autowired
    private AsyncService asyncService;

    @RequestMapping("/hello")
    public String hello(){
        asyncService.hello();  //  不使用异步注解  停止 3秒
        return "ok";
    }
}

主启动类

@EnableAsync // 开启异步请求注解
@SpringBootApplication
public class SpringBootTask08Application {

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

}

定时任务
TaskScheduler  任务调度者
TaskExecutor   任务执行者

@EnableScheduling  开启定时功能的注解
@Scheduled    什么时候执行

主启动类

@EnableAsync // 开启异步请求的注解
@EnableScheduling // 开启定时功能的注解
@SpringBootApplication
public class SpringBootTask08Application {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootTask08Application.class, args);
    }
}

@Service
public class ScheduledService {
    /**
     * 在特定的时间执行
     * cron : 表达式
     * 秒 分 时 日 月 周几
     */
    @Scheduled(cron = "0 * * * * 0-7")
    public void hello(){
        System.out.println("hello 我出来啦!");
    }
}

常用表达式例子
  (10/2 * * * * ?   表示每2秒 执行任务
  (10 0/2 * * * ?    表示每2分钟 执行任务
  (10 0 2 1 * ?   表示在每月的1日的凌晨2点调整任务
  (20 15 10 ? * MON-FRI   表示周一到周五每天上午10:15执行作业
  (30 15 10 ? 6L 2002-2006   表示2002-2006年的每个月的最后一个星期五上午10:15执行作
  (40 0 10,14,16 * * ?   每天上午10点,下午2点,4点 
  (50 0/30 9-17 * * ?   朝九晚五工作时间内每半小时 
  (60 0 12 ? * WED    表示每个星期三中午12点 
  (70 0 12 * * ?   每天中午12点触发 
  (80 15 10 ? * *    每天上午10:15触发 
  (90 15 10 * * ?     每天上午10:15触发 
  (100 15 10 * * ?    每天上午10:15触发 
  (110 15 10 * * ? 2005    2005年的每天上午10:15触发 
  (120 * 14 * * ?     在每天下午2点到下午2:59期间的每1分钟触发 
  (130 0/5 14 * * ?    在每天下午2点到下午2:55期间的每5分钟触发 
  (140 0/5 14,18 * * ?     在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 
  (150 0-5 14 * * ?    在每天下午2点到下午2:05期间的每1分钟触发 
  (160 10,44 14 ? 3 WED    每年三月的星期三的下午2:102:44触发 
  (170 15 10 ? * MON-FRI    周一至周五的上午10:15触发 
  (180 15 10 15 * ?    每月15日上午10:15触发 
  (190 15 10 L * ?    每月最后一日的上午10:15触发 
  (200 15 10 ? * 6L    每月的最后一个星期五上午10:15触发 
  (210 15 10 ? * 6L 2002-2005   2002年至2005年的每月的最后一个星期五上午10:15触发 
  (220 15 10 ? * 6#3   每月的第三个星期五上午10:15触发

邮件任务
  • 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

  • 配置文件
spring.mail.username=2177393158@qq.com
spring.mail.password=ktdihwxprzqveaad     
spring.mail.host=smtp.qq.com

# 开启加密验证 (qq才需要)
spring.mail.properties.mail.smtp.ssl.enable=true

  • 测试
@Autowired
JavaMailSenderImpl mailSender;

/**
     * 简单的邮件
     */
@Test
void contextLoads() {
    SimpleMailMessage mailMessage = new SimpleMailMessage();

    mailMessage.setSubject("hello xiaotao");
    mailMessage.setText("嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿");
    mailMessage.setTo("2177393158@qq.com");
    mailMessage.setFrom("2177393158@qq.com");

    mailSender.send(mailMessage);
}

/**
     * 复杂的邮件
     */
@Test
void contextLoads2() throws MessagingException {
    MimeMessage mimeMessage = mailSender.createMimeMessage();
    // 组装
    MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);

    //正文
    helper.setSubject("hello xiaotao");
    helper.setText("<p style='color:red'>嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿</P>",true);

    // 附件
    helper.addAttachment("1.jpg",new File("D:\\01.jpg"));
    helper.addAttachment("2.jpg",new File("D:\\01.jpg"));

    helper.setTo("2177393158@qq.com");
    helper.setFrom("2177393158@qq.com");

    mailSender.send(mimeMessage);
}

分布式 Dubbo + Zookeeper + SpringBoot

什么是分布式系统:

在 《分布式系统原理与泛型》一书中有如下定义:“分布式系统是若干独立计算机的集合 这些计算机对于用户来说就像单个相关系统”

分布式系统(distributed system)是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件

利用更多的机器 处理更多的数据

只有当单个机器无法处理的时候我们才会考虑使用分布式

详细概念

https://mp.weixin.qq.com/s/sKu9-vH7NEpUd8tbxLRLVQ

Zookeeper 安装

Zookeeper : 注册中心

下载:http://archive.apache.org/dist/zookeeper/

在这里插入图片描述

管理员身份

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Dubbo 安装

Dubbo : 一个jar包

下载dubbo-admin

地址 :https://github.com/apache/dubbo-admin/tree/master-0.2.0

在这里插入图片描述

先要启动 (管理员)

zkServer.cmd :打开 zookeeper 服务

去cmd 运行(管理员身份)

java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

访问:

在这里插入图片描述

在这里插入图片描述

dubbo-admin : 是一个监控管理后台 查看我们注册了那些服务· 那些服务被消费了

SpringBoot 整合 Dubbo + Zookeeper

新建项目

在这里插入图片描述

在这里插入图片描述

服务提供者

导入依赖:

<!-- Dubbo Spring Boot Starter -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.1</version>
</dependency>

<!-- 引入zookeeper -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.14</version>
    <!--排除这个slf4j-log4j12-->
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

application.properties

server.port=8081

#当前应用名字
dubbo.application.name=provider-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
#扫描指定包下服务
dubbo.scan.base-packages=com.zhang.service


TicketServiceImpl

package com.zhang.service;

import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;

@Service  // dubbo :下的 在项目一启动就自动注到注册中心
@Component //使用了dubbo 后尽量不要用 service 注解 会和上一个冲突
public class TicketServiceImpl implements TicketService{
    @Override
    public String getTicket() {
        return "小涛 你好呀!!";
    }
}

先要启动 (管理员)

zkServer.cmd :打开 zookeeper 服务

去cmd 运行(管理员身份)

java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

启动项目:

provider-server

在这里插入图片描述

在这里插入图片描述

服务消费者

导入依赖:(同上)

<!-- Dubbo Spring Boot Starter -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-spring-boot-starter</artifactId>
    <version>2.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.sgroschupf/zkclient -->
<dependency>
    <groupId>com.github.sgroschupf</groupId>
    <artifactId>zkclient</artifactId>
    <version>0.1</version>
</dependency>

<!-- 引入zookeeper -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>2.12.0</version>
</dependency>
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.14</version>
    <!--排除这个slf4j-log4j12-->
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
    </exclusions>
</dependency>

application.properties

server.port=8082

#当前应用名字
dubbo.application.name=consumer-server
#注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181

UserService

package com.zhang.service;

import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;

@Service  // 放到spring 容器中
public class UserService {
    // 获取到 provider-server 提供的票  需要去注册中心那到服务
    /**
     * 两种方式:
     * 1. pom 坐标
     * 2. 定义路径相同的接口名
     */
    // 这里使用的是 路径相同的接口名
    @Reference // 引用
    TicketService ticketService;

    public void buyTicket(){
        String ticket = ticketService.getTicket();
        System.out.println("在注册中心拿到票===> "+ticket);
    }
}

TicketService

public interface TicketService {
    public String getTicket();
}

测试:

@SpringBootTest
class ConsumerServerApplicationTests {

    @Autowired
    UserService userService;

    @Test
    void contextLoads() {
        userService.buyTicket();
    }

}

在这里插入图片描述

启动项目

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值