SpringBoot常用操作

news2024/11/16 7:52:22

SpringBoot常用操作

SpringBoot启动过程

1. 生成一个SpringApplication的对象
	1. webApplicationType = 推测web应用类型(NONE、REACTIVE、SERVLET)
	2. 从spring.factories中获取BootstrapRegistryInitializer对象
	3. initializers = 从spring.factories中获取ApplicationContextInitializer对象
	4. listeners = 从spring.factories中获取ApplicationListener对象


2. SpringApplication的对象.run()
	1. 获取SpringApplicationRunListener---->EventPublishingRunListener
	2. SpringApplicationRunListener.starting()
	3. 创建一个Spring容器
	4. ApplicationContextInitializer--->初始化Spring容器
	5. SpringApplicationRunListener.contextPrepared()
	6. 把传给run方法的配置类注册成为一个Bean
	7. SpringApplicationRunListener.contextLoaded()
	8. 会解析配置类、扫描、启动Tomcat/Jetty/Undertow  (AutoConfigurationImportSelector, DeferredImportSelector)
	9. SpringApplicationRunListener.started()
	10. callRunners-->ApplicationRunner,CommandLineRunner
	11. SpringApplicationRunListener.ready()

Spring Boot中的Starters

Spring Boot中的starter是Spring Boot的神器之⼀,Spring Boot提⾼了很多的starter,⽽每个starter 其实就是⼀个pom.xml⽂件。

⽐如在我们项⽬的pom.xml⽂件中,我们依赖了

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

这段相当于我们的⼯程依赖了spring-boot-starter-web,但是我们并没有指定具体的version,那到底 依赖的是哪个版本的spring-boot-starter-web呢?

这就是由控制的。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

这段代码是在给我们⾃⼰的⼯程指定了⼀个⽗⼯程,那这个⽗⼯程在哪呢?

Maven会先从本地仓库根据groupId和artifactId看是否有匹配的Jar包,如果没有就会进⾏下载,⽐如在 我电脑的2.6.6中就已经有了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dr9BQth-1687510426734)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623143650399.png)]

这个⽂件夹就是我们项⽬⼯程的⽗⼯程,我们的项⽬可以直接⽤⽗⼯程中所提供的。

那⽗⼯程中有什么东⻄呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tf8Z5IUB-1687510426735)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623143746435.png)]

其实就是⼀个pom⽂件。

所以,在我们的项⽬中,只要加上

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

这段代码,就相当于引⼊了spring-boot-starter-parent-2.6.6.pom这个pom⽂件。

在spring-boot-starter-parent-2.6.6.pom中有很对内容,其中最重要的是:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.6.6</version>
  </parent>
  <artifactId>spring-boot-starter-parent</artifactId>
  <packaging>pom</packaging>
  <name>spring-boot-starter-parent</name>
  <description>Parent pom providing dependency and plugin management for applications built with Maven</description>
  <properties>
    <java.version>1.8</java.version>
    <resource.delimiter>@</resource.delimiter>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>

⼜指定了⼀个⽗⼯程,以及⼀些properties,⽐如java.version为1.8

也就是,我们⾃⼰项⽬的⽗⼯程为spring-boot-starter-parent-2.6.6,⽽它的⽗⼯程为springboot-dependencies

同样,我们可以在Maven仓库中找到spring-boot-dependencies

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qGaL6P4T-1687510426735)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623144237925.png)]

进⽽看看spring-boot-dependencies⼜是什么?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OnUL4vFX-1687510426735)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623144333918.png)]

其实也就是⼀个pom⽂件,但是这个⽂件的内容就⾮常⾮常关键,因为它管理了Spring Boot默认⽀持 的所有依赖以及对应的版本。

就是因为了有了这个pom⽂件,在我们⾃⼰项⽬的pom⽂件中,如果想要⽤某个依赖,只要这个依赖在 spring-boot-dependencies-2.6.6.pom中提供了,那你就可以不写版本,就相当于⽤的Spring Boot给你提供的版本。

回到我们的项⽬,所以我们可以不写version。

那spring-boot-starter-web这个starter⾥⾯⼜有什么呢?

⾸先我们知道,这个依赖表示,我们是⼀个Spring Boot的web⼯程,正常来说,搭建⼀个web⼯程,是 要依赖很多东⻄的,⽐如tomcat,spring-web,spring-webmvc等等依赖,⽽这就是spring-bootstarter-web的作⽤,spring-boot-starter-web的作⽤就是帮我们提前把我们要开发⼀个web应⽤的依 赖都写好了,我们只要依赖spring-boot-starter-web,就相当于了依赖其他很多相关的依赖。

Spring Boot真的很贴⼼。

除开web场景,还有很多其他场景也是类似的,所以Spring Boot默认提供了很多starter,具体可以看官 ⽹的统计:https://docs.spring.io/spring-boot/docs/2.6.6/reference/html/using.html#using.build-systems.starters

值得注意的是,Spring Boot官⽅虽然提供了很多starter,但是有时可能仍然需要第三⽅来来⾃⼰实现 ⼀个starter并提供出来,对于这种情况,Spring Boot是有规范的,Spring Boot官⽅默认提供的starter 命名格式为 spring-boot-starter-* ,第三⽅⾃⼰实现的starter的命名格式为 *-spring-boot -starter 。

Spring Boot中的配置类

在Spring中,我们可以使⽤XML的⽅式来对Spring进⾏配置,也可以通过Java Config(也就是类+注 解)的⽅式进⾏配置,在Spring Boot中也是⼀样的。

我们可以通过@ImportResource注解来导⼊⼀个XML⽂件作为Spring的配置⽂件

@EnableAutoConfiguration
@ImportResource("spring.xml")
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/")
    String home() {
        return userService.test();
   }
}
public class UserService {
    public String test() {
        return "hello world, nice";
   }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
   https://www.springframework.org/schema/beans/spring-beans.xsd">
 <bean id="userService" class="com.xiaoge.service.UserService" />
 <bean id="userController" class="com.xiaoge.controller.UserController"
/>
</beans>

我们也可以通过@Import+@Configuration+@Bean来进⾏等价替换掉XML的形式

@Configuration
public class AppConfig {
    @Bean
    public UserService userService(){
        return new UserService();
   }
    @Bean
    public UserController userController(){
        return new UserController();
   }
}
@Import(AppConfig.class)
@EnableAutoConfiguration
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

当然,我们可以直接让XiaoGeApplication成为⼀个配置类,这样就不⽤额外添加⼀个AppConfig类了。

@EnableAutoConfiguration
@Configuration
public class XiaoGeApplication {
    @Bean
    public UserService userService(){
        return new UserService();
   }
    @Bean
    public UserController userController(){
        return new UserController();
   }
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

配置类的作⽤除开可以通过@Bean来定义Bean之外,也可以配置扫描路径,⽐如:

@EnableAutoConfiguration
@Configuration
@ComponentScan("com.xiaoge")
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

这样就不需要⽤@Bean了,但是得在类上加上@Component注解来定义Bean

扫描过程中,除开可以扫描到@Component、@Service、@Controller、@RestController等注解之 外,也能扫描到@Configuration

也就是我们可以在扫描路径下定义其他的配置类。

另外,由于XiaoGeApplication类所在包就是com.xiaoge,所以我们可以直接这么写:

@EnableAutoConfiguration
@Configuration
@ComponentScan
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

此时扫描路径就是@ComponentScan注解所在的类的包路径。

此时MyApplication就存在三个注解:

