SpringBoot使用手册

本文最后更新于:2021年3月19日 晚上

SpringBoot开冲,Spring的配置实在是太太太太多了。

一、SpringBoot概述

Maven、Spring、SpringMVC、SpringBoot均约定大于配置

微服务架构。

1)特点

  • 开箱即用,没有代码生成,也无需XML配置,同时也可以修改默认值来满足特定的需求
  • 提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标、健康监测等等
  • SpringBoot不是Spring功能上的增强,而是提供了一种快速使用Spring的方式

2)核心功能

  • 起步依赖:将具备某些功能的坐标打包在一起,并提供一些默认功能。(本质上是一个Maven项目对象模型POM)
  • 自动配置:应用程序启动时,自动地完成相应配置

谈谈你对SpringBoot的理解:

SpringBoot提供了一种快速使用Spring的方式,无需繁杂的xml文件配置;其可以在应用程序启动时自动装配组件(自动装配的原理巴拉巴拉…;SpringBoot通过SpringBootApplication注解标注引导类,然后再通过SpringApplication.run方法运行该引导类。这个SpringApplciation在我看来,主要做了四个事吧…

二、HelloSpringBoot

1)spring initializer

  • 在使用IDEA创建Spring Boot项目的时候,报Artifact contains illegal characters”:把Artifact的内容都改成小写;例如 : “Demo” 改为 “demo”

2)配置POM文件

  • 继承SpringBoot的起步依赖spring-boot-starter-parent
  • 由于导入了父依赖,因此在导入SpringBoot相关依赖时,无需自己再指定版本了
  • 导入web的启动依赖以使SpringBoot集成SpringMVC进行Controller的开发
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
	
    <!-- SpringBoot的起步依赖 -->
    <!-- 所有的SpringBoot工程都必须继承spring-boot-starter-parent -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.0</version>
    </parent>
	
    <!--  项目名、版本、组织等 -->
    <groupId>com.gaowl</groupId>
    <artifactId>SpringBoot_HelloWorld</artifactId>
    <version>1.0-SNAPSHOT</version>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 可以实现热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
	  <!-- web功能的启动依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <!-- 打jar包用 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3)工程热部署

开启项目自动编译 同时按住 Ctrl + Shift + Alt + / 然后进入Registry ,勾选自动编译并调整延时参数
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>

4)修改端口号、banner

  • 在application.properties等配置文件中输入server.port=8081即可自定义端口号
  • banner在线生成网站:https://www.bootschool.net/ascii
  • 在resource文件夹下,新建banner.txt,粘贴上述网站中生成的信息,即可自定义banner

5)程序入口和控制器

package com.gaowl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// SpringBoot应用程序的启动入口
@SpringBootApplication
public class Springboot01HelloworldApplication {

    public static void main(String[] args) {
        // 此处是SpringApplication!!!!不是SpringBootApplication
        // run方法 表示运行SpringBoot的引导类,其参数用SpringBoot引导类的字节码即可
        SpringApplication.run(Springboot01HelloworldApplication.class, args);
    }
}

SpringApplication.run这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

package com.gaowl.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @RequestMapping("/test")
    public String test(){
        return "测试环境已成功配置...";
    }
}

三、SpringBoot的配置文件

1)几种配置文件类型

  • application.ymlapplication.yaml或者application.properties

    • 其中application.properties优先级最高,application.yml优先级最低
    • 更推荐yml,比properties不知道好用到哪了去了
  • yaml基本语法:其通过空格来控制层级关系

    # 1.普通数据的配置
    key: value
    
    # 2.对象配置
    map:
    	key1: value1
    	key2: value2
    # 示例    
    person:
    	name: sam
    	age: 18
    	addr: beijing
    # 行内写法
    person: {name: sam, age: 18, addr: beijing}
    
    # 3.配置数组(list、set)
    city:
    	- beijing
    	- tianjian
    	- chongqing
    city: [beijing, tianjin, chongqing]
    
    # 4.配置集合等
    student:
    	- name: tom
    	  age: 18
    	  addr: beijing
    	- name: sam
    	  age: 20
    	  addr: tianjin
    student: [{name: tom, age: 18, addr: beijing},{name: sam, age: 20, addr: tianjin}]

