柏竹 柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档

柏竹

奋斗柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档
  • Java基础

  • JavaWeb

  • 拓展技术

  • 框架技术

  • 数据库

  • 数据结构

  • Spring

  • SpringMVC

  • SpringBoot

    • SpringBoot
      • 概述
      • 首次应用
        • Spring Boot无xml应用Web项目
        • Spring Boot启动器应用Web项目
      • 基础配置
        • Bean配置
        • Bean读取
        • Constructor 注入
        • Controller配置
        • 配置文件
        • 读取配置
        • 单属性读取
        • 对象成员变量读取
      • 业务事务
      • Session集群共享
      • 计划任务
      • 热部署
      • 日志
        • 日志级别
        • 日志配置
        • 环境控制
        • 日志格式
      • 事件监听
        • 监听顺序
        • 异步监听
        • 事务监听
    • SpringBoot3基础特性
    • SpringBoot3核心原理
    • 框架整合

    • SpringBoot部署
  • SpringClound

  • Ruoyi-Vue-Plus

  • 后端
  • SpringBoot
柏竹
2021-09-25
目录

SpringBoot

# 概述

Spring Boot 是 Spring 快速开发的脚手架,Spring Boot 大大优化了 Spring的 ==复杂配置== 和 ==依赖配置版本== 问题

特点

  1. 独立运行 Spring项目
  2. 内嵌 tomcat 和 jetty 容器 , 无需打包war文件
  3. 简化 maven配置
  4. 无 代码生成/xml配置
  5. ...

# 首次应用

# Spring Boot无xml应用Web项目

模拟原生的Servlet搭建Web