  1. @EnableAutoConfiguration
  2. @Configuration
  3. @ComponentScan

在Spring Boot中,提供了⼀个注解来替代这三个注解,这个注解就是@SpringBootApplication

所以代码就可以改成

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

所以,这个经典的写法,它表示了什么意思呢

  1. 定义了MyApplication类是⼀个配置类
  2. 定义了扫描路径,就是MyApplication所在的包路径
  3. 加了@EnableAutoConfiguration,那这个注解⼜表示什么意思呢?

翻译⼀下就是开启⾃动配置 接下来就来看看什么是⾃动配置

Spring Boot中的⾃动配置

Spring Boot⾃动配置会根据项⽬中所添加的依赖进⾏⾃动配置,⽐如我们项⽬中添加了

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

⽽这个依赖中,间接添加了

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.6.6</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.6.6</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.6.6</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.3.18</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.3.18</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

我们知道,我们在搭建Spring MVC⼯程时,除开要假如spring-web,spring-webmvc等依赖包之外, 最复杂的就是还要进⾏很多额外的配置

那在Spring Boot中,这些配置在哪呢?

注意,在我们项⽬中引⼊的spring-boot-starter-web中,引⼊了spring-boot-starter,⽽这个⾥⾯⼜ 引⼊了spring-boot-autoconfigure

在spring-boot-autoconfigure依赖中存在⼀个⽂件spring.factories,这个⽂件中记录了各种各样的 *****AutoConfiguration类,这些⾃动配置类(其实就是配置类)就是⽤来进⾏⾃动配置的。

那这个spring.factories⽂件中所记录的⾃动配置类,是什么时候⽣效的呢,这就是 @EnableAutoConfiguration注解的作⽤,只有加了这个注解,那这些⾃动配置类才会⽣效,因为 @EnableAutoConfiguration注解会去寻找spring.factories⽂件,并解析内容,所以能解析出来⾃动配 置类,并进⼀步对配置类进⾏解析。

⽐如在spring.factories⽂件中存在⼀个DispatcherServletAutoConfiguration,很明显是⽤来对 DispatcherServlet进⾏⾃动配置的,具体的细节,我们暂时就不深⼊了,本节课只需⼤体理解⾃动配置 的作⽤。

⾃动配置并不是去帮助我们配置扫描路径之类的,⽽是针对各种各样的场景,Spring Boot已经给我们配 置好了本来是我们需要配置的⼀些Bean以及⼀些参数。

条件注解

只⾃动配置类中,通常能看到很多条件注解(⽐如@ConditionalOnClass、@ConditionalOnBean),这是因为,如果我们要⽤Spring Boot的⾃动配置功能,就会加上@EnableAutoConfiguration注解,从⽽就会将解析spring.factories⽂件中的所有⾃动配置类,但是在⼀个项⽬中并不是所有⾃动配置类都要使⽤到。

⽐如我不需要⽤到MVC,那么WebMvcAutoConfiguration就没什么⽤,⽐如我不⽤Jta,那JtaAutoConfiguration也就没什么⽤,没⽤的配置类,就不要让Spring去解析它。

条件注解就⽤来实现这种控制的。

⽐如WebMvcAutoConfiguration上就有

@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class,
WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
  1. @ConditionalOnWebApplication(type = Type.SERVLET),表示WebMvcAutoConfiguration只有在应⽤类型是SERVLET类型是才⽣效

  2. @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class}),表示只有在项⽬以及依赖中存在这三个类时,WebMvcAutoConfiguration才⽣效

  3. @ConditionalOnMissingBean({WebMvcConfigurationSupport.class}),表示只有Spring容器中没有WebMvcConfigurationSupport.class类型的Bean时,WebMvcAutoConfiguration才⽣效

所以总结⼀下,WebMvcAutoConfiguration⽣效的条件是:当前应⽤类型是SERVLET类型的Web项⽬,并且项⽬的classpath中存在Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class三个,并且项⽬的Spring容器中没有WebMvcConfigurationSupport.class类型的Bean。

我们现在并不需要去理解为什么是这么⼀个条件,我们先来看看Spring Boot中到底有多少种这些条件注
解,分别表示什么意思。

1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
2. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
3. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有⼀个
4. ConditionalOnClass:是否存在某个类
5. ConditionalOnMissingClass:是否缺失某个类
6. ConditionalOnExpression:指定的表达式返回的是true还是false
7. ConditionalOnJava:判断Java版本
8. ConditionalOnJndi:JNDI指定的资源是否存在
9. ConditionalOnWebApplication:当前应⽤是⼀个Web应⽤
10. ConditionalOnNotWebApplication:当前应⽤不是⼀个Web应⽤
11. ConditionalOnProperty:Environment中是否存在某个属性
12. ConditionalOnResource:指定的资源是否存在
13. ConditionalOnWarDeployment:当前项⽬是不是以War包部署的⽅式运⾏
14. ConditionalOnCloudPlatform:是不是在某个云平台上
package com.xiaoge.service;

import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.system.JavaVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import javax.jws.soap.SOAPBinding;

