SpringBoot基础 Spring

2020-07-04 约 8831 字 阅读时长18 分钟

SpringBoot

SpringBoot入门

简介

SpringBoot简化了所有的spring配置文件,使开发,部署变得更简单了

特性:

  • 能够快速创建基于Spring的应用程序
  • 能够直接使用java main 方法启动内嵌的tomcat服务器运行jar包,不需要部署war包文件
  • 提供约定的 starter POM 来简化maven配置,让maven的配置变得更简单
  • 自动化配置,更具项目的maven依赖配置,Springboot自动配置spring,springmvc等
  • 提供了程序的健康检查等功能
  • 基本可以完全不适用XML配置文件,采用注解配置

四大核心:

  • 自动配置
  • 起步依赖
  • Actuator(健康检测)
  • 命令行界面

Hello SpringBoot

快速搭建

  1. 通过spring官网快速生成,并注入spring-web依赖(自动配置好web.xml),然后下载demo包,并导入ide中
  2. 通过idea创建SpringBoot项目(idea集成了spring官网的自动生成)

pom.xml

xml
 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4	<modelVersion>4.0.0</modelVersion>
 5    <!-- 父依赖,后续SpringBoot依赖可以自动继承该依赖版本 -->
 6	<parent>
 7		<groupId>org.springframework.boot</groupId>
 8		<artifactId>spring-boot-starter-parent</artifactId>
 9		<version>2.5.0</version>
10		<relativePath/> <!-- lookup parent from repository -->
11	</parent>
12	<groupId>com.lei</groupId>
13	<artifactId>demo</artifactId>
14	<version>0.0.1-SNAPSHOT</version>
15	<name>demo</name>
16	<description>Demo project for Spring Boot</description>
17	<properties>
18		<java.version>11</java.version>
19	</properties>
20	<dependencies>
21        <!-- SpringBoot 起步依赖-->
22        <!-- SpringBootWeb 配置好了web.xml,并且默认使用tomcat作用web容器 -->
23		<dependency>
24			<groupId>org.springframework.boot</groupId>
25			<artifactId>spring-boot-starter-web</artifactId>
26		</dependency>
27        <!-- SpringBoot 测试起步依赖-->
28		<!-- SpringBoot 单元测试 -->
29		<dependency>
30			<groupId>org.springframework.boot</groupId>
31			<artifactId>spring-boot-starter-test</artifactId>
32			<scope>test</scope>
33		</dependency>
34	</dependencies>
35
36	<build>
37		<plugins>
38            <!-- SpringBoot 打包、编译插件 -->
39			<plugin>
40				<groupId>org.springframework.boot</groupId>
41				<artifactId>spring-boot-maven-plugin</artifactId>
42			</plugin>
43		</plugins>
44	</build>
45
46</project>

Application类

java
1//SpringBoot项目启动入口类
2@SpringBootApplication	//SpringBoot核心注解,主要用于开启SpringBoot自动配置
3public class DemoApplication {
4	public static void main(String[] args) {
5		SpringApplication.run(DemoApplication.class, args);
6	}
7}

后续步骤

  1. 在Appliction配置类的同级目录下创建相关的 dao包,service包,controller包等
  2. 创建hello controller
  3. 打包(jar)
  4. java -jar xxx.jar 运行jar包
  5. 浏览器访问 localhost:8080/hello

SpringBoot源码分析