2)配置文件 中的信息 注入到java中

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定(转换为驼峰命名) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  • @ConfigurationProperties映射

    // 在类上面 @ConfigurationProperties(prefix="person") 会自动从配置文件中寻找
    
    // 添加预处理依赖
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-configuration-processor</artifactId>
    	<optional>true</optional>
    </dependency>
    
    // 然后在类里面,直接创建变量就好,无需再@value,比如
    private String name;
    private String addr;
  • @Value映射

    @Value("${name}")
    private String name;
    
    @Value("${person.addr}")
    private String addr;
  • 示例:

    实体类Person

    package com.gaowl.pojo;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    @Component
    @ConfigurationProperties(prefix = "person")  // 自动到yml等配置文件中寻找person,进行绑定
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
        private String firstName;
        private Integer age;
        private Boolean happy;
        private Date birth;
        private Map<String, Object> maps;
        private List<Object> lists;
        private Dog dog;
    
    }

    配置文件application.yml

    person:
      first-name: gaowl
      age: 23
      happy: true
      bitth: 2021/1/9
      maps: {k1: v1,k2: v2}
      lists:
        - code
        - girl
        - music
      dog:
        name: ${person.happy}
        age: ${random.int}

3)JSR303数据校验

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

@Component  // 注册bean
@ConfigurationProperties(prefix = "person")
@Validated  // 开启数据校验
public class Person {
    @Email(message="邮箱格式错误")  // 不符合邮箱格式时,弹出提示消息
    private String email_addr;
}

常用的校验注解:

@NotNull(message="名字不能为空")
private String userName;

@Max(value=120,message="年龄最大不能查过120")
private int age;

@Email(message="邮箱格式错误")
private String email;


1.空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.    
    
2.Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false      
    
3.长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Length(min=, max=) string is between min and max included.
    
4.日期检查
@Past       验证 DateCalendar 对象是否在当前时间之前  
@Future     验证 DateCalendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

4)多环境切换

配置文件的设置路径有以下四种,高优先级的配置会覆盖低优先级的配置。

./config/application.yml > ./config/application.yml > ./src/main/resource/config/application.yml > ./src/main/resource/application.yml

  • yaml形式的的环境切换

    server:
      port: 8081
    spring:
      profiles:
        active: dev
    
    ---
    server:
      port: 8082
    spring:
      profiles: dev
    
    ---
    server:
      port: 8083
    spring:
      profiles: test
  • properties形式,需要创建多个properties文件(如application-dev.propertiesapplication-test.properties),然后在以applicaiton.properties命名的文件中指定要激活的环境

    spring.profiles.active=dev

此外,我们还可以通过spring.config.location来改变默认的配置文件位置:

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高

java -jar spring-boot-config.jar --spring.config.location=F:/application.properties

四、自动装配原理

待补充,没怎么听懂

五、SpringMVC

1)静态资源访问