/**
 * TODO
 *
 *     @Bean
 *     @ConditionalOnBean(value = MemberService.class)
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnBean(value = MemberService.class)  ioc容器中有MemberService类型单例对象, orderService才会被注入到ioc容器中
 *          @ConditionalOnBean(value = "bean类型")
 *          @ConditionalOnBean(name = "bean名称")
 *          @ConditionalOnBean 不写value/name默认是方法返回值类型
 *
 *
 *
 *     @Bean
 *     @ConditionalOnMissingBean(name = "userService")
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *     TODO @ConditionalOnMissingBean(value = UserService.class) ioc容器中没有UserService类型单例对象, orderService才会被注入到ioc容器中
 *          @ConditionalOnMissingBean(value = "bean类型")
 *          @ConditionalOnMissingBean(name = "bean名称")
 *          @ConditionalOnMissingBean 不写value/name默认是方法返回值类型
 *
 *
 *     @Bean
 *     @ConditionalOnSingleCandidate(value = UserService.class)
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *     TODO @ConditionalOnSingleCandidate(value = UserService.class) ioc容器中用UserService类型对象, 但是只能有一个, orderService才会被注入到ioc容器中
 *          @ConditionalOnSingleCandidate(value = "bean类型")
 *          @ConditionalOnSingleCandidate(name = "bean名称")
 *          @ConditionalOnSingleCandidate 不写value/name默认是方法返回值类型
 *          1. 使用该注解要对springBean加载顺序非常了解, 要不然, 装配了这个注解的bean明明有两个但是还是会被注入进去
 *          因为spring在扫描的时候, 扫描了其中一个UserService类型 bean, 另外一个UserService类型 bean还没有被扫描到
 *          就先扫描了当前orderService()方法, 所以它会被注入到ioc容器中
 *          2. 最好是使用自动装配, 把标注了该注解的方法仿造spring.factories文件中, 这样@ConditionalOnSingleCandidate百分百生效
 *          因为, 自动装配实现了DeferredImportSelector接口, 它是延迟的, 它会等项目中所有的bean加载完了, 再去加载spring.factories中的Bean
 *
 *
 *     @Bean
 *     @ConditionalOnClass(value = UserService.class)
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *     TODO @ConditionalOnClass(value = UserService.class) 当前classpath中有UserService类型的类, orderService才会被注入到ioc容器中
 *          @ConditionalOnClass(value = "class类型")
 *          @ConditionalOnClass(name = "class名称") 权限定类名 (@ConditionalOnClass(name = "com.xiaoge.service.UserService"))
 *
 *
 *     @Bean
 *     @ConditionalOnMissingClass(value = "com.xiaoge.service.UserService")
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *     TODO @ConditionalOnMissingClass(value = "com.xiaoge.service.UserService") 当前classpath中没有UserService类型的类, orderService才会被注入到ioc容器中
 *
 *
 *     @Bean
 *     @ConditionalOnExpression(value = "#{!'abc'.equals(environment.getProperty('test.condition'))}")
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnExpression(value = "${test.condition} == true") ${test.condition} == true结果为 true, orderService才会被注入到ioc容器中
 *          @ConditionalOnExpression(value = "#{!'abc'.equals(environment.getProperty('test.condition'))}") 复杂表达式这样写#{}
 *
 *
 *     @Bean
 *     @ConditionalOnJava(value = JavaVersion.EIGHT)
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnJava(value = JavaVersion.EIGHT)   当前系统jdk版本跟指定的java版本相同则为true, true的情况下, orderService才会被注入到ioc容器中
 *          @ConditionalOnJava(value = "java版本") 
 *          @ConditionalOnJava(range = "java版本范围", value = "java版本") 比较方式,Range.EQUAL_OR_NEWER:当前版本等于或高于、Range.OLDER_THAN:当前版本老于,越早的版本越老
 *
 *
 *     @Bean
 *     @ConditionalOnWebApplication
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnWebApplication 当前应用是一个web应用, orderService才会被注入到ioc容器中
 *
 *
 *     @Bean
 *     @ConditionalOnNotWebApplication
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnNotWebApplication 当前应用不是一个web应用, orderService才会被注入到ioc容器中
 *
 *
 *     @Bean
 *     @ConditionalOnProperty(name = "test.condition123", matchIfMissing = true) Environment中是否存在某个属性
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnProperty(name = "test.condition") 当前properties文件中有test.condition属性, orderService才会被注入到ioc容器中
 *          @ConditionalOnProperty(name = "test.condition123", matchIfMissing = true) 当前properties文件中不管有没有test.condition123属性, orderService都会被注入到ioc容器中(因为matchIfMissing = true, 默认matchIfMissing = false)
 *
 *
 *     @Bean
 *     @ConditionalOnResource(resources = "http://www.baidu.com")
 *     public OrderService orderService() {
 *         return new OrderService();
 *     }
 *
 *     TODO @ConditionalOnResource(resources = "http://www.baidu.com") 指定一个资源, 当前资源存在(本地/网络), orderService才会被注入到ioc容器中
 *
 *
 *
 *     TODO @ConditionalOnWarDeployment:当前项⽬是不是以War包部署的⽅式运⾏
 *     TODO @ConditionalOnCloudPlatform:是不是在某个云平台上
 *
 *      @Bean
 *      @Profile(value = "dev")
 *      public OrderService orderService() {
 *         return new OrderService();
 *     }
 *     TODO @Profile(value = "dev") 当前环境是dev, , orderService才会被注入到ioc容器中
 *          spring.profiles.active: dev  自己在application.yml中配置对应版本@Profile->value的值必须跟spring.profiles.active值相同
 *          要不然bean不能被注入到ioc中
 *
 *
 * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
 * @since
 */
@Configuration
public class AppConfig {

    @Bean
    @ConditionalOnResource(resources = "http://www.baidu.com")
    public OrderService orderService() {
        return new OrderService();
    }

}

Spring Boot中的spring.factories

在我们自己的项目中,我们自己定义的配置类,我们自然可以扫描到它,但是如果某个配置类不在我们的扫描范围内,该怎么办呢?

此时就可以利用spring.factories机制,可以在spring.factories中指定想添加的配置类,并可以使用

  1. @AutoConfigureAfter(BppConfig.class):表示本配置类要在BppConfig解析之后才解析
  2. @AutoConfigureBefore(BppConfig.class):表示本配置类要在BppConfig解析之前才解析
  3. @AutoConfigureOrder( 1 ):直接用数字定义顺序
@Configuration
@AutoConfigureOrder( 2 )
public class AppConfig {
    @Bean
    public OrderService orderService(){
    	return new OrderService();
    }
}
@Configuration
@AutoConfigureOrder( 2 )
public class BppConfig {
    @Bean
    @ConditionalOnBean(OrderService.class)
    public UserService userService(){
    	return new UserService();
    }
}

META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xiaoge.AppConfig,\
com.xiaoge.BppConfig

注意,上⾯的上个能排序的注解,只能针对spring.factories中的配置类进⾏排序,如果配置类是我们⾃⼰扫描出来的,则这三个注解⽆效,⽽且如果是扫描出来的使⽤@Order或者Ordered接⼝来排序也是没有⽤的,因为Spring的扫描中根本就不会进⾏排序。

Spring Boot中的属性绑定

我们可以使⽤@Value(“${xxx}”)的⽅式来获取properties中的属性值。

如果properties⽂件的名字是application.properties,那就不需要⽤@PropertySource注解,如果不是,就需要@PropertySource(“xiaoge.properties”)

在Spring Boot提供了⼀种更⽅便的⽅式来获取properties⽂件中的属性值。