该配置方式学习了解即可,并非针对学习!!

  1. 创建 Maven无骨架项目 IDEA创建Maven项目 (opens new window) (无骨架Java项目)

  2. pom.xml 引入 SpringBoot相关依赖

    依赖配置 展开
    <packaging>war</packaging>
    <!-- 集中定义依赖版本号 -->
    <properties>
        <spring.version>5.1.2.RELEASE</spring.version>
        <pagehelper.version>4.2.1</pagehelper.version>
    </properties>
    
    <dependencies>
    
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- jstl -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  3. 建立 模拟web.xml配置 的Java程序类 WebInit类

    web.xml文件配置 (比对示例的配置文件

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    Java程序配置 (应用代码

    public class WebInit implements WebApplicationInitializer {
        // 初始化配置
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
            // 指定 spring配置类
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringMVCConfig.class);
    
            // 添加Serlvet并指定映射
            ServletRegistration.Dynamic springmvc =
                    servletContext.addServlet("springmvc",new DispatcherServlet(context));
            springmvc.addMapping("/");
            springmvc.setLoadOnStartup(1);
        }
    }
    

    SpringMVCConfig类 需要指定加载的 SpringMVC配置的类,在下面

  4. 创建 SpringMVC配置类

    web.xml文件配置 (比对示例的配置文件

    <mvc:annotation-driven />
    <context:component-scan base-package="com.sans"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<property name="prefix" value="/html/"/>
    	<property name="suffix" value=".html"/>
    </bean>
    

    Java程序配置 (应用代码

    @Configuration
    @ComponentScan("com.sans")
    @EnableWebMvc
    public class SpringMVCConfig implements WebMvcConfigurer {
    // 组件注解加载 xml:<mvc:default-servlet-handler/>
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    
    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/html/");
        viewResolver.setSuffix(".html");
        return viewResolver;
    }
    
  5. 创建 Controller

    @Controller
    public class HelloController {
        @RequestMapping("/hello")
        public String hello() {
            return "test";
        }
    }
    
  6. 在Web项目访问 html/test.html文件 访问即可

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── controller
  |    |    |          |      └── HelloController.java
  |    |    |          └── config
  |    |    |                 ├── SpringMVCConfig.java
  |    |    |                 └── WebInit.java
  |	   |	└──	resources
  |	  test
  └── pom.xml

# Spring Boot启动器应用Web项目

  1. 创建 Maven无骨架项目 IDEA创建Maven项目 (opens new window) (无骨架Java项目)

  2. pom.xml 引入 SpringBoot相关依赖

    <project>
        ...
        <!--父工程坐标
            web相关应用无需配置再配置版本号(解决版本冲突问题)
        -->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.0.RELEASE</version>
        </parent>
      
        <dependencies>
            <!--当中包含 tomcat、SpringMVC、...(有关web应用,自动配置)-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    ...
    </project>
    
  3. 创建 Application启动器类 全限定名 com.Application

    package com;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    // @SpringBootApplication启动类
    @SpringBootApplication
    public class Application {
        
        public static void main(String[] args) {
            SpringApplication.run(Application.class , args);
        }
    
    }
    
  4. 创建 HelloController控制器类 全限定名 com.controller.Application (为了方便看出构架结构)

    package com.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.sql.DataSource;
    
    @RestController
    public class HelloController {
        
        @GetMapping("/hello")
        public String hello() {
            return "hello , Spring Boot...";
        }
        
    }
    
  5. 启动类启用Main方法,再浏览器访问:http://localhost:8080/hello (页面返回有信息表示成功!)

项目结构

  .
  |
  ├── src
  |    ├── main
  |	   |	├── java
  |	   |	|	  └── com
  |	   |    |   	   ├── controller
  |    |    |          |        └── HelloController.java
  |	   |    | 		   └── Application.java
  |	   |	└──	resources
  |	  test
  └── pom.xml

# 基础配置

Spring Boot应用的是全注解配置(Java程序配置

注解:

  • @Configuration

    声明在类上 , 作为配置类(代替xml文件)

  • @Bean

    声明在方法上 , 将方法返回值导入Bean容器(代替 标签)

  • @value

    属性注入(需要属性文件进行搭配应用)

  • @PropertySource

    引入读取外部属性文件

  • @ConfigurationProperties

    引入读取外部属性文件 , 自动注入 成员变量(需要指定前缀 、Properties文件

  • @EnableConfigurationProperties

    自动注入 @ConfigurationProperties类

  • @EnableAutoConfiguration

    开启Spring Boot的自动配置机制

  • @RequestMapping 负责URL路由映射 , 可以添加映射规则(请求类型/响应体/请求头/...)

  • @RestController

    响应的对象都会转换为JSON格式

  • @Component

    把类对象实例化到 Bean容器 (类似于 @Service

  • @PostConstruct

    在配置类 执行的构造函数 和 自动注入 后执行初始化的方法(类似servlet的init()方法)

  • @Transactional

    Servcie事务业务异常回滚应用

  • @Repository

    DAO数据访问的层面 , 一般用于JDBC , 如果当中出现了异常很好的被 Service进行处理该异常

# Bean配置

通过 Spring Boot 还原 Spring配置连接池 的操作

Spring Bean配置

<!-- 配置连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
	<property name="url" value="${jdbc.url}" />
	<property name="username" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
</bean>

Spring Boot Bean配置

  1. 在以上篇章 首次应用的代码 基础上进行更改

  2. pom.xml 引入 Druid连接池依赖

    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>druid</artifactId>
    	<version>1.1.10</version>
    </dependency>
    
  3. 创建 jdbc.properties文件 到 resources资源中

    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://127.0.0.1:3306/test
    jdbc.username=root
    jdbc.password=root
    
  4. 创建 JdbcConfig配置类

    //配置类
    @Configuration
    //读取数据文件
    @PropertySource("classpath:jdbc.properties")
    public class JdbcConfig {
    
        /** 属性
         * 注入属性
         */
        @Value("${jdbc.driverClassName}")
        String driverClassName;
        @Value("${jdbc.url}")
        String url;
        @Value("${jdbc.username}")
        String username;
        @Value("${jdbc.password}")
        String password;
    
        /** 对象
         *  声明的Bean方法返回值会加入到Spring容器中
         *  说明:
         *      - 注解方法 Bean对象
         *      - 默认对象名id(Bean的ID) 与 方法名 一致
         *      - 自定义Bean对象名,可在注解 @Bean("自定义名称"),指定新对象
         */
        @Bean
        public DataSource dataSource() {
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    
    }
    
  5. 在 HelloController控制类 自动注入测试

    @RestController
    public class HelloController {
        
        // 该注解自动匹配名称进行指定注入
        @Autowired
        private DataSource dataSource;
    
        @GetMapping("/hello")
        public String hello() {
            return "hello , Spring Boot..." + dataSource;
        }
        
    }
    
  6. 启用Main方法调试测试。可利用断点进行查看配置连接池的参数传递情况

多个Bean情况

@Primary可设置优先选择 , 设置Bean优先级 , 防止同时出现两个情况

来源 : https://stackoverflow.com (opens new window)

# Bean读取

SpringBoot Bean读取方式有 :

方式 说明
@Autowired 根据 对象类型 进行匹配 获取Bean
@Resource 根据 setter方法 进行匹配 获取Bean
getBean() 根据 指定参数匹配获取Bean
Constructor 注入 构造器注入

getBean()方法重载有 :

  • getBean(String name)

    指定Bean标记的名称获取

  • getBean(Class<T> clazz)

    根据Bean的类型获取Bean , 如果同一类型有多个Bean , 会抛出异常

  • getBean(String name, Class<T> clazz)

    根据名称和类型获取Bean

  • <T> T getBean(Class<T> clazz, Object... args)

    根据类型和构造器参数获取Bean

  • ...

非Spring环境下获取Bean

SpringUtil 工具类 点击展开
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import com.ruoyi.common.utils.StringUtils;

/**
 * spring工具类 方便在非spring管理环境中获取bean
 */
@Component
public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware
{
    /** Spring应用上下文环境 Bean定义的后置处理器 */
    private static ConfigurableListableBeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
    {
        SpringUtils.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 获取对象
     *
     * @param name
     * @return Object 一个以所给名字注册的bean的实例
     * @throws org.springframework.beans.BeansException
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException
    {
        return (T) beanFactory.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     *
     * @param clz
     * @return
     * @throws org.springframework.beans.BeansException
     *
     */
    public static <T> T getBean(Class<T> clz) throws BeansException
    {
        T result = (T) beanFactory.getBean(clz);
        return result;
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @param name
     * @return boolean
     */
    public static boolean containsBean(String name)
    {
        return beanFactory.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @param name
     * @return boolean
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.isSingleton(name);
    }

    /**
     * @param name
     * @return Class 注册对象的类型
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getType(name);
    }

    /**
     * 如果给定的bean名字在bean定义中有别名,则返回这些别名
     *
     * @param name
     * @return
     * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
     *
     */
    public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
    {
        return beanFactory.getAliases(name);
    }

    /**
     * 获取aop代理对象
     * 
     * @param invoker
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getAopProxy(T invoker)
    {
        return (T) AopContext.currentProxy();
    }

    /**
     * 获取当前的环境配置,无配置返回null
     *
     * @return 当前的环境配置
     */
    public static String[] getActiveProfiles()
    {
        return applicationContext.getEnvironment().getActiveProfiles();
    }

    /**
     * 获取当前的环境配置,当有多个环境配置时,只获取第一个
     *
     * @return 当前的环境配置
     */
    public static String getActiveProfile()
    {
        final String[] activeProfiles = getActiveProfiles();
        return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null;
    }

    /**
     * 获取配置文件中的值
     *
     * @param key 配置文件的key
     * @return 当前的配置文件的值
     *
     */
    public static String getRequiredProperty(String key)
    {
        return applicationContext.getEnvironment().getRequiredProperty(key);
    }

}

# Constructor 注入

这个是强制依赖 , 是在构造器中提供的依赖 , 构造器必须有这依赖Bean , 并且依赖的对象受 final修饰 (防止循环依赖)

示例

@Controller
public class HelloController {
    private final AlphaService alphaService;
    private final BetaService betaService;
    
    @Autowired
    public HelloController(AlphaService alphaService, BetaService betaService) {
        this.alphaService = alphaService;
        this.betaService = betaService;
    }
}

搭配lombok @RequiredArgsConstructor

@RequiredArgsConstructor
public class VerifyController {

    private final VerifyService verifyService;
    private final InvitationService invitationService;
    private final VerificationCodeService verificationCodeService;
}

# Controller配置

Controller配置中 Spring提供有以下两种注解进行应用

  • @Controller 请求包含有 页面和数据
  • @RestController 请求只有 数据 (前后端分离模式)

一般情况下使用的 @RestController 居多 , 前后端分离嘛 . @Controller 则需要搭配模板引擎进行应用

参数传递 直接上代码 , 详细了解进入 SpringMVC参数传递篇

@RestController
public class ParamsController {

    // http://localhost:8080/getTest1
    @RequestMapping(value = "getTest1", method = RequestMethod.GET)
    public String getTest1() {
        return "GET请求1";
    }

    // http://localhost:8080/getTest2?nickname=zs&phone=123
    @RequestMapping(value = "getTest2", method = RequestMethod.GET)
    public String getTest2(String nickname, String phone) {
        System.out.println("nickname = " + nickname);
        System.out.println("phone = " + phone);
        return "GET请求2";
    }

    // http://localhost:8080/getTest3?name=zs
    @RequestMapping(value = "getTest3", method = RequestMethod.GET)
    public String getTest3(@RequestParam("name") String nickname) {
        System.out.println("nickname = " + nickname);
        return "GET请求3";
    }

    // http://localhost:8080/postTest1
    @RequestMapping(value = "/postTest1", method = RequestMethod.POST)
    public String postTest1() {
        return "POST请求1";
    }

    // http://localhost:8080/postTest2
    /*
    * application/x-www-form-urlencoded : {
    *   username:zs,
    *   password:123
    * }
     * */
    @RequestMapping(value = "/postTest2", method = RequestMethod.POST)
    public String postTest2(String username, String password) {
        System.out.println("username = " + username);
        System.out.println("password = " + password);
        return "POST请求2";
    }

    // http://localhost:8080/postTest3
    /*
     * application/x-www-form-urlencoded : {
     *   username:zs,
     *   password:123
     * }
     * */
    @RequestMapping(value = "/postTest3", method = RequestMethod.POST)
    public String postTest3(MyUser user) {
        System.out.println(user);
        return "POST请求3";
    }

    // http://localhost:8080/postTest4
    /*
       application/json : {
         "username":"zs",
         "password":"123"
       }
     */
    @RequestMapping(value = "/postTest4", method = RequestMethod.POST)
    public String postTest4(@RequestBody MyUser user) {
        System.out.println(user);
        return "POST请求4";
    }

    // http://localhost:8080/test/xxx...
    @GetMapping("/test/**")
    public String test() {
        return "通配符请求";
    }
}

通配符

  • * : 任意单词
  • ** : 任意路径
  • ? : 任意单个字符

通常使用的 * 较多 , 比如指定未知文件名的文件等等...

# 配置文件

SpringBoot 在启动时会将 resources 目录下的 application.properties/apllication.yml 文件 作为其默认配置文件

.properties 和 .yml 的区别

  • 配置结构 : .properties 线性结构 ;.yml 树状结构
  • 分割方式 : .properties 以 . 进行分割;.yml 以 : 进行分割

.properties配置文件结构

# properties格式
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com

.yml配置文件结构

# YAML格式
environments:
	dev:
		url: http://dev.bar.com
		name: Developer Setup
	prod:
		url: http://foo.bar.com
		name: My Cool App
my:
	servers:
		- dev.bar.com
		- foo.bar.com

优先级

目录的配置文件都会被加载,且顺序就是它们的优先级依次降低

  1. file:./config/
  2. file:./config/*/
  3. file:./
  4. classpath:/config/
  5. classpath:/

相同位置的 application.properties 和 application.yml ,而application.properties 更优先加载

file: 指当前项目根目录

**classpath:**指当前项目的类路径,即 resources 目录

多个配置文件存在的时候且它们有相同的配置内容情况下,SpringBoot会根据优先级进行覆盖低级的配置,形成互补配置:

  • 存在相同配置内容,高级覆盖低级的配置内容
  • 存在不同的配置内容,所有配置内容都读取

示例:

在以上的基础进行测试

application.yml文件

# 类路径下的 config 目录下
# 端口号为8090
# 上下文路径为 /test2
server:
    port: 8090
    servlet:
        context-path: /test2
#...

application.properties文件

server.port=8080
server.servlet.path=/test1
#...

启动 SpringBoot,控制信息:

通过浏览器访问测试

出现以上字样,因本地 test1、test2没有对应的资源

# 读取配置

SpringBoot中的配置也是根据用户定义的约定进行配置 , 唯独与Spring区别 , 就在于它不需要繁琐的xml解析的过程 , 省去了加载所需要的资源 !

SpringBoot读取配置方式 :

  • 单属性读取
  • 对象成员变量读取

参考文章 :

  • https://www.baeldung.com (opens new window)
  • https://blog.csdn.net (opens new window)

# 单属性读取

@Value 实现读取配置

jdbc.driver.username=root
jdbc.driver.password=root
@Configuration
public class JdbcProperties {
    @Value("${jdbc.driver.username}")
    private String username;
    @Value("${jdbc.driver.password}")
    private String password;
}

提示

@Configuration 注解是必不可少 , 启动项目时会实注入

# 对象成员变量读取

大致步骤 :

  1. 在配置文件中写好配置

  2. 在指定类应用 @ConfigurationProperties注解的prefix属性配置前缀 , 匹配 前缀和属性名

  3. 使映射的属性配置生效 , 有以下两种方式实现

    • 启动类 配置扫描加载 @ConfigurationPropertiesScan注解
      • 配置类 加载生效类 @EnableConfigurationProperties注解
  4. 测试应用

配置文件

jdbc.driver.username=root
jdbc.driver.password=root

引用类

配置约定 : 成员变量名 和 配置后缀名 进行匹配 , 前缀则自行在注解中添加参数prefix配置 , 例如 :

@Data
@Configuration
@ConfigurationProperties(prefix = "jdbc.driver")
public class JdbcProperties {
    private String username;
    private String password;
    // settre/gettre 方法省略 (一定一定不能少写
}

提示

我采用了lombok 可以使用@Data , 成员属性的 settre/gettre 方法 , 一定一定不能省略少写

生效配置

有两种实现方法

  1. 启动类 (推荐 , SpringBoot2.2以上版本生效)

    @ConfigurationPropertiesScan
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  2. 类加载 @EnableConfigurationProperties注解 , 可写在 启动类/配置类 上加载

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

测试

采用 自动注入形式 测试即可

# 业务事务

事务就不做多解释了 . SpringBoot中的业务事务 , 通过 @Transactional注解 实现

@Transactional常用属性

属性名 说明
propagation 配置 事务传播行为
isolation 配置 事务隔离级别
timeout 配置 事务超时时间(单位:s)
readOnly 配置 事务是否只读
rollbackFor 配置 事务回滚异常

一般些在 serviceImpl类使用如下 : (抛异常DDDD)

@Override
@Transactional(rollbackFor = {RuntimeException.class})
public boolean updateBean(Bean bean) {
    ....
}

# Session集群共享

SpringBoot默认情况下存Session到本机中 , 多台服务器不会共享Session

**解决方案 : **

Spring-Session 和 Redis 整合

  1. 引入 redis 和 spring-session redis 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson-spring-boot-starter</artifactId>
        <version>3.17.6</version>
    </dependency>
    
  2. 配好 redis连接 和 Session存储方式

    # redis 配置
    spring:
        redis:
            port: 6379
            host: localhost
            database: 0
        session:
        	store-type: redis
    

Session的读取会通过Redis进行的

# 计划任务

@EnableScheduling 在配置类上使用,开启计划任务的支持

  1. @EnableScheduling定义指定启动类 application上
  2. @Scheduled定义指定方法 预定执行(运行频率/运行周期等配置...)
  3. 运行即可

# 热部署

在实际开发中会频繁的修改后台类文件 , 而且每次修改都需要重新进行编译运行 , 因此非常的麻烦 .

Spring Boot 提供了 devtools组件 , 热部署正是解决了该问题 , devtools会监听 , classpath下的文件变动 , 触发 Restart类加载器加载该类 , 实现文件和配置热部署功能

热部署应用

  1. pon.xml

    <!-- 热部署 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
    
  2. application.properties

    # 热部署
    # 启动 热部署
    spring.devtools.restart.enabled=true
    # 重启目录
    spring.devtools.restart.additional-paths=src/main/java
    # classpath目录 修改修改不重启
    spring.devtools.restart.exclude=static/**
    
  3. 设置启动自动构建项目 设置 -> 搜索Conpile(编辑器) -> 勾选Build project automatically(启动自动构建项目)

  4. 设置运行时执行刷新 ==Ctrl+Shift+Alt+/== -> 进入Registry(注册表) -> 启动compiler.automake.allow.when.app.running(运行时执行)

    PS : 如果 4步骤没有该选项 , 则操作一下步骤 : 设置 -> Advansed Settings(高级设置) -> 勾选Allow auto-make(运行时执行)

做完以上配置 , 可以在类中边修改边测试代码啦

# 日志

SpringBoot 选用 SLF4j 和 logback (大厂建议: SLF4j)

SLF4j官方文档 :

  • https://springdoc.cn/spring-boot/features#features.logging (opens new window)
  • https://www.slf4j.org/manual.html (opens new window)
  • https://www.slf4j.org/apidocs/index.html (opens new window)

简单示例

public static void main(String[] args) {
	Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
}

# 日志级别

trace(跟踪) < debug(调试) < info(信息) < warn(警告) < error(错误)

在配置 info 时 , 仅打印靠后的 warn , error (包含本身info)

常用 trace / debug / info 三种日志输出

# 日志配置

只需在 applicaton.properties文件 配置即可

配置项 值 说明
logging.level.** (指定类路径) (String) info 控制台打印级别限制解除
logging.file.name (String) my.log 输出日志到 指定文件(my.log)
logging.file.path (String) /var/log 输出日志到 指定目录的指定文件(my.log)
logging.pattern.console (String) 在控制台输出日志格式
logging.pattern.file / logging.config (String) 指定文件中日志输出格式

基本配置

# 日志配置
logging:
  level:
  	# 根据 maven控制环境 , 按不同环境配置日志级别 (在pom.xml中查看对应环境)
    com.bozhu: @logging.level@
    org.springframework: warn

# 环境控制

环境控制交给Maven管理 , 在 pom.xml 增加配置 , 标识日志级别

<profiles>
    <profile>
        <id>local</id>
        <properties>
            <!-- 环境标识,需要与配置文件的名称相对应 -->
            <profiles.active>local</profiles.active>
            <logging.level>debug</logging.level>
        </properties>
    </profile>
    <profile>
        <id>dev</id>
        <properties>
            <!-- 环境标识,需要与配置文件的名称相对应 -->
            <profiles.active>dev</profiles.active>
            <logging.level>debug</logging.level>
        </properties>
        <activation>
            <!-- 默认环境 -->
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <profiles.active>prod</profiles.active>
            <logging.level>warn</logging.level>
        </properties>
    </profile>
</profiles>

# 日志格式

日志格式在默认使用中 一般会很长 , 大部分不是阅读维护查阅的信息 , 因此需要简化格式 .

布局文档 : https://logback.qos.ch/manual/layouts.html (opens new window)

默认格式 : 日期 - 时间 - 日志级别 - 线程id - 线程名称 - 全类名 - 消息

配置日志格式 :

日志格式 说明
%d 日期时间
%thread 线程名
%-5level 级别显示字符宽度
%logger{50} 日志名最长50 , 否则分割
%msg 日志消息
%n 换行

文件配置应用

  1. 配置日志

    logging.config: classpath:logback-plus.xml
    
  2. 创建 logback-plus.xml , resources根路径

    点击展开
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!-- 存储路径 -->
        <property name="log.path" value="./logs"/>
        <!-- 红色日期 绿色线程 高亮级别(5个字符空间) 洋红色类名 日志信息 -->
        <property name="console.log.pattern"
                  value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
        <!-- 去掉高亮颜色 -->
        <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
    
        <!-- appender日志输出组件 -->
        <!-- 控制台输出 -->
        <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
            <!-- 控制台输出格式(引用上面格式) -->
            <encoder>
                <pattern>${console.log.pattern}</pattern>
                <charset>utf-8</charset>
            </encoder>
        </appender>
    
        <!-- 控制台输出 -->
        <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log.path}/sys-console.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 日志文件名格式 -->
                <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
                <!-- 日志最大 1天 -->
                <maxHistory>1</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
                <charset>utf-8</charset>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                <!-- 过滤的级别 -->
                <level>INFO</level>
            </filter>
        </appender>
    
        <!-- 系统日志输出 -->
        <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log.path}/sys-info.log</file>
            <!-- 循环政策:基于时间创建日志文件 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 日志文件名格式 -->
                <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
                <!-- 日志最大的历史 60天 -->
                <maxHistory>60</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!-- 过滤的级别 -->
                <level>INFO</level>
                <!-- 匹配时的操作:接收(记录) -->
                <onMatch>ACCEPT</onMatch>
                <!-- 不匹配时的操作:拒绝(不记录) -->
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
    
        <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${log.path}/sys-error.log</file>
            <!-- 循环政策:基于时间创建日志文件 -->
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!-- 日志文件名格式 -->
                <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
                <!-- 日志最大的历史 60天 -->
                <maxHistory>60</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>${log.pattern}</pattern>
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!-- 过滤的级别 -->
                <level>ERROR</level>
                <!-- 匹配时的操作:接收(记录) -->
                <onMatch>ACCEPT</onMatch>
                <!-- 不匹配时的操作:拒绝(不记录) -->
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
    
        <!-- info异步打印追加日志 -->
        <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
            <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
            <discardingThreshold>0</discardingThreshold>
            <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
            <queueSize>512</queueSize>
            <!-- 添加附加的appender,最多只能添加一个 -->
            <appender-ref ref="file_info"/>
        </appender>
    
        <!-- error异步打印追加日志 -->
        <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
            <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
            <discardingThreshold>0</discardingThreshold>
            <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
            <queueSize>512</queueSize>
            <!-- 添加附加的appender,最多只能添加一个 -->
            <appender-ref ref="file_error"/>
        </appender>
        
    
        <!--系统操作日志-->
        <root level="info">
            <!-- 分别引用上面的日志输出组件 -->
            <appender-ref ref="console" />
            <appender-ref ref="async_info" />
            <appender-ref ref="async_error" />
            <appender-ref ref="file_console" />
        </root>
    
    </configuration>
    
  3. 测试打印日志即可

# 事件监听

详细篇章 : 点击跳转 (opens new window)

简单示例

点击展开
  1. 创建监听对象 MsgEvent

    @Data
    public class MsgDataEvent {
        // 消息类型
        private String type;
        // 消息内容
        private String content;
        // 发送者
        private String sender;
        // 接收者
        private String receiver;
    }
    
  2. 创建 监听接收

    @Slf4j
    @Service // @Component 也可以 , 只需将对象放入ioc容器即可(Bean管理)
    public class MsgServiceImpl {
    
        @EventListener
        public void MsgEvent(MsgDataEvent msg) {
            log.info("监听到消息啦: {}", msg);
        }
    
    }
    
  3. 构建 事件发送

    @RestController
    @RequiredArgsConstructor
    public class EventTestController {
    
        private final ApplicationContext applicationContext;
    
        @GetMapping("/event1")
        public String event1() {
            MsgDataEvent msg = new MsgDataEvent();
            msg.setType("INFO");
            msg.setContent("hello world");
            msg.setReceiver("system");
            msg.setSender("localhost");
            applicationContext.publishEvent(msg);
            return "event1";
        }
        
    }
    
  4. 发送请求测试即可

# 监听顺序

在监听一个对象多个监听器方法时 , 他们的执行是无序的

通过 @Order注解 控制排序 , 注解中的val值是有由小到大控制顺序

在以上简单示例的基础上修改

监听接收

 





 





 





@Order(0)
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
    log.info("1 => 监听到消息啦: {}", msg);
}

@Order(2)
@EventListener
public void MsgEvent3(MsgDataEvent msg) {
    log.info("3 => 监听到消息啦: {}", msg);
}

@Order(1)
@EventListener
public void MsgEvent2(MsgDataEvent msg) {
    log.info("2 => 监听到消息啦: {}", msg);
}

# 异步监听

监听事件执行的线程默认是同步 , 必须等待监听器的方法执行完才能继续执行 . 在某些业务场景可能会影响执行时长 (日志打印等..)

通过 @Async注解 异步实现

异步弊端

异步的线程是无法获取当前请求线程的信息 . 必须通过 传递监听对象 / 缓存 / 静态 等方式传递数据

在以上简单示例的基础上修改

监听接收








 

 



@GetMapping("/event1")
public String event1() {
    MsgDataEvent msg = new MsgDataEvent();
    msg.setType("INFO");
    msg.setContent("hello world");
    msg.setReceiver("system");
    msg.setSender("localhost");
    log.info(" => op");
    applicationContext.publishEvent(msg);
    log.info(" => ed");
    return "event1";
}

事件发送

 

























@Async
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
    log.info("1 => 监听到消息啦: {}", msg);
}

@Order(2)
@EventListener
public void MsgEvent3(MsgDataEvent msg) {
    log.info("3 => 监听到消息啦: {}", msg);
}

@Order(1)
@EventListener
public void MsgEvent2(MsgDataEvent msg) {
    log.info("2 => 监听到消息啦: {}", msg);
}

/** 结果
=> op
2 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
3 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
=> ed
1 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
**/

# 事务监听

事务监听能够确保事件是有效避免无效数据执行

通过 @TransactionalEventListener注解 实现异步 , 当完外部方法事务提交后执行 (更多事务自行查)

在以上简单示例的基础上修改

监听接收

 
















@TransactionalEventListener
@EventListener
public void MsgEvent1(MsgDataEvent msg) {
    log.info("1 => 监听到消息啦: {}", msg);
}

@EventListener
public void MsgEvent3(MsgDataEvent msg) {
    log.info("3 => 监听到消息啦: {}", msg);
}


/** 结果
3 => 监听到消息啦: MsgDataEvent(type=INFO, content=hello world, sender=localhost, receiver=system)
异常...
**/

在以上示例可以看出异常后 , 不会对事务的方法进行执行


以上笔记做了多次更改 , 如有 理解片面 , 评论指正 , 谢谢!

#SpringBoot

← SpringMVC RESTful API SpringBoot3基础特性→

最近更新
01
HTTPS自动续签
10-21
02
博客搭建-简化版(脚本)
10-20
03
ruoyi-vue-plus-部署篇
07-13
更多文章>
Theme by Vdoing | Copyright © 2019-2024 | 桂ICP备2022009417号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式