只要将静态资源(css、js、img等)放在/static/public/resources或者INF/resource文件夹下,即可通过当前项目根路径/ + 静态资源名称访问

  • SpringBoot中请求进来时,先去找Controller看能不能处理,不能处理的所有请求均交给静态资源处理器;静态资源也找不到时,报404

  • 改变默认的静态资源路径的方法:

    spring:
      mvc:
        static-path-pattern: /res/**     # 前缀,访问时需在8080后加上该前缀才可访问
    
      resources:
        static-locations: [classpath:/haha/]
  • 静态资源访问时,无需前缀,只要路径对了直接静态资源名即可

  • webjars的访问地址,要按照依赖里面具体的包路径:如http://localhost:8080/webjars/jquery/3.5.1/jquery.js

    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>jquery</artifactId>
        <version>3.5.1</version>
    </dependency>

2)首页和图标订制

  • 在静态资源支持的路径下,放置的index.html即为首页
  • 在静态资源支持的路径下,放置的favicon.ico即为网站的图标

3)thymeleaf 模板引擎

  • Thymeleaf是一张 适用于Web和独立环境 的现代 服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。

  • 📚 基本语法:

  • 🔎 常用的th:*标签及其优先级

4)员工管理系统

六、整合数据库

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

1)整合JDBC

  • 导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
  • 编写yaml配置文件连接数据库

    spring:
      datasource:
        username: root
        password: root
        #?serverTimezone=UTC解决时区的报错
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.jdbc.Driver
  • 查看当前数据源

    SpringBoot默认的数据源为HikariDataSource

    @SpringBootTest
    class SpringbootDataJdbcApplicationTests {
    
        // 注入数据源
        @Autowired
        DataSource dataSource;
    
        @Test
        public void contextLoads() throws SQLException {
            //看一下默认数据源
            System.out.println(dataSource.getClass());
            //获得连接
            Connection connection =   dataSource.getConnection();
            System.out.println(connection);
            //关闭连接
            connection.close();
        }
    }
  • 使用

    package com.kuang.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/jdbc")
    public class JdbcController {
    
        /**
         * Spring Boot 默认提供了数据源,默认提供了 org.springframework.jdbc.core.JdbcTemplate
         * JdbcTemplate 中会自己注入数据源,用于简化 JDBC操作
         * 还能避免一些常见的错误,使用起来也不用再自己来关闭数据库连接
         */
        @Autowired
        JdbcTemplate jdbcTemplate;
    
        //查询employee表中所有数据
        //List 中的1个 Map 对应数据库的 1行数据
        //Map 中的 key 对应数据库的字段名,value 对应数据库的字段值
        @GetMapping("/list")
        public List<Map<String, Object>> userList(){
            String sql = "select * from employee";
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
            return maps;
        }
        
        //新增一个用户
        @GetMapping("/add")
        public String addUser(){
            //插入语句,注意时间问题
            String sql = "insert into employee(last_name, email,gender,department,birth)" +
                    " values ('狂神说','24736743@qq.com',1,101,'"+ new Date().toLocaleString() +"')";
            jdbcTemplate.update(sql);
            //查询
            return "addOk";
        }
    
        //修改用户信息
        @GetMapping("/update/{id}")
        public String updateUser(@PathVariable("id") int id){
            //插入语句
            String sql = "update employee set last_name=?,email=? where id="+id;
            //数据
            Object[] objects = new Object[2];
            objects[0] = "秦疆";
            objects[1] = "24736743@sina.com";
            jdbcTemplate.update(sql,objects);
            //查询
            return "updateOk";
        }
    
        //删除用户
        @GetMapping("/delete/{id}")
        public String delUser(@PathVariable("id") int id){
            //插入语句
            String sql = "delete from employee where id=?";
            jdbcTemplate.update(sql,id);
            //查询
            return "deleteOk";
        }
        
    }