⽐如我们⽤@Value,在UserService得写⼀遍所有的@Value,可能在其他Service也得写⼀遍

@Component
public class UserService {
    @Value("${username}")
    private String username;
    @Value("${password}")
    private String password;
    public String test() {
        return username + ":" + password;
   }
}

现在⽤Spring Boot,我们可以

@ConfigurationProperties
@Component
public class MyProperties {
    private String username;
    private String password;
    // setter getter
}

在UserService中,把MyProperties当作⼀个Bean⽤即可

@Component
public class UserService {
    @Autowired
    private MyProperties myProperties;
    public String test() {
        return myProperties.getUsername() + ":" + myProperties.getPassword();
   }
}

在Spring Boot⾃身的源码中,通常不会在MyProperties上加@Component,⽽是在配置类上加上@EnableConfigurationProperties(MyProperties.class)

@Configuration
@ConditionalOnClass(XiaoGeApplication.class)
@EnableConfigurationProperties(MyProperties.class)
public class AppConfig {
}

这样,就可以控制只有在符合指定的条件时,才会使得MyProperties成为Bean

另外还可以使⽤@ConfigurationPropertiesScan来进⾏扫描

@SpringBootApplication
@ConfigurationPropertiesScan("com.xiaoge.properties")
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

这样,照样可以是MyProperties⽣效。

有时,如果我们想利⽤我们⾃⼰的properties来构造第三⽅提供的Bean,那就可以利⽤ @Bean+@ConfigurationProperties

@ConfigurationPropertiesScan:  @ConfigurationPropertiesScan("com.xiaoge.properties") 直接扫描包, 包里面带@ConfigurationProperties, 全部都属性绑定并且注入到ioc容器中, 因为当属性绑定类很多的时候你不可能用@EnableConfigurationProperties一个个注入的
/**
 * TODO
 *    @ConfigurationPropertiesScan("com.xiaoge.properties") 直接扫描包, 包里面带@ConfigurationProperties, 全部都属性绑定并且注入到ioc容器中
 *    因为当属性绑定类很多的时候你不可能用@EnableConfigurationProperties一个个注入的
 *
 *    todo 使用@ConfigurationPropertiesScan和@EnableConfigurationProperties好处是当用到了bean判断注解, 满足条件后才行这是规范, 而不是直接(@Component + @ConfigurationProperties)
 *
 * @author <a href="mailto:1330137071@qq.com">Zhang Xiao</a>
 * @since
 */
@Service("OrderService1")
@ConfigurationPropertiesScan("com.xiaoge.properties")
public class OrderService {
}

Spring Boot中的外部配置

Spring Boot虽然会⾃动给我们做⼀些配置,当有些配置肯定是得我们⾃⼰来配的,⽐如数据库的连接地 址,⽤户名,密码等。

我们可以通过Java properties files, YAML files, environment variables, and command-line arguments来进⾏配置。

application.properties中配置

password=xiaoge123456

UserService类是这么写:

@Component
@ConfigurationProperties
public class UserService {
    private String password;
    public String test() {
        return password;
   }
    // setter getter
}

最后password属性就为xiaoge123456。

删掉application.properties⽂件,新建application.yml

password: xiaoge123456yml

重启项⽬,得到的是xiaoge123456yml

如果同时存在application.properties和application.yml,application.properties的优先级更⾼

假如在JVM环境变量中配置,如果是通过java命令运⾏,就是-Dpassword=xiaoge123456jvm

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DBzkC0gl-1687510426736)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623151935293.png)]

最后password取到的值为JVM环境变量中的值

我们在操作系统的环境变量中设置,设置完IDEA重启才能⽣效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vmnAf9u-1687510426736)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623152114898.png)]

最后结果仍然是JVM环境变量中的值,那如果把JVM中的环境变量中配置的删除掉,就能获取掉操作系 统环境变量中所配置的值了

所以,JVM环境变量优先级 > 操作系统环境变量 > application.properties > application.yml

还可以通过命令⾏参数来设置值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TByd2Qok-1687510426736)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623152543747.png)]

相当于:java -jar XiaoGeSpringBoot-1.0-SNAPSHOT.jar --password=1111

这种⽅式的优先级⾼于JVM环境变量优先级

所以

命令⾏参数 >VM环境变量优先级 > 操作系统环境变量 > application.properties > application.yml

除了这⼏种配置参数之外,还有很多其他⽅式,优先级从低到⾼为:

  1. SpringBoot默认值,通过SpringApplication.setDefaultProperties所设置的

  2. 在@Configuration配置类上通过@PropertySource注解引⼊的properties⽂件,注意在Spring容器 刷新之前这种配置是不会⽣效的,所以通过这种⽅式所配置的loggin.*,spring.main.*的配置项在 容器启动过程中是不会⽣效的。

  3. Config data

    1. 同⼀个Jar包内的application.properties和YAML

    2. 同⼀个Jar包内的application-{profile}.properties and YAML

    3. jar包外的application.properties and YAML

    4. jar包外的application-{profile}.properties and YAML

  4. 操作系统环境变量

  5. JVM环境变量

  6. ServletContext初始化参数

  7. ServletConfig初始化参数

  8. 嵌⼊在环境变量中的⾏内JSON,⽐如Dspring.application.json=‘{“password”:“xiaoge123456json”}’

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJr4TyVm-1687510426737)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623153434937.png)]

  9. 命令⾏参数

  10. 测试环境

  11. Devtools全局设置

对于application.properties and application.yaml⽂件,Spring Boot会(优先级从低到⾼)

  1. 先从classpath下找
    1. classpath根⽬录
    2. classpath下的config⽬录
  2. 应⽤启动时的当前⽬录
    1. 当前⽬录
    2. 当前⽬录下的/config⼦⽬录
    3. /config⼦⽬录下的⼦⽬录(这个⼦⽬录名字随便叫,只要⾥⾯有application.properties and application.yaml⽂件就可以)

默认会找名字叫做application的⽂件,我们可以通过–spring.config.name=myproject换⼀个名字

Spring Boot中的Profiles

Spring Boot⽀持某个Bean、某个配置类、某个@ConfigurationProperties在某个特定环境下才⽣效。

⽐如:

@SpringBootApplication
public class XiaoGeApplication {
    @Bean
    @Profile("dev")
    public UserService userServiceDev(){
        return new UserService("zhouyudev");
   }
    @Bean
    @Profile("prod")
    public UserService userServiceProd(){
        return new UserService("zhouyuprod");
   }
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}
public class UserService {
    private String password;
    public UserService(String password) {
        this.password = password;
   }
    public String test() {
        return password;
   }
}

application.properties:

spring.profiles.active=prod