准备工作

  1. 安装 JDK1.8+
  2. 安装并配置Gradle
  3. 使用新版IDEA,并安装代码插件 Statistic 和翻译插件 ECTranslation
  4. github上下载Springboot源码,并解压
  5. 阅读源码目录下的 README.adoc,里面说明了如何进行构建源码
  6. 进入DOS执行命令进行构建:./gradlew publishToMavenLocal,可能出现网络错误;git 错误解决如下:
    1. 该SpringBoot目录必须纳入到git管理中
    2. git init
    3. git add ./*,这里会报文件名太长的错误,通过下面命令:git config core.longpaths true
    4. git commit -m “1”
    5. 然后再进行源码构建
  7. 构建成功后 IDEA 导入;IDEA导入,选择源码目录下的build.gradle作为工程导入
  8. 修改编码 UTF-8、配置自动注解

验证源码环境

  1. 创建gradle项目,并在依赖文件中,通过源码项目导入父依赖以及所需依赖

    java
    1dependencies {
    2    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    3    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    4    //指定parent
    5    implementation(platform(project(":spring-boot-project:spring-boot-parent")))
    6    //添加依赖
    7    implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))
    8    //api(group:"org.apache.tomcat.embed",name:"tomcat-embed-jasper")
    9}
  2. 创建启动类,启动测试,因为是普通java程序,运行结束后退出

    java
     1@SpringBootApplication
     2public class Application implements CommandLineRunner {
     3	public static void main(String[] args) {
     4		SpringApplication.run(Application.class);
     5	}
     6
     7	@Override
     8	public void run(String... args) throws Exception {
     9		System.out.println("SpringBoot start");
    10	}
    11}

SpringBoot启动原理

主要分为三部分

  1. 第一部分进行SpringbootApplication的初始化模块,配置一些基本的环境变量、资源、构造器、监视器
  2. 第二部分实现了应用具体的启动方案,包括启动流程监听模块、加载配置环境模块、及核心的创建上下文环境模块
  3. 第三部分是自动化配置模块,该模块是SpringBoot自动化配置的核心

宏观步骤(概括为18个小步骤)

  1. 创建并启动计时监控类 StopWatch
  2. 初始化应用上下文和异常报告集合(ConfigurableApplicationContext和Collection<SpringBootExceptionReporter>)
  3. 设置系统属性 configureHeadlessProperty()
  4. 创建Spring运行的监听器并启动
  5. 初始化默认应用参数类
  6. 准备Spring环境
  7. 创建beanner打印类
  8. 创建应用Spring上下文(spring容器)
  9. 准备异常报告器
  10. 准备应用上下文(spring容器)
  11. 刷新上下文(spring容器)
  12. 应用上下文刷新后置处理(spring容器)
  13. 停止计时监控类
  14. 输出日志信息
  15. 发布应用上下文启动完成事件
  16. 执行所有的Runner运行期(如果实现了CommandLineRunner接口)
  17. 发布应用上下文就绪事件
  18. 完成返回容器对象

总结:

  1. new一个SpringApplication对象,使用一种类似于SPI机制的技术加载ApplicationContextInitializer、ApplicationListener 接口实例

  2. 调用SpringApplication.run()方法,run方法中大概有 18 个小步骤

  3. 调用createApplicationContext()方法创建Spring IOC上下文对象,创建上下文对象同时会注册spring的核心组件类

    ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostPrecessor等

  4. 调用refreshContext() 方法启动 Spring 容器(12步)和内置 Servlet 容器

img

自动装配

  1. @import:spring注解,作用为注入一个指定的bean
  2. SpringBoot—-> start起步依赖(空的) —-> 整合依赖(整合相关包,然后暴露出去)

核心配置文件

一个SpringBoot项目只有一个核心配置文件,有多种存在形式,取一种就可以

  1. application.properties

    properties
    1#设置内嵌Tomcat端口号
    2server.port=8080
    3#设置项目默认上下文根(localhost:8080/SpringBoot/hello)
    4server.servlet.context-path=/SpringBoot
  2. application.yaml

    yaml
    1server:
    2  port: 8081
    3  servlet:
    4    context-path: /SpringBoot

多环境配置文件

  • application.properties

    • 创建多个配置文件,文件名格式为 application-xxx.properties

    • 在application.properties中指定需要使用的配置文件

      properties
      1#指定需要使用的配置文件
      2spring.profiles.active=xxx
  • application.yaml

    • 创建多个配置文件,文件名格式为 application-xxx.yaml

    • 在application.yaml中指定需要使用的配置文件

      yaml
      1spring:
      2  profiles:
      3    active: xxx

获取自定义配置

  • 获取自定义配置的值,使用@Value(${…})

    properties
    1school.name=tanglei
    2school.address=wc
    java
    1@Value("${school.name}")
    2private String name;
    3@Value("${school.address}")
    4private String address;
  • 将自定义配置映射到一个对象

    • application.properties配置文件

      properties
      1school.name=tanglei
      2school.address=wc
    • 创建一个类,包含字段name、address

      java
      1//注册为spring组件
      2@Component
      3//配置类,获取application的自定义配置,prefix为自定义配置的前缀
      4@ConfigurationProperties(prefix = "school")
      5public class School {
      6    private String name;
      7    private String address;
      8}
  • 通过Environment对象读取;(spring ioc直接给我们创建了该对象)

    • application.properties配置文件

      properties
      1school.name=tanglei
      2school.address=wc
    • 注入Environment对象bean,通过该对象读取

      java
       1@SpringBootApplication
       2public class Application implements CommandLineRunner {
       3
       4    @Autowired
       5    private Environment environment;
       6
       7
       8    public static void main(String[] args) {
       9        SpringApplication.run(Application.class);
      10    }
      11
      12    @Override
      13    public void run(String... args) throws Exception {
      14        //读取自定义配置
      15        System.out.println(environment.getProperty("school.name"));
      16        System.out.println(environment.getProperty("school.address"));
      17    }
      18}

常用配置

properties
 1#指定需要使用的配置文件(多环境配置文件)
 2#多环境配置文件的命名格式:application-dev.properties
 3spring.profiles.active=xxx
 4
 5#配置数据库
 6spring.datasource.type=com.alibaba.druid.pool.DruidDataSource	#指定数据源
 7spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 8#链接地址、 useSSL 安全套接字协议,mysql的版本是5.7之后的版本必须要加上useSSL=false
 9#connectTimeout 链接超时、socketTimeout读写超时、autoReconnect当数据库连接异常中断时,是否自动重新连接
10#serverTimezone 设置时区
11#zeroDateTimeBehavior JAVA连接MySQL数据库,在操作值为0的timestamp类型时不能正确的处理;【exception:默认值,抛出异常、convertToNull:将日期转换成NULL值、round:替换成最近的日期即0001-01-01】
12#rewriteBatchedStatements 默认情况下会无视executeBatch(),设置为true,驱动才会帮你批量执行SQL
13#characterEncoding 所处理字符的解码和编码的格,如果项目和数据库一致可不设
14#allowMultiQueries 可以在sql语句后携带分号,实现多语句执行
15spring.datasource.url=jdbc:mysql://192.168.10.129:3306/crm_manage?useSSL=false&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&serverTimezone=GMT%2B8&zeroDateTimeBehavior=ROUND&rewriteBatchedStatements=true&characterEncoding=utf8&allowMultiQueries=true
16spring.datasource.username=crm_manage
17spring.datasource.password=JCRMp3LHkrSZ5y6c
18
19#配置redis
20spring.redis.host=192.168.10.129
21spring.redis.port=6379
22spring.redis.password=123456
23
24#配置restful返回json字符串时的时间格式和时区
25spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
26spring.jackson.time-zone=GMT+8
27
28#mybatis
29mybatis.mapper-locations=classpath:mapper/*.xml		#自定义mapper.xml文件所在位置
30mybatis.type-aliases-package=com.lei.model		#定义别名
31
32#jsp视图解析器
33spring.mvc.view.prefix=/
34spring.mvc.view.suffix=.jsp
35
36#启动类配置
37spring.main.banner-mode=off			#关闭logo
38spring.main.sources=com.lei.Application			#指定配置类,启动类可以不传入启动类的
39classspring.main.allow-bean-definition-overriding=true		#允许 bean 定义覆盖
40spring.main.lazy-initialization=true	#延时初始化bean,获取时才会创建;可以在指定bean上添加注解 @Lazy(false)来不延时初始化
41
42#自动化配置
43spring.autoconfigure.exclude=配置类	#排除指定的自动化配置,也可以通过 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) 排除指定自动化配置
44
45#jasypt
46jasypt.encryptor.password=sdjhauidasdw    #加密解密的秘钥key
47jasypt.encryptor.property.prefix=EVC(		#识别密文内容的开始
48jasypt.encryptor.property.suffix=)			#识别密文内容的结束
49
50#线程池相关
51spring.task.execution.pool.core-size=5
52spring.task.execution.pool.max-size=100
53spring.task.execution.thread-name-prefix=th-
54spring.task.execution.pool.queue-capacity=999
55
56#ssl配置
57
58server.port=8088   #ssh端口
59server.ssl.key-store=classpath:tomcat.keystore   #证书
60server.ssl.key-store-password=      #证书密码
61server.ssl.trust-store-type=jks     #证书证书类型 jks/PKCS12
yaml
 1#logback配置
 2logging:
 3  level:
 4    root: info
 5    com.lei.mapper: debug
 6  file:
 7    path: /
 8    name: "log.log"
 9  charset:
10    console: UTF-8
11    file: UTF-8
12  pattern:
13    dateformat: "MM-dd HH:mm:ss"

启动时区配置

  1. 启动类配置

    java
     1@SpringBootApplication
     2public class MbsGenericApplication {
     3        @PostConstruct
     4        void started() {
     5            TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
     6        }
     7        public static void main(String[] args) {
     8        SpringApplication.run(MbsGenericApplication.class, args);
     9    }
    10}
  2. JVM启动参数配置

    tex
    1-Duser.timezone=GMT+8

Springboot参数接收

注意看请求头中的 content-type 属性,他告诉后台用什么方式解析参数

@PathVariable

restful风格

java
1@GetMapping({"/test/{id}","/test"})
2public Object test(@PathVariable(value = "id",required = false) String id){
3    return "id:"+id;
4}

image-20220525134354485

@PathVariable 的 required 属性默认为true,如果设置为false表示该参数可以为空,此时访问url时可以不携带参数;注意:mapping需要设置不带参数的路径,否则会抛404

@PathParam

从请求地址中截取参数

java
1@GetMapping("/test")
2public Object test(@PathParam(value = "id") String id, @PathParam(value = "name") String name) {
3    return "id:" + id+name;
4}

image-20220525135642963

@PathParam注解没有 required属性,可以不传参数

@RequestParam

被标注的参数的值来源于 request.getParameterrequest.getParameterValues

java
 1//获取参数
 2@GetMapping("/test")
 3public Object test(@RequestParam String id) {
 4    return "id:" + id;
 5}
 6
 7//处理上传文件
 8@GetMapping("/test")
 9public Object test(@RequestParam("file") MultipartFile file) {
10    return "id:" + map;
11}
  • 获取url后拼接参数

    image-20220525140206382

  • 获取body中通过 multipart/form-data 发送的参数

    image-20220525140332362

@RequestParam 注解存在required属性,默认为true,当后台未接收到参数时(null)会抛错;

@RequestParam 可以处理 multipart/form-data 中上传的文件

@RequestPart

@RequestPart与@RequestParam比较相似;它不能解析 url 上的参数

  1. @RequestPart这个注解用在multipart/form-data表单提交请求的方法上
  2. 支持的请求方法的方式MultipartFile,属于Spring的MultipartResolver类。这个请求是通过http协议传输的
  3. @RequestParam也同样支持multipart/form-data请求
  4. 他们最大的不同是,当请求方法的请求参数类型不再是String类型的时候
  5. @RequestParam适用于name-valueString类型的请求域,@RequestPart适用于复杂的请求域(像JSON,XML)

@RequestBody

@RequestBody 可以接受 application/json、application/xml、application/javascript、text 等

java
1//会自动解析文件,并赋值到map之中
2@RequestMapping("/test")
3public Object test(@RequestBody Map map) {
4    return "id:" + map;
5}

可以自动将参数解析为 Entity

无注解

  • 可以处理 multipart/form-data 、 url 地址后参数
  • 可以将参数自动解析为 Entity (无法映射为map)

dubbo集成ssm

接口工程:存放实体bean和业务接口

服务提供者:

  • 它是一个SpringBoot框架web项目,集成MyBatis、Redis
  • 添加依赖:mybatis依赖、mysql驱动、dubbo依赖、zookeeper依赖、redis依赖、接口工程
  • 配置SpringBoot核心配置文件
    • 配置连接数据库
    • 配置连接redis
    • 配置dubbo

服务消费者:

  • 它是一个SpringBoot框架的web项目,集成JSP、dubbo
  • 添加依赖dubbo依赖、zookeeper依赖、解析jsp的依赖、接口工程
  • 配置SpringBoot核心配置文件
    • 配置jsp视图解析器

服务提供者

  1. 相关依赖 pom.xml

    xml
     1<dependencies>
     2    <!-- SpringBoot起步依赖 -->
     3    <dependency>
     4        <groupId>org.springframework.boot</groupId>
     5        <artifactId>spring-boot-starter</artifactId>
     6    </dependency>
     7    <!--SpringBoot dubbo整合起步依赖-->
     8    <dependency>
     9        <groupId>org.apache.dubbo</groupId>
    10        <artifactId>dubbo-spring-boot-starter</artifactId>
    11        <version>2.7.8</version>
    12        <exclusions>
    13            <exclusion>
    14                <groupId>org.slf4j</groupId>
    15                <artifactId>slf4j-log4j12</artifactId>
    16            </exclusion>
    17        </exclusions>
    18    </dependency>
    19    <!--dubbo-->
    20    <dependency>
    21        <groupId>org.apache.dubbo</groupId>
    22        <artifactId>dubbo</artifactId>
    23        <version>2.7.8</version>
    24    </dependency>
    25    <!--zookeeper客户端-->
    26    <dependency>
    27        <groupId>org.apache.curator</groupId>
    28        <artifactId>curator-framework</artifactId>
    29        <version>4.2.0</version>
    30    </dependency>
    31    <dependency>
    32        <groupId>org.apache.curator</groupId>
    33        <artifactId>curator-recipes</artifactId>
    34        <version>4.2.0</version>
    35    </dependency>
    36    <!--SpringBoot mybatis起步依赖-->
    37    <dependency>
    38        <groupId>org.mybatis.spring.boot</groupId>
    39        <artifactId>mybatis-spring-boot-starter</artifactId>
    40        <version>2.2.0</version>
    41    </dependency>
    42    <!--mysql驱动-->
    43    <dependency>
    44        <groupId>mysql</groupId>
    45        <artifactId>mysql-connector-java</artifactId>
    46    </dependency>
    47    <!--SpringBoot redis起步依赖-->
    48    <dependency>
    49        <groupId>org.springframework.boot</groupId>
    50        <artifactId>spring-boot-starter-data-redis</artifactId>
    51    </dependency>
    52    <!--接口工程-->
    53    <dependency>
    54        <groupId>org.example</groupId>
    55        <artifactId>api</artifactId>
    56        <version>1.0-SNAPSHOT</version>
    57    </dependency>
    58</dependencies>
  2. SpringBoot核心配置文件

    properties
     1#mysql配置
     2spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
     3spring.datasource.url=jdbc:mysql://192.168.10.129:3306/jdbc_learn?useSSL=true&useUnicode=true&characterEncoding=utf8
     4spring.datasource.username=jdbc_learn
     5spring.datasource.password=tWDp8b2xDFZpzF5N
     6#mapper文件位置
     7mybatis.mapper-locations=classpath:mapper/*.xml
     8#dubbo相关配置
     9dubbo.application.name=provider
    10dubbo.registry.address=zookeeper://192.168.10.130
    11dubbo.registry.port=2181
    12dubbo.protocol.name=dubbo
    13dubbo.protocol.port=20880
    14dubbo.scan.base-packages=com.lei.service.impl
    15#redis配置
    16spring.redis.host=192.168.10.129
    17spring.redis.port=6379
  3. 编写接口服务方法的实现

    java
     1//暴露服务
     2@DubboService
     3public class StudentServiceImpl implements StudentService {
     4//注入mapper
     5    @Autowired
     6    private StudentsMapper studentsMapper;
     7//注入redis操作类
     8    @Autowired
     9    private RedisTemplate<Object,Object> redisTemplate;
    10
    11    @Override
    12    public int getStudentCount() {
    13        //redis查询,有返回,无进mysql查询结果并放入redis
    14        Integer total= (Integer) redisTemplate.opsForValue().get("total");
    15        if (total==null){
    16            total=studentsMapper.queryCount();
    17            redisTemplate.opsForValue().set("total",total,30, TimeUnit.SECONDS);
    18        }
    19        return total;
    20    }
    21
    22    @Override
    23    public Students queryStudentById(long id) {
    24        return studentsMapper.selectByPrimaryKey(id);
    25    }
    26}

服务消费者

  1. 添加依赖 pom.xml

    xml
     1<dependencies>
     2    <dependency>
     3        <groupId>org.springframework.boot</groupId>
     4        <artifactId>spring-boot-starter-web</artifactId>
     5    </dependency>
     6
     7    <dependency>
     8        <groupId>org.springframework.boot</groupId>
     9        <artifactId>spring-boot-starter-test</artifactId>
    10        <scope>test</scope>
    11    </dependency>
    12    <dependency>
    13        <groupId>org.apache.dubbo</groupId>
    14        <artifactId>dubbo-spring-boot-starter</artifactId>
    15        <version>2.7.8</version>
    16        <exclusions>
    17            <exclusion>
    18                <groupId>org.slf4j</groupId>
    19                <artifactId>slf4j-log4j12</artifactId>
    20            </exclusion>
    21        </exclusions>
    22    </dependency>
    23    <dependency>
    24        <groupId>org.apache.dubbo</groupId>
    25        <artifactId>dubbo</artifactId>
    26        <version>2.7.8</version>
    27    </dependency>
    28    <dependency>
    29        <groupId>org.apache.curator</groupId>
    30        <artifactId>curator-framework</artifactId>
    31        <version>4.2.0</version>
    32    </dependency>
    33    <dependency>
    34        <groupId>org.apache.curator</groupId>
    35        <artifactId>curator-recipes</artifactId>
    36        <version>4.2.0</version>
    37    </dependency>
    38    <!--SpringBoot内嵌tomcat的jsp支持-->
    39    <dependency>
    40        <groupId>org.apache.tomcat.embed</groupId>
    41        <artifactId>tomcat-embed-jasper</artifactId>
    42    </dependency>
    43    <dependency>
    44        <groupId>org.example</groupId>
    45        <artifactId>api</artifactId>
    46        <version>1.0-SNAPSHOT</version>
    47    </dependency>
    48</dependencies>
    49<build>
    50    <resources>
    51        <!--因为需要整合jsp文件,所以要指定jsp文件编译位置-->
    52        <resource>
    53            <directory>src/main/webapp</directory>
    54            <targetPath>META-INF/resources</targetPath>
    55            <includes>
    56                <include>*.*</include>
    57            </includes>
    58        </resource>
    59    </resources>
    60    <plugins>
    61        <plugin>
    62            <groupId>org.springframework.boot</groupId>
    63            <artifactId>spring-boot-maven-plugin</artifactId>
    64        </plugin>
    65    </plugins>
    66</build>
  2. 创建webapp目录,并指定为web资源目录

  3. SpringBoot核心配置文件

    yaml
     1spring:
     2  #视图解析设置
     3  mvc:
     4    view:
     5      prefix: /
     6      suffix: .jsp
     7#dubbo设置
     8dubbo:
     9  #zookeeper设置
    10  registry:
    11    address: zookeeper://192.168.10.130
    12    client: curator
    13    port: 2181
    14  application:
    15    name: consumer
    16  scan:
    17  	#扫描指定包下的远程注入注解
    18    base-packages: com.lei.controller
  4. 创建控制层

    java
     1@Controller
     2public class UserController {
     3    //远程注入bean,负载均衡策略为 random
     4    @DubboReference(loadbalance = "random")
     5    private UserService userService;
     6    @DubboReference
     7    private StudentService studentService;
     8    @RequestMapping("/t1")
     9    public String t1(){
    10        return userService.say();
    11    }
    12    @RequestMapping("/t2")
    13    public String t2(int id, Model model){
    14        model.addAttribute("msg",studentService.queryStudentById(id));
    15        return "index";
    16    }
    17    @RequestMapping("/t3")
    18    public String t3( Model model){
    19        model.addAttribute("msg",studentService.getStudentCount());
    20        return "index";
    21    }
    22}

手写starter启动器

创建自动配置模块

创建maven项目:ch01-autoconfig

目录结构如下:

image-20210809213739290

  1. pom文件如下

    xml
     1<dependencies>
     2    <dependency>
     3        <groupId>org.springframework.boot</groupId>
     4        <artifactId>spring-boot-starter</artifactId>
     5    </dependency>
     6    <dependency>
     7        <groupId>org.projectlombok</groupId>
     8        <artifactId>lombok</artifactId>
     9    </dependency>
    10</dependencies>
  2. MyProperties类,用于读取properties配置文件

    java
    1@Data
    2@ConfigurationProperties("spring.user")		//配置文件前缀
    3public class MyProperties {
    4    private String name;
    5    private Integer age;
    6    private String address;
    7}
  3. MyBean自己需要注册的bean的类

    java
    1@Data
    2public class MyBean {
    3    private MyProperties myProperties;
    4}
  4. MyAutoConfig自动配置类,用于装配bean

    java
     1@Configuration
     2//ConditionalOnClass,当classpath路径下存在该类时,配置生效
     3@ConditionalOnClass(MyBean.class)
     4@EnableConfigurationProperties(MyProperties.class)
     5public class MyAutoConfig {
     6    @Autowired
     7    private MyProperties myProperties;
     8
     9    @Bean
    10    //prefix:前缀
    11    //value:具体的配置名
    12    //havingValue:判断是否等于value取出的值
    13    //ConditionalOnProperty 满足条件才装配该类
    14    @ConditionalOnProperty(prefix = "spring.user", value = "enabled", havingValue = "true")
    15    public MyBean myBean(){
    16        MyBean myBean=new MyBean();
    17        myBean.setMyProperties(myProperties);
    18        return myBean;
    19    }
    20}
  5. resources/META-INF/spring.factories,SpringBoot会自动扫描该配置文件,并读取里面的类

    properties
    1org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.MyAutoConfig

创建starter项目

仅用于依赖,里面不存在任何内容,依赖于autoconfig模块

pom文件

xml
1<dependencies>
2    <dependency>
3        <groupId>com.demo</groupId>
4        <artifactId>ch01-autoconfig</artifactId>
5        <version>0.0.1-SNAPSHOT</version>
6    </dependency>
7</dependencies>

创建测试项目

  1. pom文件,依赖自己创建的starter

    xml
     1<dependencies>
     2    <dependency>
     3        <groupId>org.springframework.boot</groupId>
     4        <artifactId>spring-boot-starter</artifactId>
     5    </dependency>
     6    <dependency>
     7        <groupId>com.lei</groupId>
     8        <artifactId>ch01-SpringBoot-start-demo</artifactId>
     9        <version>0.0.1-SNAPSHOT</version>
    10    </dependency>
    11    <dependency>
    12        <groupId>org.springframework.boot</groupId>
    13        <artifactId>spring-boot-starter-web</artifactId>
    14    </dependency>
    15</dependencies>
  2. application.properties

    properties
    1#spring.user.enabled=true,自己创建的starter中的配置类才会生效
    2spring.user.enabled=true
    3spring.user.name=lei
    4spring.user.age=24
    5spring.user.address=81497400@qq.com
  3. 创建控制器,并测试

    java
    1@RestController
    2public class TestController {
    3    @Autowired
    4    private MyBean myBean;		//这里的myBean是通过自己创建的starter加载进来的
    5    @RequestMapping("/t1")
    6    public Object t1(){
    7        return myBean;
    8    }
    9}
  4. 访问

    image-20210809215132689

相关注解

  • @Configuration:标记该类为SpringBoot配置类
  • @ConditionalOnClass(MyBean.class):标记类或方法,当classpath中存在MyBean字节码文件时,配置类生效
  • @ConditionalOnProperty(prefix = "spring.user", value = "enabled", havingValue = "true"):标记类或方法,prefix 前缀、value 属性、havingValue 该值与属性值进行比较;配置是否生效
  • @EnableConfigurationProperties(MyProperties.class):使读取配置属性的类MyProperties生效
  • @ConfigurationProperties("spring.user"):将配置属性映射到类

websocket

介绍

  • websocket时html5定义的一个协议,由tcp协议实现的一种网络协议
  • 该协议可以主动向客户端发送信息
  • http是短连接,一个请求结束,连接关闭,客户端到服务端单向;websocket是长连接,只需要一次请求初始化连接,然后所有的请求与响应都通过该TCP连接通信,双向
  • 传统web推送和websocket
    • 传统web推送:通常在页面通过Ajax定时轮询,浏览器需要不断的向服务器发送请求;无效请求占用大量带宽、CPU资源
    • websocket:可以节省带宽和资源,消息推送及时、通信高效、协议标识符 ws
  • 业务场景
    • 聊天室
    • 股票基金的实时显示
    • 企业内部管理通讯
    • 系统提醒、用户上下线提醒、实时数据更新

java中websocket Api

  • javaee 7中开始支持 websocket 协议,javaee 7定义了一套 websocket API 规范,也就是一系列接口,没有实现
  • 位于 javax.websocket 包下,包含客户端 API 和服务端 API
  • websocket API 的具体实现需要 web 容器
  • 服务端实现:Tomcat 7.x+、Spring 4.x+

相关注解

  1. 服务端代码

    java
     1@ServerEndpoint("/socket")
     2public class MyWebSocket {
     3    @OnOpen
     4    public void onOpen(Session session) throws Exception{
     5        System.out.println("客户端连接成功");
     6        session.getBasicRemote().sendText("websocket连接成功");
     7    }
     8    @OnMessage
     9    public void onMessage(String message, Session session) throws Exception{
    10        System.out.println("接收到消息");
    11        session.getBasicRemote().sendText("消息"+message);
    12    }
    13    @OnClose
    14    public void onClose(){
    15        System.out.println(this+"关闭连接");
    16    }
    17    @OnError
    18    public void onError(Session session,Throwable throwable){
    19        System.out.println("出错");
    20        throwable.printStackTrace();
    21    }
    22}
  2. 客户端代码

    html
     1<!DOCTYPE html>
     2<html lang="en">
     3<head>
     4    <meta charset="UTF-8">
     5    <title>Title</title>
     6    <script type="text/javascript" src="js/jquery-3.6.0.js" ></script>
     7    <script>
     8        var ws
     9        $(function (){
    10            if ("WebSocket" in window){
    11                var url="ws://localhost:8080/socket"
    12                ws=new WebSocket(url)
    13                //连通后回调函数
    14                ws.OPEN=function (){
    15                    console.log("websocket连接")
    16                }
    17                //接收到服务端返回的数据时执行
    18                ws.onmessage=function (msg){
    19                    console.log(msg.data)
    20                }
    21                //关闭websocket时执行
    22                ws.onclose=function (){
    23                    console.log("关闭websocket连接")
    24                }
    25            }
    26            $("#sendbtn").click(function (){
    27                sendMsg($("#sendmsg").val())
    28            })
    29            $("#closebtn").click(function (){
    30                ws.close()
    31            })
    32        })
    33        function sendMsg(msg){
    34            ws.send(msg)
    35        }
    36    </script>
    37</head>
    38<body>
    39    <div>
    40        <label for="sendmsg"></label>
    41        <textarea id="sendmsg" style="width: 500px;height: 50px"></textarea>
    42    </div>
    43    <div>
    44        <input type="button" id="sendbtn" value="发送">
    45        <input type="button" id="closebtn" value="离开">
    46    </div>
    47</body>
    48</html>

SpringBoot中websocket

SpringBoot 的 web 起步依赖内嵌的 tomcat 集成了 websocket 依赖

在线聊天室实现

  1. 添加websocker起步依赖

    xml
     1<!--模板引擎起步依赖-->
     2<dependency>
     3    <groupId>org.springframework.boot</groupId>
     4    <artifactId>spring-boot-starter-thymeleaf</artifactId>
     5</dependency>
     6<!--websocket起步依赖-->
     7<dependency>
     8    <groupId>org.springframework.boot</groupId>
     9    <artifactId>spring-boot-starter-websocket</artifactId>
    10</dependency>
  2. 编写配置文件,启用websocket支持,并注入 扫描websocket注解的bean

    java
     1@Configuration
     2@EnableWebSocket
     3public class MyConfig {
     4        /**
     5         * 注入一个ServerEndpointExporter,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     6         */
     7        @Bean
     8        public ServerEndpointExporter serverEndpointExporter() {
     9            return new ServerEndpointExporter ();
    10    }
    11}
  3. 编写工具类,维护所有在线人的信息

    java
     1public class WebsocketUtil {
     2    //保存用户信息的map
     3    public static final ConcurrentHashMap<String,Session> CLIENTS=new ConcurrentHashMap<>();
     4    //发送消息给所有人
     5    public static void sendMessageAll(String msg) {
     6        CLIENTS.forEach(((username, session) -> sendMessage(session,username,msg)));
     7    }
     8    //获取在线人数,即map中key的数量
     9    public static String getOnlineInfo() {
    10        Set<String> set=CLIENTS.keySet();
    11        if (set.size()==0){
    12            return "当前无人在线";
    13        }
    14        return set.toString()+" 在线";
    15    }
    16    //发送消息
    17    public static void sendMessage(Session session, String username,String msg) {
    18        if (session==null){
    19            return;
    20        }
    21        final RemoteEndpoint.Basic basic=session.getBasicRemote();
    22        if (basic==null){return;}
    23        try{
    24            basic.sendText(msg);
    25        }catch (Exception e){
    26            e.printStackTrace();
    27        }
    28    }
    29}
  4. 编写websocket类,用于接收请求

    java
     1@Component   //SpringBoot需要注入到ioc容器
     2@ServerEndpoint("/websocket/{username}")
     3public class ChartWebsocket {
     4    @OnOpen
     5    public void openSession(@PathParam("username") String username, Session session){
     6        String msg="用户["+username+"]进入聊天室";
     7        //发送登录信息给其他人
     8        WebsocketUtil.sendMessageAll(msg);
     9        String onlineInfo=WebsocketUtil.getOnlineInfo();
    10        //发送在线人数给自己
    11        WebsocketUtil.sendMessage(session,username,onlineInfo);
    12        //将信息放入工具类map
    13        WebsocketUtil.CLIENTS.put(username,session);
    14    }
    15    @OnMessage
    16    public void onMessage(@PathParam("username") String username,String message){
    17        //接收到信息后,发送给所有人
    18        WebsocketUtil.sendMessageAll("["+username+"]:"+message);
    19    }
    20    @OnClose
    21    public void onClose(@PathParam("username") String username){
    22        System.out.println(this+"关闭连接");
    23        //广播某人退出聊天室
    24        WebsocketUtil.sendMessageAll(username+"退出聊天室");
    25        //移除该用户的信息
    26        WebsocketUtil.CLIENTS.remove(username);
    27    }
    28    @OnError
    29    public void onError(@PathParam("username") String username,Session session,Throwable throwable){
    30        System.out.println("出错"+username);
    31    }
    32}
  5. 前端页面

    html
     1<!DOCTYPE html>
     2<html lang="en" xmlns:th="http://www.thymeleaf.org">
     3<head>
     4    <meta charset="UTF-8">
     5    <title>聊天室</title>
     6    <link th:href="@{/bootstrap-4.6.0-dist/css/bootstrap.css}" rel="stylesheet">
     7    <script type="text/javascript" th:src="@{/js/jquery-3.6.0.js}"></script>
     8    <script>
     9        var ws
    10        $(function (){
    11            if ("WebSocket" in window){
    12                ws=new WebSocket("ws://localhost:8080/websocket/"+$("#username").val())
    13                ws.OPEN=function (msg){
    14                    alert("进入聊天室")
    15                }
    16                //接收到服务端返回的数据时执行
    17                ws.onmessage=function (msg){
    18                    $("#disPlayTextarea").text($("#disPlayTextarea").text()+msg.data+"\n")
    19                }
    20                //关闭websocket时执行
    21                ws.onclose=function (){
    22                    console.log("关闭websocket连接")
    23                }
    24            }else {
    25                alert("浏览器不支持")
    26            }
    27            $("#sendBtn").click(function (){
    28                sendMsg($("#sendmsg").val())
    29            })
    30            $("#closeBtn").click(function (){
    31                ws.close()
    32            })
    33        })
    34        function sendMsg(msg){
    35            ws.send(msg)
    36        }
    37    </script>
    38</head>
    39<body>
    40<div class="container">
    41    <div class="container">
    42        <div class="form-group">
    43            <label for="disPlayTextarea">websocket聊天室</label>
    44            <textarea class="form-control" disabled id="disPlayTextarea" rows="10"></textarea>
    45        </div>
    46    </div>
    47    <div class="container">
    48        <div class="form-group">
    49            <label for="sendmsg"></label>
    50            <textarea class="form-control" id="sendmsg" rows="1"></textarea>
    51        </div>
    52        <input type="button" class="btn-primary" id="sendBtn" value="发送信息">
    53        <input type="button" class="btn-warning" id="closeBtn" value="退出聊天室">
    54        <input type="hidden" id="username" th:value="${username}">
    55    </div>
    56</div>
    57</body>
    58</html>
使用滚轮缩放
按住拖动