2)整合Druid数据源

  • 添加Druid数据源相关依赖

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.4</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
  • 切换数据源

    如果允许时报错”java.lang.ClassNotFoundException: org.apache.log4j.Priority”,则导入 log4j 依赖即可

    spring:
      datasource:
        username: root
        password: root
        #?serverTimezone=UTC解决时区的报错
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf-8
        driver-class-name: com.mysql.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注入
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  • 自定义数据源配置类DruidConfig.java

    package com.gaowl.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.sql.DataSource;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class DruidConfig {
    
    	// 将 全局配置文件中前缀为 spring.datasource的属性值注入到com.alibaba.druid.pool.DruidDataSource 的同名参数中
        @ConfigurationProperties(prefix = "spring.datasource")
        @Bean
        public DataSource druidDataSource() {
            return new DruidDataSource();
        }
    
        // 内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
        @Bean
        public ServletRegistrationBean statViewServlet() {
            ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    
            // 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet
            // 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
            Map<String, String> initParams = new HashMap<>();
            initParams.put("loginUsername", "admin");   //后台管理界面的登录账号
            initParams.put("loginPassword", "123456");  //后台管理界面的登录密码
            // 为localhost,只有本机可以访问;为空或者为null时,表示允许所有访问
            initParams.put("allow", "");  
            //deny:Druid 后台拒绝谁访问
            //initParams.put("kuangshen", "192.168.1.20");表示禁止此ip访问
            bean.setInitParameters(initParams);
            return bean;
        }
    
        //配置 Druid 监控
        //WebStatFilter:用于配置Web和Druid数据源之间的管理关联监控统计
        @Bean
        public FilterRegistrationBean webStatFilter() {
            FilterRegistrationBean bean = new FilterRegistrationBean();
            bean.setFilter(new WebStatFilter());
    
            //exclusions:设置哪些请求进行过滤排除掉,从而不进行统计
            Map<String, String> initParams = new HashMap<>();
            initParams.put("exclusions", "*.js,*.css,/druid/*,/jdbc/*");
            bean.setInitParameters(initParams);
    
            //"/*" 表示过滤所有请求
            bean.setUrlPatterns(Arrays.asList("/*"));
            return bean;
        }
    }
  • 测试

    @SpringBootTest
    class Springboot04DataApplicationTests {
    
        @Autowired
        DataSource dataSource;
    
        @Test
        void contextLoads() throws SQLException {
            //看一下默认数据源
            System.out.println("当前数据源为:" + dataSource.getClass());
    
            //获得连接
            Connection connection = dataSource.getConnection();
            System.out.println(connection);
    
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
            System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
            System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());
    
            //关闭连接
            connection.close();
        }
    
    }