此时userServiceProd⽣效。

可以通过其他各种⽅式来配置spring.profiles.active。

还可以

@ConfigurationProperties
@Profile("dev")
@Component
public class UserService {
    private String password;
    public String test() {
        return password;
   }
    public String getPassword() {
        return password;
   }
    public void setPassword(String password) {
        this.password = password;
   }
}

application.properties:

password=xiaoge

application-dev.properties:

password=xiaogedev

application-prod.properties:

password=xiaogeprod

通过命令⾏的⽅式指定profile:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vqOitNuR-1687510426737)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623154339363.png)]

我们可以在properties⽂件中定义⼀个profile groups

spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq

这样可以通过–spring.profiles.active=production,来激活proddb和prodmq这两种profile

Spring Boot中的⽇志

⽇志格式

默认的⽇志格式为

2023-06-23 16:04:41.303  INFO 5972 --- [           main] com.xiaoge.XiaoGeApplication             : Starting XiaoGeApplication using Java 1.8.0_221 on xiaoge with PID 5972 (D:\worksprace\zhangxiao-java\springboot2.6.6demo\target\classes started by 13301 in D:\worksprace\zhangxiao-java\springboot2.6.6demo)
2023-06-23 16:04:41.303  INFO 5972 --- [           main] com.xiaoge.XiaoGeApplication             : The following 1 profile is active: "dev"
2023-06-23 16:04:55.266  INFO 5972 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2023-06-23 16:04:55.266  INFO 5972 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2023-06-23 16:04:55.275  INFO 5972 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.60]
2023-06-23 16:04:55.586  INFO 5972 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2023-06-23 16:04:55.586  INFO 5972 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 14235 ms
2023-06-23 16:04:55.922  INFO 5972 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2023-06-23 16:04:55.928  INFO 5972 --- [           main] com.xiaoge.XiaoGeApplication             : Started XiaoGeApplication in 15.107 seconds (JVM running for 17.531)
  • ⽇期和时间:精确到毫秒
  • ⽇志级别:ERROR, WARN, INFO, DEBUG, or TRACE
  • 进程ID
  • — 分隔符
  • 线程名
  • Logger名:通常就是类名(经常是缩写)
  • ⽇志消息
控制台打印

默认⽇志配置在会将消息回显到控制台。默认情况下,会记录 ERROR 级别、WARN 级别和 INFO 级别 的消息。可以通过使⽤ --debug 在启动应⽤程序时启⽤调试模式。

也可以在application.properties配置debug=true

调试模式下会打印更多的信息。

⽂件输出

默认情况下Spring Boot只会把⽇志输出到控制台,不会写⼊到⽂件中。我们可以在 application.properties中配置logging.file.name或logging.file.path指定⽇志⽂件路径,从⽽可以额外 的将⽇志写⼊⽂件中。

logging.file.namelogging.file.path描述
仅在控制台打印
具体的某个⽂件名称可以是某个具体路径,或者 相对于当前⽬录的某个⽂件
具体的某个⽬录写⼊到指定⽬录下的spring.log

⽇志⽂件达到10MB时就会rotate,默认会记录ERROR、WARN、INFO级别的⽇志。

Spring Boot中的异步和任务调度

如果我们没有配置⼀个Executor的Bean,Spring Boot会默认给我们配置⼀个具有合理默认值的 ThreadPoolTaskExecutor,⽤来在@EnableAsync时进⾏异步执⾏。

默认线程池会使⽤8个核⼼线程,并可以根据负载增⻓和收缩,可以通过spring.task.execution.*进⾏配 置,⽐如:

spring.task.execution.pool.max-size=16
spring.task.execution.pool.queue-capacity=100
spring.task.execution.pool.keep-alive=10s

默认情况下在@EnableScheduling时会配置⼀个ThreadPoolTaskScheduler,这个线程池默认只会有⼀ 个线程,可以通过spring.task.scheduling*来进⾏配置,⽐如:

spring.task.scheduling.thread-name-prefix=scheduling-
spring.task.scheduling.pool.size=2
@Component
public class UserService {
    private static Log log = LogFactory.getLog(UserService.class);
    @Async
    public void test() {
        log.info("test");
   }
}
debug=true
spring.task.execution.threadNamePrefix=xiaoge-

可以利⽤TaskExecutorBuilder来快速的⾃定义⼀个线程池,利⽤部分默认值。

@Bean
public ThreadPoolTaskExecutor taskExecutor(TaskExecutorBuilder builder){
    ThreadPoolTaskExecutor taskExecutor = builder.build();
    taskExecutor.setThreadNamePrefix("xiaoge--");
    return taskExecutor;
}

自定义TypeFilter

package com.xiaoge;

import com.xiaoge.service.UserService;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;

/**
 * 让spring容器排除当前指定的bean
 *
 * @since
 */
public class XiaogeTypeExcludeFilter extends TypeExcludeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        return metadataReader.getClassMetadata().getClassName().equals(TestClass.class.getName());
    }

}

package com.xiaoge;

import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

/**
 * 该bean定义在spring.factories自动装配
 *
 * @since
 */
public class XiaoGeApplicationContextInitializer implements ApplicationContextInitializer {
    /**
     * Initialize the given application context.
     *
     * @param applicationContext the application to configure
     */
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.getBeanFactory().registerSingleton("xiaogeTypeExcludeFilter", new XiaogeTypeExcludeFilter());
    }
}

在spring.factory添加该过滤器

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.xiaoge.XiaoGeApplicationContextInitializer

Spring Boot中的 SpringApplicationRunListener

Spring Boot中的 SpringApplicationRunListener
public interface SpringApplicationRunListener {
 /**
 * 应⽤刚启动时调⽤.
 */
 default void starting() {
 }
 /**
 * Environment准备好之后调⽤
 */
 default void environmentPrepared(ConfigurableEnvironment environment)
{
 }
 /**
 * 创建完ApplicationContext对象后调⽤
 */
 default void contextPrepared(ConfigurableApplicationContext context)
{
 }
 /**
 * 将Main类注册为BeanDefinition之后调⽤
 */
 default void contextLoaded(ConfigurableApplicationContext context) {
 }
 /**
 * ApplicationContext完成了刷新,应⽤已经启动,但是CommandLineRunner和
ApplicationRunner还没调⽤
 */
 default void started(ConfigurableApplicationContext context) {
 }
 /**
 * CommandLineRunner和ApplicationRunner调⽤完
 */
 default void running(ConfigurableApplicationContext context) {
 }
 /**
 * 容器启动出现异常后调⽤
 */
 default void failed(ConfigurableApplicationContext context, Throwable exception) {
 }
}

ApplicationRunner和CommandLineRunner

这两个接⼝只有定义上有点区别,在功能和执⾏时机上都⼀样。

public interface CommandLineRunner {
 void run(String... args) throws Exception;
}
public interface CommandLineRunner {
 void run(String... args) throws Exception;
}

可以发现,区别在于run⽅法所接收的⼊参不同,所以这⾥的重点就是ApplicationArguments表示什 么。

定义⼀个XiaoGeApplicationRunner:

@Component
public class XiaoGeApplicationRunner implements ApplicationRunner {
 @Override
 public void run(ApplicationArguments args) throws Exception {
 System.out.println("nonOptionArgs: " + args.getNonOptionArgs());
 System.out.println("optionNames: " + args.getOptionNames());
 for (String sourceArg : args.getSourceArgs()) {
 System.out.println("sourceArg: " + sourceArg);
 }
 }
}

启动Spring Boot时,增加⼀下参数

@SpringBootApplication
public class XiaoGeApplication {
 public static void main(String[] args) {
 SpringApplication.run(XiaoGeApplication.class, "--k1=v1", "--k2=v2", "t3","t4");
 }
}

runner中打印的结果为:

nonOptionArgs: [t3, t4]
optionNames: [k1, k2]
sourceArg: --k1=v1
sourceArg: --k2=v2
sourceArg: t3
sourceArg: t4

可以发现,加了"–"的都是option, 没有加的就是nonOption,sourceArg就是原始的参数。⽽ CommandLineRunner中接收的就是sourceArg

Spring Boot中的@WebServlet注解

我们知道,在⼀个Java Web应⽤中,我们可以通过@WebServlet来定义⼀个Servlet,Tomcat会负责扫 描项⽬中哪些类上⾯添加了@WebServlet,从⽽把相对应的Servlet对象添加到Tomcat中去。

但是在Spring Boot中,我们除开照样可以通过@WebServlet来定义⼀个Servlet之外,还需要额外在配 置类上添加⼀个@ServletComponentScan注解,顾名思义,这个注解是负责去扫描Servlet的。

也就是在⽤Spring Boot时,@WebServlet的扫描⼯作从Tomcat中转移到了Spring Boot。

那底层是如何⼯作的呢?

ServletRegistrationBean

⾸先得学习⼀下ServletRegistrationBean,很明显,这个是⼀个Bean,这个Bean可以⽤来注册 Servlet。

可以通过BeanDefinition来定义⼀个ServletRegistrationBean,并指定我们想要注册的Servlet,⽐ 如:

BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServletRegistrationBean.class);
builder.addPropertyValue("initParameters", servlet的初始化参数);
builder.addPropertyValue("loadOnStartup", loadOnStartup的值);
builder.addPropertyValue("name", servlet的名字);
builder.addPropertyValue("servlet", servlet对象);
builder.addPropertyValue("urlMappings", servlet的urlMappings);
registry.registerBeanDefinition(name, builder.getBeanDefinition()); // 把BeanDefinition注册到Spring容器中

那ServletRegistrationBean是如何⼯作的呢?

ServletRegistrationBean继承了ServletContextInitializer接⼝,⽽ServletContextInitializer是Spring Boot所提供的⼀个函数式接⼝。

在Spring Boot启动流程中,会⽣成⼀个TomcatStarter对象,TomcatStarter实现了 ServletContainerInitializer接⼝,注意,这个接⼝是Servlet规范中所定义的接⼝。

  • ServletContextInitializer:这是Spring Boot定义的
  • ServletContainerInitializer:这是Servlet规范定义的

并且Tomcat在启动之前会把TomcatStarter添加到Tomcat中去,从⽽Tomcat在启动过程中,就会调⽤ ServletContainerInitializer接⼝的onStartup()⽅法,也就是会执⾏TomcatStarter的onStartup()⽅法。

⽽TomcatStarter的onStartup()⽅法会从Spring容器中找到所有的ServletContextInitializer类型的 Bean,然后循环执⾏ServletContextInitializer的**onStartup(ServletContext servletContext)**⽅法,从 ⽽就会执⾏到某个ServletRegistrationBean的onStartup()。

⽽ServletRegistrationBean的onStartup⽅法就会把⾃⼰所拥有的Servlet对象添加到ServletContext中 去,从⽽完成了Servlet的添加的。

@ServletComponentScan

⽽@ServletComponentScan注解的作⽤就是去指定路径下扫描@WebServlet,扫描到了⽣成 ServletRegistrationBean。

⽽核⼼是会使⽤⼀个ClassPathScanningCandidateComponentProvider来进⾏扫描,这个类并不是 Spring Boot中的,⽽是Spring Framework中的,Spring Boot只是利⽤它,当然Spring Boot会做⼀些 设置,⽐如向ClassPathScanningCandidateComponentProvider中添加IncludeFilter

⽐如会添加三个IncludeFilter:

1.  new AnnotationTypeFilter(WebServlet.class); 
1.  new AnnotationTypeFilter(WebFilter.class); 
1.  new AnnotationTypeFilter(WebListener.class)

就是有了这三个Filter,扫描时才能扫描到这三个注解,并⽣成对应的BeanDefinition,⽐如:

  1. @WebServlet对应ServletRegistrationBean
  2. @WebFilter对应FilterRegistrationBean
  3. @WebListener对应ServletListenerRegistrationBean
总结

Spring Boot启动时:

  1. 先扫描@WebServlet,⽣成ServletRegistrationBean
  2. 向Tomcat中添加TomcatStarter
  3. 启动Tomcat时执⾏TomcatStarter的onStartup()
  4. 从⽽执⾏ServletRegistrationBean的onStartup(ServletContext servletContext)
  5. 从⽽将Servlet对象注册到Tomcat中

Spring Boot整合JdbcTemplate

增加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置Datasource
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xiaoge
spring.datasource.username=root
spring.datasource.password=xiaoge123456***
使⽤JdbcTemplate
@Component
public class UserService {
    private static Log log = LogFactory.getLog(UserService.class);
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void test() {
        jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
        String result = jdbcTemplate.queryForObject("select a from t1 limit 1", String.class);
        log.info(result);
   }
    
}

Spring Boot整合Mybatis

增加依赖
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置Datasource
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/xiaoge
spring.datasource.username=root
spring.datasource.password=xiaoge123456***
新建Mapper
@Mapper
public interface UserMapper {
    @Insert("insert into t1 values(1,1,1,1,'1')")
    void insert();
    @Select("select a from t1 limit 1")
    String select();
}
使用mybatis
@Component
public class UserService {
    private static Log log = LogFactory.getLog(UserService.class);
    @Autowired
    private UserMapper userMapper;
    public void test() {
        userMapper.insert();
        String result = userMapper.select();
        log.info(result);
   }
}
@MapperScan