3)整合Mybatis

  • 导入依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.1</version>
    </dependency>
  • 配置Druid数据源,连接MySQL数据库,并整合mybatis

    spring:
      datasource:
        username: root
        password: root
        #?serverTimezone=UTC解决时区的报错
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf-8
        driver-class-name: com.mysql.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注入
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
    server:
      port: 8081
    
    mybatis:
      type-aliases-package: com.gaowl.pojo
      mapper-locations: classpath:mybatis/mapper/*.xml
  • 创建实体类、接口、接口实现

    和mybatis的没差

  • 创建控制器类,测试

    package com.gaowl.controller;
    
    import com.gaowl.dao.UserMapper;
    import com.gaowl.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    
    @RestController
    public class UserController {
        @Autowired
        UserMapper userMapper;
    
        @RequestMapping("/users")
        public List<User> getUsers(){
            List<User> users = userMapper.getUsers();
            for (User user : users) {
                System.out.println(user);
            }
            return users;
        }
    
        @GetMapping("/getUserById/{id}")
        public User getUserById(@PathVariable("id") Integer id){
            return userMapper.getUserById(id);
        }
    
    }

七、Spring Security

官网介绍:Spring Security是一个功能强大且高度可定制的身份验证(Authentication)和访问控制(授权,Authorization)框架,其侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求。

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

1)导入相关依赖项

  • spring-boot-starter-security
  • spring-boot-starter-thymeleaf
  • thymeleaf-extras-springsecurity5
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- 模板引擎 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- 基于SpringSecurity对模板引擎进行增强 -->
<dependency>
  <groupId>org.thymeleaf.extras</groupId>
  <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

2)自定义安全策略

  • 在config文件夹下创建SecurityConfig类自定义安全策略
    • 通过注解@EnableWebSecurity开启安全模式
    • 其继承自WebSecurityConfigurerAdapter类,并重写configure方法:
      • 定制请求的授权规则
package com.gaowl.config;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 重写Http的安全规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 定制请求的授权规则,此时写完了大家都木得权限,因此,需要重写认证规则,设置用户的权限
        http.authorizeRequests().antMatchers("/").permitAll()   // 首页所有人可以访问
               .antMatchers("/level1/**").hasRole("vip1")   // level1下的界面,只有拥有vip1权限才可访问
               .antMatchers("/level2/**").hasRole("vip2")
               .antMatchers("/level3/**").hasRole("vip3");
	   
        // 开启登录功能,控制器处理"/toLogin"请求,来到自定义的登陆页
        // 登陆页传递参数username和password
        // 登陆页中的<form th:action="@{/login}" method="post">表明提交请求为login,因此加一个loginProcessingUrl鉴权
        // 若其action为toLogin,后面的loginProcessingUrl可以删掉。
        http.formLogin().usernameParameter("username").passwordParameter("password")
                .loginPage("/toLogin").loginProcessingUrl("/login");   
        
		
        // 关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
        http.csrf().disable();  
        
        // 开启账户注销功能,账号成功注销后跳转到首页
        http.logout().logoutSuccessUrl("/");    
        
        // 开始记住用户名及密码功能,cookie
        http.rememberMe().rememberMeParameter("remember");

    }

    // 重写定义认证规则:
    // 为不同的用户设置权限,且对密码进行bcrypt加密
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在内存中定义,也可以在jdbc中去拿....
        // Spring security 5.0中之后,将前端传过来的密码进行某种方式加密,spring security 官方推荐的是使用bcrypt加密方式。

        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("ks").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
                .and()
                .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
                .and()
                .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
    }
}

3)自定义主页等

  • 控制器代码:
package com.gaowl.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

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

    @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;
    }
}
  • 视图代码:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>首页</title>
    <!--semantic-ui-->
    <link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
    <link th:href="@{/qinjiang/css/qinstyle.css}" rel="stylesheet">
</head>
<body>

<!--主容器-->
<div class="ui container">

    <div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
        <div class="ui secondary menu">
            <a class="item"  th:href="@{/index}">首页</a>

            <!--登录注销-->
            <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">
                        <i class="address card icon"></i>
                        用户名:<span sec:authentication="principal.username"></span>
                        &nbsp;&nbsp;&nbsp;&nbsp;<i class="address card icon"></i>
                        权限:<span sec:authentication="principal.authorities"></span>
                    </a>
                </div>

                <!-- 如果已登录,显示注销按钮 -->
                <div sec:authorize="isAuthenticated()">
                    <a class="item" th:href="@{/logout}">
                        <i class="address card icon"></i> 注销
                    </a>
                </div>

            </div>
        </div>
    </div>

    <div class="ui segment" style="text-align: center">
        <h3>Spring Security Study by 秦疆</h3>
    </div>

    <div>
        <br>
        <div class="ui three column stackable grid">
            <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 class="column" sec:authorize="hasRole('vip2')">
                <div class="ui raised segment">
                    <div class="ui">
                        <div class="content">
                            <h5 class="content">Level 2</h5>
                            <hr>
                            <div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
                            <div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
                            <div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
                        </div>
                    </div>
                </div>
            </div>

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

        </div>
    </div>
    
</div>


<script th:src="@{/qinjiang/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/qinjiang/js/semantic.min.js}"></script>

</body>
</html>

八、Shiro

1)概述

Shiro也是一种高效的安全框架,其主要的功能有:认证、鉴权、session管理、编码加密、缓存等等

2)HelloWorld

  • 获取当前用户对象:Subject currentUser = SecurityUtils.getSubject();
  • 获取当前用户对象的session:Session session = currentUser.getSession();
  • 是否已认证:currentUser.isAuthenticated()
  • 用户登陆:currentUser.login(token)
  • currentUser.getPrincipal()
  • 是否拥有角色:currentUser.hasRole(“schwartz”)
  • 是否拥有权限:currentUser.isPermitted(“lightsaber:wield”)、currentUser.isPermitted(“winnebago:drive:eagle5”)
  • 用户登出:currentUser.logout();
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

    public static void main(String[] args) {

        // 创建默认安全管理器,然后将配置文件shiro.ini加载到该安全管理器中
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
        securityManager.setRealm(iniRealm);

        SecurityUtils.setSecurityManager(securityManager);

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

        // 通过当前用户拿到shiro的session,存值、取值
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("取值正确! [" + value + "]");
        }

        // 如果当前用户未被认证
        if (!currentUser.isAuthenticated()) {
            // 设置其token(令牌)为true
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);

            try {
                currentUser.login(token);  // 通过该token登陆当前用户
            } catch (UnknownAccountException uae) {
                log.info("不存在该用户:" + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("用户 " + token.getPrincipal() + " 的密码不正确");
            } catch (LockedAccountException lae) {
                log.info("用户 " + token.getPrincipal() + " 已被锁定,请寻找管理员解锁  " +
                        "Please contact your administrator to unlock it.");
            }
            catch (AuthenticationException ae) {
                //unexpected condition?  error?
            }
        }


        log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功.");

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

        // 测试权限(粗粒度)
        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.");
        }

        // 测试权限(细粒度)
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " );
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        currentUser.logout();
        System.exit(0);
    }
}

3)和SpringBoot整合

  • 导入相关依赖

    <!-- Subject 用户、SecurityManager 管理所有用户、Realm 连接数据 -->
    
    <!-- 安全框架 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.1</version>
    </dependency>
    <!-- 模板引擎 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
    </dependency>
    
    <!-- 数据库连接驱动 mysql-connector-java -->
    <!-- mybatis驱动 mybatis-spring-boot-starter -->
    <!-- Druid数据源 -->
    <!-- log4j日志 -->
  • 编写Shiro配置

    package com.gaowl.config;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    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.LinkedHashMap;
    import java.util.Map;
    
    // 声明为配置类,替代原先的xml配置文件
    @Configuration
    public class ShiroConfig {
    
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            bean.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/*","authc");
    
            bean.setFilterChainDefinitionMap(filterMap);
    
            // 拦截后跳转到的界面
            bean.setLoginUrl("/toLogin");
            bean.setUnauthorizedUrl("/noauth");
    
            return bean;
        }
    
        // 创建默认安全管理器,并装配Realm对象
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
    
        // 创建Realm对象:其调用我们自定义的UserRealm类
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    
        // 整合ShiroDialect以整合Shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    
    }

九、Swagger

1)概述

  • 可是实现==Api文档 与 Api定义实时更新==

  • 可以直接运行,在线运行api接口

  • 支持多种语言

2)和SpringBoot集成

  • 导入相关依赖

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>

    当swagger版本大于3.0时,仅导入springfox-boot-starter,然后在浏览器中访问http://localhost:8080/swagger-ui/即可拥有文档功能;

  • 编写Swagger配置文件

    package com.gaowl.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.service.Contact;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    
    import java.util.ArrayList;
    
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
    
        // 配置Swagger的Docket的bean实例
        @Bean
        public Docket docket(){
            return new Docket(DocumentationType.SWAGGER_2).apiInfo( MyApiInfo());
        }
    
        // 自定义Swagger信息
        private ApiInfo MyApiInfo(){
            // 作者信息
            Contact contact = new Contact("gaowl", "https://www.jngwl.top/", "462549693@qq.com");
    
            return new ApiInfo("Api Documentation by gaowl",
                    "集中一点,登峰造极",
                    "1.0",
                    "https://www.jngwl.top/",
                    contact,
                    "Apache 2.0",
                    "http://www.apache.org/licenses/LICENSE-2.0",
                    new ArrayList());
        }
    }
    

    此时,便可以通过http://localhost:8080/swagger-ui.html#/查看文档了

  • 自定义Docket

    • 扫描哪些端口:RequestHandlerSelectors
    • 如何使swagger在开发环境时使用,在发布时禁用:enable
    • 通过groupName进行分组,然后创建对个Docket方法
@Bean
public Docket docket(Environment environment){

    // 获取项目的环境
    Profiles profile = Profiles.of("dev","test");  // 当为开发和测试环境时,才开启swagger
    boolean flag = environment.acceptsProfiles(profile);

    return new Docket(DocumentationType.SWAGGER_2).groupName("AAAAA")
            .apiInfo(myApiInfo()).enable(flag)
            .select()
            // RequestHandlerSelectors 配置要扫描接口的方式
            // basePackage 指定要扫描哪个包下的接口
            // any 扫描全部;none 都不扫描;
            // withClassAnnotation  扫描带有某种注解的方法(需传入GetMapping.class这种注解的反射对象)
            .apis(RequestHandlerSelectors.basePackage("com.gaowl.controller"))
            .build();
}
  • 一些注解

    待补充

十、任务

假设我们现在有一个需求:每半个小时向某个邮箱发送一封邮件,此时便涉及到两个问题:如何发送邮件、如何定时执行某项任务;此时,前台界面在邮件发送时是静止不动的,还是前台先响应,然后后台自己去发送邮件。因此,异步任务、定时任务、邮件任务应运而生。

1)异步任务

  • 在SpringBoot引导类加上@EnableAsync 注解
  • 在需要异步执行的方法的加上@Async 注解

2)邮件任务

  • 引入邮件依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
  • 基本使用

    @Autowired
    JavaMailSenderImpl mailSender;
    
    @Test
    void contextLoads() throws MessagingException {
       /* SimpleMailMessage message = new SimpleMailMessage();
        message.setSubject("测试邮件功能");
        message.setText("Hello, Are you ok?");
        message.setTo("gaowl@stu.jiangnan.edu.cn");
        message.setFrom("462549693@qq.com");
    
        mailSender.send(message);*/
    
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    
        helper.setSubject("假设这里是主题");
        helper.setText("<b style='color:red'>有无格式啊</b>",true);
        helper.addAttachment("1.jpg",new File("C:\\Users\\GWL\\Desktop\\springboot-10-tasks\\src\\main\\resources\\1.jpg"));
        helper.setTo("gaowl@stu.jiangnan.edu.cn");
        helper.setFrom("462549693@qq.com");
    
        mailSender.send(mimeMessage);
    }