可以不使⽤@Mapper注解,可以通过@MapperScan来进⾏扫描

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

Spring Boot整合Mybatis Plus

添加依赖
  <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
代码⽣产器
public class MybatisPlusGenerator {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/xiaoge",
"root", "xiaoge123456***")
               .globalConfig(builder -> {
                    builder.author("xiaoge") // 设置作者
                           .fileOverride() // 覆盖已⽣成⽂件
                           
.outputDir(System.getProperty("user.dir")+"/src/main/java"); // 指定输出⽬})
               .packageConfig(builder -> {
                    builder.parent("com.xiaoge") // 设置⽗包名
                           
.pathInfo(Collections.singletonMap(OutputFile.mapperXml,
System.getProperty("user.dir") +"/src/main/resources/mapper/"));
               })
               .strategyConfig(builder -> {
                    builder.addInclude("t1"); // 设置需要⽣成的表名
               })
               .templateEngine(new FreemarkerTemplateEngine()) // 使⽤Freemarker引擎模板,默认的是Velocity引擎模板
               .execute();
   }
}

运⾏代码⽣成器就能根据表⾃动⽣成代码,⽐如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v6dpfmPs-1687510426738)(C:\Users\13301\AppData\Roaming\Typora\typora-user-images\image-20230623163212250.png)]

在T1Controller中定义测试⽅法:

@Controller
@RequestMapping("/t1")
public class T1Controller {
    @Autowired
    private IT1Service it1Service;
    @GetMapping("/test")
    public List<T1> test(){
        return it1Service.list();
   }
}

注意加上@MapperScan:

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

注意这次使⽤@MapperScan时指定⼀下mapper的路径,不然IT1Service也会被扫描到,最终⽣成⼀个 Mybais的代理对象的Bean,导致Controller中依赖注⼊的时候会产⽣问题:

Field it1Service in com.xiaoge.controller.T1Controller required a single
bean, but 2 were found:
 - t1ServiceImpl: defined in file [D:\IdeaProjects\springboot2.6.6demo\target\classes\com\xiaoge\service\impl\T1ServiceImpl.class]
 - IT1Service: defined in file [D:\IdeaProjects\springboot2.6.6demo\target\classes\com\xiaoge\service\IT1Service.class]

改成:

@SpringBootApplication
@MapperScan("com.xiaoge.mapper")
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}

并且把@Controller改成@RestController,即可进⾏测试

Spring Boot整合JPA

添加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
配置

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/xiaoge
    username: root
    password: xiaoge123456***
定义Entity
package com.xiaoge.entity;
import jakarta.persistence.*;
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String name;
    private Integer age;
    // getter setter
}
定义Dao
package com.xiaoge.dao;
import com.xiaoge.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User, Integer> {
}
使⽤
@Component
public class UserService {
    @Autowired
    private UserDao userDao;
    public List<User> test(){
        return userDao.findAll();
   }
}
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/test")
    public List<User> test(){
        return userService.test();
   }
}

启动项⽬时可能会报错

java.sql.SQLSyntaxErrorException: Table 'xiaoge.user' doesn't exist

要么我们⼿动创建user表,要么利⽤JPA⾃⼰创建,增加配置:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/xiaoge
    username: root
    password: xiaoge123456***
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: update

即可让JPA⾃动帮我们创建表,database表示使⽤什么数据库,⽽不是填tuling,update表示jpa如果发 现entity有属性改变会⾃动修改数据库。

如何我们想利⽤jpa执⾏⾃定义的sql,可以这么写:

//   @Query(value = "select * from user where name = ?", nativeQuery = true)
    @Query(value = "select u from User u where u.name = ?1")
    List<User> getByName(String name);

默认是执⾏的HQL,如果要执⾏原⽣sql,就nativeQuery = true

Spring Boot整合Redis

增加依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
添加配置
spring:
  redis:
    host: localhost
    port: 6379
    database: 0
使⽤RedisTemplate
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/test")
    public void test(){
        userService.test();
   }
}
@Component
public class UserService {
    @Autowired
    private RedisTemplate redisTemplate;
    public void test(){
        redisTemplate.opsForValue().set("xiaoge", 123);
   }
}

Spring Boot整合RocketMQ

搭建RocketMQ
  1. 下载:https://dlcdn.apache.org/rocketmq/4.9.3/rocketmq-all-4.9.3-bin-release.zip
  2. 解压
  3. 配置环境变量:
    1. ROCKETMQ_HOME=D:\soft\rocketmq-4.9.3
    2. NAMESRV_ADDR=localhost:9876
  4. cmd进⼊D:\soft\rocketmq-4.9.3⽬录
  5. 启动nameserver:bin\mqnamesrv.cmd
  6. 启动broker:bin\mqbroker.cmd -n localhost:9876 autoCreateTopicEnable=true
添加依赖
<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-spring-boot-starter</artifactId>
    <version>2.2.1</version>
</dependency>
⽣产者

配置

rocketmq:
  name-server: localhost:9876
  producer:
    group: my-group
@Component
public class UserService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    public void test(){
        rocketMQTemplate.syncSend("myTopic", "xiaoge");
   }
}
消费者
@Service
@RocketMQMessageListener(topic = "myTopic", consumerGroup = "mygroup")
public class MyConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String s) {
        System.out.println("接收到消息: " + s);
   }
}

Sprig Boot整合Swagger3

添加依赖
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>
添加@EnableOpenApi
@SpringBootApplication
@EnableOpenApi
public class XiaoGeApplication {
    public static void main(String[] args) {
        SpringApplication.run(XiaoGeApplication.class, args);
   }
}
启动应⽤

启动Spring Boot,即可访问swagger

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

A brief taste of JIFA

JIFA 是阿里贡献给 Eclipse 的一个适用于 Java 应用的问题诊断应用。 它以图形化的方式展示 Heap Dump AnalysisGC Log AnalysisThread Dump Analysis 因为云环境/生产环境的一些限制&#xff0c;应用的问题可能不能被就地分析&#xff0c;所以Jifa provides a web solution…

关于运动模糊问题的分析及处理方法

1、问题背景 前段时间有做一个化妆镜项目&#xff0c;就是一面镜子上装有一个摄像头&#xff0c;用户对着镜子化妆时&#xff0c;可同时用来采集人脸信息&#xff0c;分析人脸用的。客户反馈抓拍静止的人脸图像时&#xff0c;画面正常&#xff0c;而当人脸稍微运动时&#xff…

java springboot整合MyBatis实现分页查询以及带条件的分页查询

之前的文章 java springboot整合MyBatis做数据库查询操作操作了springboot整合MyBatis&#xff0c;然后简单做了个按id查询的操作 那么 我们按上文搭建起的环境继续 我们直接在staffDao接口中声明一个分页函数 Select("select * from staff limit #{page},#{pageSize}&q…