3)定时执行任务

  • 在SpringBoot引导类上,加上@EnableScheduling 注解

  • 在要定时执行的方法上,加上@Scheduled(cron = "0 56 14 * * *")注解

  • cron表达式:

4)练习

在网页上设置一个邮件按钮,当开启按钮时,定时发送邮件

  • 视图层

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
    
    <form th:action="@{/autoMail}" method="post" >
    
        <div>
            自动发送邮件 <input type="checkbox" name="autoMail" value="on">   开启
        </div>
    
        <div>
            <input type="submit" value="保存">
        </div>
    
        <p style="color: red" th:text="${msg}" ></p>
    </form>
    
    </body>
    </html>
  • 控制层

    package com.gaowl.controller;
    
    import com.gaowl.service.AsyncService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.mail.MessagingException;
    
    
    @Controller
    public class AsyncController {
    
        AsyncService asyncService;
        @Autowired
        public void setAsyncService(AsyncService asyncService) {
            this.asyncService = asyncService;
        }
    
    
        @RequestMapping("/")
        public String index(){
            return "index";
        }
    
        @RequestMapping("/autoMail")
        public String autoMail(@RequestParam("autoMail") String autoMail, Model model) throws MessagingException {
            if(autoMail == "on"){
                model.addAttribute("msg","开启成功...");
                asyncService.autoMail();
            }
    
            return "index";
        }
    
    }
  • 服务层

    package com.gaowl.service;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.mail.javamail.JavaMailSenderImpl;
    import org.springframework.mail.javamail.MimeMessageHelper;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;
    import javax.mail.MessagingException;
    import javax.mail.internet.MimeMessage;
    
    @Service  // 将该类标注为service类,并注入到spring容器中
    public class AsyncService {
    
        @Autowired
        JavaMailSenderImpl mailSender;
    
        @Async
        @Scheduled(cron = "0/10 * * * * *")
        public void autoMail() throws MessagingException {
    
             MimeMessage mimeMessage = mailSender.createMimeMessage();
             MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
    
             helper.setSubject("假设这里是主题");
             helper.setText("<b style='color:red'>有无格式啊</b>",true);
             helper.setTo("gaowl@stu.jiangnan.edu.cn");
             helper.setFrom("462549693@qq.com");
    
             mailSender.send(mimeMessage);
        }
    
    }
  • 改进处:ajax、邮件发送的日志

  • 一看就会,一练就废。太tm菜了