【Vue】Vite 组件化开发

文章目录 组件化开发一、组件化开发思想二、Vue 组件的构成2.1 组件组成结构2.2 组件 template 节点2.2.1 在 template 中使用指令2.2.2 在 template 中定义根节点 2.3 组件的 script 模板 三、组件的基本使用3.1 组件的注册3.1.1 全局组件注册3.1.2 局部组件注册 3.2 组件样式…

ffmpeg命令参数

主要参数 -i 设定输入流 -f 设定输出格式&#xff08;format&#xff09; -ss 开始时间 -t 时间长度视频参数 -vframes 设置要输出的视频帧数 -b 设定视频码率 -b:v 视频码率 -r 设定帧率 -s 设定画面的宽与高 -vn 不处理视频 -aspect aspect 设置横纵比4:3 或16:9 或1.333或…

软件项目管理 第三章软件项目的启动过程课后习题参考答案——主编:李冰、张桥珍、刘玉娥

第三章 软件项目的启动过程 课后习题参考答案 1.选择题 &#xff08;1&#xff09;乙方在项目初始阶段的主要任务不包含以下哪一项&#xff08;D&#xff09;。 A. 项目分析 B. 竞标 C. 合同签署 D. 合同管理 &#xff08;2&#xff09;项目章程中不…

Spring Boot Configuration Annotation Processor not configured 问题解决

目录 问题抛出&#xff1a; ConfigurationProperties 问题抛出&#xff1a; 进行Spring Boot配置文件部署时&#xff0c;发出警告Spring Boot Configuration Annotation Processor not configured&#xff0c;但是不影响运行。 不过有时候springboot项目出错了并且排除了所有…

【裸机开发】中断系统 —— 中断向量表(设置中断向量偏移的原因)

之前的LED驱动不存在中断&#xff0c;也就不包含中断的初始化。如果程序包含了中断&#xff0c;我们应还需要初始化哪些内容&#xff1f;要解决这个问题&#xff0c;我们需要先了解一个中断系统包含了哪些内容。 ① 中断向量表&#xff1a;描述中断对应的中断服务函数&#xf…

C++笔记之stack、queue、priority_queue、deque

code review! C笔记之stack、queue、priority_queue、deque 文章目录 C笔记之stack、queue、priority_queue、deque一.cppreference介绍1.stack2.queue3.priority_queue4.deque 二.容器适配器三.stack详解1.创建一个不包含任何元素的 stack 适配器&#xff0c;并采用默认的 de…

Debian 12中再次安装R软件

上篇博客&#xff08;地址&#xff1a;https://blog.csdn.net/my1114/article/details/131347147?spm1001.2014.3001.5501&#xff09;中所述的&#xff0c;在Debian12中按默认方式编译安装R软件&#xff0c;有一定的局限性。 如下图所示&#xff1a; 因此&#xff0c;本…

Redis入门(2)-字符串

String是Redis最基础、最常见的类型&#xff0c;string类型的value中可存放任意数据&#xff0c;包括数值型、二进制的图片、音频、视频、序列化对象等。一个String类型的value最大是512M. 1.getset k v 若key存在返回之前的值&#xff0c;若不存在返回nil 2.strlen key 返…

【面试】标准库相关题型(三)

文章目录 1. unordered_map底层实现原理1.1 散列表1.2 STL 中的 unordered_map 的实现1.3 unordered_map 2. 迭代器底层实现原理及种类2.1 主要作用2.2 底层原理2.3 迭代器类型属性 3. 迭代器失效3.1 容器类别3.2 失效情况3.3 C11容器类别 4. STL容器的线程安全4.1 背景知识4.2…

Git分支使用方法

目录 前言 一、查看可用分支 二、创建新分支 三、切换到新分支 四、在新分支上进行工作 五、提交更改 六、切换回主分支 前言 分支是指在同一个代码仓库中的不同版本线。它们可以被用来同时开展不同的开发任务、修复bug或实现新功能&#xff0c;而不会影响到主要的代码…

【数据结构】栈和队列的应用

&#x1f387;[数据结构]栈和队列的应用&#x1f387; &#x1f31f; 正式开始学习数据结构啦~此专栏作为学习过程中的记录&#x1f31f; 文章目录 &#x1f387;[数据结构]栈和队列的应用&#x1f387;&#x1f370;一.栈在括号表达式中的应用&#x1f680;1.原理&#x1f680…

Linux1.基础指令(上)

1.Linux系统可创建多个用户。 2.创建用户:adduser 用户名 设置密码:passwd 用户名 (系统会提示再次输入密码&#xff0c;注意密码不回显)。 3.删除用户首先要在root权限下&#xff0c;输入指令:userdel -r 用户名。 4.ls指令 ls -a(显示所有文件&#xff0c;包括隐藏文件) :…

【软件设计师暴击考点】计算机组成原理与体系结构高频考点暴击系列【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;软件…

【P3】kali 最强渗透工具 - metasploit(安装配置及使用教程详解)

文章目录 一、metasploit 是什么&#xff1f;二、metasploit 攻击 windows 操作系统三、metasploit 攻击永恒之蓝全流程四、metasploit 攻击成功后能做什么4.1、操作步骤4.2、攻击示例 五、msfvenom 绕过杀毒软件技巧5.1、捆绑木马5.2、加壳&#xff1a;压缩壳、加密壳5.2.1、T…

分片和一致性哈希

在设计大规模分布式系统时&#xff0c;你可能会遇到两个概念——分片&#xff08;sharding&#xff09;和一致性哈希&#xff08;consistent hashing&#xff09;。虽然我在网上找到了很多关于这些术语的解释&#xff0c;但它们让我感到有些困惑。我觉得分片和一致性哈希本质上…

Web网页制作-知识点(2)——常用文本标签、列表标签、表格标签、Form表单、块元素与行内元素(内联元素)

目录 常用文本标签 列表标签 有序列表 无序列表 定义列表 表格标签 表格组成与特点 表格标签 表格属性 ​​​合并表格单元格 Form表单 属性说明 表单元素 文本框 密码框 提交按钮 块元素与行内元素&#xff08;内联元素&#xff09; 内联元素和块级元素…

Flink JdbcSink.sink源码解析及常见问题

文章目录 源码入口我们看下flush方法干了什么flush方法至此走完了&#xff0c;但是什么时机写入的数据呐&#xff1f;补充总结&#xff1a; 常见问题1. 为什么会出现JdbcSink.sink方法插入Mysql无数据的情况&#xff1f;2. JdbcSink.sink写Phoenix无数据问题 参考 基于Flink 1.…