十一、分布式

1)概述

分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。

  • 分布式和集群的区别:待补充
  • RPC,远程过程调用Remote Procedure Call,其允许程序调用另一个地址空间的过程或者函数。
  • 推荐文章:https://www.jianshu.com/p/2accc2840a1b
  • RPC的两个核心:通信以及序列化

2)Dubbo + ZooKeeper

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

服务提供商(Provider)在注册中心(Registy)注册自己的服务,然后服务消费者(Consumer)需要使用该服务时,先向注册中心订阅该服务,注册中心批准后,用户才可以调用该服务;期间,监控中心(Monitor)会对消费者和提供商均进行监控,监控调用时间和调用次数

🟡 常采用zookeeper作为注册中心,其在Windows下的安装使用流程如下:

  • 清华源下载对应的文件apache-zookeeper-3.5.8-bin.tar.gz
  • 解压缩,到/conf文件夹下根据zoo_sample.cfg生成zoo.cfg
  • 然后,到bin文件夹下,在zkServer.cmd中加入pause,双击即可启动
  • 通过双击zkCli.cmd测试是否安装成功

🟡 Dubbo本身并不是一个服务软件。它其实就是一个jar包,能够帮你的java程序连接到zookeeper,并利用zookeeper消费、提供服务。为了让用户更好的管理监控众多的dubbo服务,可以安装dubbo-admin进行可视化

🟡 SpringBoot + Dubbo + zookeeper

(1)启动注册中心zookeeper

(2)配置服务提供商

  • 新建项目provider-server,导入依赖

    <dependencies>
        <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>
    
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>com.github.sgroschupf</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>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>
    
    </dependencies>
  • 新建 服务及其实现类

    package com.gaowl.service;
    
    public interface TicketService {
        public String getTicket();
    }
    package com.gaowl.service;
    
    import org.apache.dubbo.config.annotation.Service;
    import org.springframework.stereotype.Component;
    
    @Service   // 发布服务(此处导入的是dubbo的service注解)
    @Component  // 注册到Spring中
    public class TicketServiceImpl implements TicketService {
        @Override
        public String getTicket() {
            return "垃圾IDEA";
        }
    }
  • 配置spring的application.properties

    #当前应用名字
    dubbo.application.name=provider-server
    #注册中心地址
    dubbo.registry.address=zookeeper://127.0.0.1:2181
    #扫描指定包下服务
    dubbo.scan.base-packages=com.gaowl.service
    
    server.port=8081
  • 运行SpringBoot的引导类,向zookeeper注册服务

(3)配置用户端

  • 新建项目consumer-server,导入和provider-server相同的依赖

  • 编写用户服务

    package com.gaowl.service;
    
    import org.apache.dubbo.config.annotation.Reference;
    import org.springframework.stereotype.Service;
    
    @Service //注入到容器中
    public class UserService {
    
        @Reference //远程引用指定的服务,他会按照全类名进行匹配,看谁给注册中心注册了这个全类名
        TicketService ticketService;  
    
        public void bugTicket(){
            String ticket = ticketService.getTicket();
            System.out.println("在注册中心买到"+ticket);
        }
    
    }
  • 将服务提供商的的接口文件TicketService.java拷贝过来,放在相同的路径

  • 开始测试

    package com.gaowl;
    
    import com.gaowl.service.UserService;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class ConsumerServerApplicationTests {
    
        @Autowired
        UserService userService;
    
        @Test
        void contextLoads() {
            userService.bugTicket();
        }
    
    }

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!