SpringBoot 自动配置 Condition基础版转到:SpringBoot快速入门–基础版(包含整合常用框架、跨域)
Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作。
需求在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
- 导入Jedis坐标后,加载该Bean,没导入,则不加载。
- 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定。
创建Spring Boot项目,勾选Spring Web
①创建User类
package com.aiw.springbootcondition.pojo;
public class User {
}
②创建配置类
package com.aiw.springbootcondition.config;
import com.aiw.springbootcondition.condition.ClassCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import com.aiw.springbootcondition.pojo.User;
@Configuration // 标识为配置类
public class UserConfig {
@Bean
@Conditional(ClassCondition.class)
public User user() {
return new User();
}
}
③实现Condition接口类
package com.aiw.springbootcondition.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 需求:导入Jedis坐标后创建Bean
// 思路:判断redis.clients.jedis.Jedis文件是否存在
boolean flag = true;
try {
Class> cls = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
e.printStackTrace();
flag = false;
}
return flag;
}
}
④主程序
package com.aiw.springbootcondition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import com.aiw.springbootcondition.pojo.User;
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
// 启动Spring Boot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
Object user = context.getBean("user", User.class);
System.out.println(user);
}
}
若在pom.xml文件中导入了如下jedis坐标,则能获取到User Bean,否则获取不到
redis.clients jedis
目前到此,只实现了一半需求,继续完善如下,新建注解
package com.aiw.springbootcondition.condition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
String[] value();
}
修改ClassCondition.java
package com.aiw.springbootcondition.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// // 需求:导入Jedis坐标后创建Bean
// // 思路:判断redis.clients.jedis.Jedis文件是否存在
// boolean flag = true;
// try {
// Class> cls = Class.forName("redis.clients.jedis.Jedis");
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// flag = false;
// }
// return flag;
// 需求:导入通过注解属性值value指定坐标后创建Bean
// 获取注解属性值 value
MultiValueMap map = metadata.getAllAnnotationAttributes(ConditionOnClass.class.getName());
String[] value = (String[]) map.get("value").get(0);
boolean flag = true;
try {
for (String className : value) {
Class> cls = Class.forName(className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
flag = false;
}
return flag;
}
}
再修改UserConfig.java
package com.aiw.springbootcondition.config;
import com.aiw.springbootcondition.condition.ConditionOnClass;
import com.aiw.springbootcondition.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 标识为配置类
public class UserConfig {
@Bean
// @Conditional(ClassCondition.class)
@ConditionOnClass("redis.clients.jedis.Jedis")
public User user() {
return new User();
}
}
这样只有导入了Jedis坐标,才会创建Bean
Spring Boot已经提供了许多的ConditionalOnXxx注解,在Maven: org.springframework.boot:spring-boot-autoconfigure-2.7.1中,如下:
使用Spring Boot提供Conditional注解,测试如下,修改UserConfig.java
package com.aiw.springbootcondition.config;
import com.aiw.springbootcondition.pojo.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration // 标识为配置类
public class UserConfig {
@Bean
// @Conditional(ClassCondition.class)
// @ConditionOnClass("redis.clients.jedis.Jedis")
public User user() {
return new User();
}
@Bean
@ConditionalOnProperty(name = "Aiw", havingValue = "test")
public User user2() {
return new User();
}
}
修改主程序,如下:
package com.aiw.springbootcondition;
import com.aiw.springbootcondition.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
// 启动Spring Boot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
Object user = context.getBean("user2", User.class);
System.out.println(user);
}
}
修改配置文件application.yml
Aiw: test
启动项目,控制台输出如下:
若是在application.yml中找不到Aiw属性和其对应的值,则无法创建Bean
小结1、自定义条件:
① 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回 boolean值 。 matches 方法两个参数:
• context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
• metadata:元数据对象,用于获取注解属性。
② 判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解
2、SpringBoot 提供的常用条件注解:
• ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
• ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
• ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
切换内置web服务器SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器(Tomcat、Jetty、Netty、Undertow)供我们选择,我们可以很方便的进行切换。
在Maven: org.springframework.boot:spring-boot-autoconfigure-2.7.1中查看如下:
切换内置Web服务器,首先需要排除Tomcat
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat org.springframework.boot spring-boot-starter-jetty
刷新依赖,重启项目,控制台如下:
切换其它服务器,方法类似。
@Enable*注解SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注 解导入一些配置类,实现Bean的动态加载。
SpringBoot 工程是否可以直接获取jar包中定义的Bean?
答:不能。解决方法如下:
-
使用@ComponentScan扫描配置类所在的包
@ComponentScan("com.aiw.config") -
使用@Import注解,加载类;这些类都会被Spring创建,并放入IOC容器
@Import(Config.class)
-
对@Import注解进行封装
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(Config.class) public @interface EnableXxx{}
@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
① 导入Bean
修改主程序,如下:
package com.aiw.springbootimport;
import com.aiw.springbootimport.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(User.class)
public class SpringbootImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootImportApplication.class, args);
// User user = context.getBean(User.class); // 这种获取不到
User user = (User) context.getBean("user");
System.out.println(user);
}
}
使用这种方法,配置类上的@Configuration注解可以不需要了
② 导入配置类
创建配置类
package com.aiw.springbootimport.config;
import com.aiw.springbootimport.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
@Bean
public User user(){
return new User();
}
}
修改主程序,如下:
package com.aiw.springbootimport;
import com.aiw.springbootimport.config.UserConfig;
import com.aiw.springbootimport.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(UserConfig.class)
public class SpringbootImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootImportApplication.class, args);
User user = context.getBean(User.class);
// User user = (User) context.getBean("user"); // 这种也可以获取到
System.out.println(user);
}
}
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
创建实现类,如下:
package com.aiw.springbootimport.config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.aiw.springbootimport.pojo.User"};
}
}
字符串不是写死的,以后可以写到配置文件中去
修改主程序,如下:
package com.aiw.springbootimport;
import com.aiw.springbootimport.config.MyImportSelector;
import com.aiw.springbootimport.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(MyImportSelector.class)
public class SpringbootImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootImportApplication.class, args);
// User user = context.getBean(User.class); // 这种获取不到
User user = (User) context.getBean("user");
System.out.println(user);
}
}
运行结果:
④ 导入 ImportBeanDefinitionRegistrar 实现类。
创建实现类
package com.aiw.springbootimport.config;
import com.aiw.springbootimport.pojo.User;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();
registry.registerBeanDefinition("user",beanDefinition);
}
}
修改主程序,如下:
package com.aiw.springbootimport;
import com.aiw.springbootimport.config.MyImportBeanDefinitionRegistrar;
import com.aiw.springbootimport.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringbootImportApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringbootImportApplication.class, args);
// User user = context.getBean(User.class); // 这种也可以获取到
User user = (User) context.getBean("user");
System.out.println(user);
}
}
@EnableAutoConfiguration 注解若报错,则在配置文件中添加如下:
spring.main.allow-bean-definition-overriding=true
- @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类。
- 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean
- 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
需求:自定义redis-starter。要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean。
步骤:
- 创建 redis-spring-boot-autoconfigure 模块
- 创建 redis-spring-boot-starter 模块,依赖 redis-springboot-autoconfigure的模块
- 在 redis-spring-boot-autoconfigure 模块中初始化 Jedis 的 Bean。并定义META-INF/spring.factories 文件
- 在测试模块中引入自定义的 redis-starter 依赖,测试获取 Jedis 的Bean,操作 redis。
SpringBoot 的监听机制,其实是对Java提供的事件监听机制的封装。
Java中的事件监听机制定义了以下几个角色:
- 事件:Event,继承 java.util.EventObject 类的对象
- 事件源:Source ,任意对象Object
- 监听器:Listener,实现 java.util.EventListener 接口 的对象
SpringBoot 在项目启动时,会对几个监听器进行回调,我们可以实现这些监听器接口,在项目启动时完成 一些操作。
ApplicationContextInitializer、SpringApplicationRunListener、CommandLineRunner、ApplicationRunner
创建Spring Boot(2.7.2)工程,分别创建4个实现类:
MyApplicationContextInitializer.java
package com.aiw.springbootlistener.listener;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("ApplicationContextInitializer...initialize");
}
}
MySpringApplicationRunListener.java
package com.aiw.springbootlistener.listener;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.time.Duration;
public class MySpringApplicationRunListener implements SpringApplicationRunListener {
public MySpringApplicationRunListener(SpringApplication application, String[] args){}
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("starting...项目启动中");
}
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
System.out.println("environmentPrepared...环境对象开始准备");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("contextPrepared...上下文对象开始准备");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("contextLoaded...上下文对象开始加载");
}
@Override
public void started(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("started...上下文对象加载完成");
}
@Override
public void ready(ConfigurableApplicationContext context, Duration timeTaken) {
System.out.println("ready...项目启动完成,开始运行");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("failed...项目启动失败");
}
}
SpringApplicationRunListener的实现类必须提供构造方法,否则报错;并且不能使用@Component自动注入
MyCommandLineRunner.java
package com.aiw.springbootlistener.listener;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("CommandLineRunner...run");
System.out.println(Arrays.toString(args));
}
}
MyApplicationRunner.java
package com.aiw.springbootlistener.listener;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationRunner...run");
System.out.println(Arrays.toString(args.getSourceArgs()));
}
}
启动项目,目前是没有任何参数。如下:
点击环境配置->修改选项->勾选程序实参,输入name=Aiw如下:
再次启动项目,如下:
可以发现,始终有两个未生效;在resources目录下新建META-INF/spring.factories文件,内容如下:
org.springframework.context.ApplicationContextInitializer=com.aiw.springbootlistener.listener.MyApplicationContextInitializer org.springframework.boot.SpringApplicationRunListener=com.aiw.springbootlistener.listener.MySpringApplicationRunListener
键是对应接口的全路径名,值是自定义实现类的引用
再次启动项目,如下:
SpringBoot 启动流程分析 启动流程 替换默认banner将以下内容保存为banner.txt,放于src/main/resources/banner.txt
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O = /O
____/`---'____
. ' \| |// `.
/ \||| : |||//
/ _||||| -:- |||||-
| | \ - /// | |
| _| ''---/'' | |
.-__ `-` ___/-. /
___`. .' /--.-- `. . __
."" '< `.____<|>_/___.' >'"".
| | : `- `.;` _ /`;.`/ - ` : | |
`-. _ __ /__ _/ .-` / /
======`-.____`-.________/___.-`____.-'======
`=---='
.............................................
佛祖保佑 永无BUG
启动项目,可以看到默认banner已被替换。
SpringBoot 监控SpringBoot自带监控功能Actuator,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、配置属性 、日志信息等。
SpringBoot 监控使用① 导入依赖坐标
创建Spring Boot(2.7.2)项目,并勾选如下依赖:
也可以手动导入
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-actuator
② 启动项目,访问http://localhost:8080/actuator
访问http://localhost:8080/actuator/health,默认不显示详细信息
在配置文件中添加如下:
# 开启健康检查的完整信息 management.endpoint.health.show-details=always
重新启动项目,如下:
若引入redis坐标:
org.springframework.boot spring-boot-starter-data-redis
重新启动项目,如下:
再次修改配置文件
# 开启健康检查的完整信息 management.endpoint.health.show-details=always # 将所有的监控endpoint暴露出来 management.endpoints.web.exposure.include=*
重新启动项目,如下:
说明:
Spring Boot Admin-
Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。
-
Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
-
应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
-
Spring Boot Admin Server 的UI界面将Spring Boot Admin Client的Actuator Endpoint上的一些监控信息。
使用步骤
admin-server:
① 创建 admin-server 模块
② 导入依赖坐标 admin-starter-server
或者手动导入:
org.springframework.boot spring-boot-starter-web de.codecentric spring-boot-admin-starter-server
③ 在引导类上启用监控功能@EnableAdminServer
package com.aiw.springbootadminserver;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableAdminServer
@SpringBootApplication
public class SpringbootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAdminServerApplication.class, args);
}
}
在配置文件中添加如下:
server.port=9000
admin-client:
① 创建 admin-client 模块
② 导入依赖坐标 admin-starter-client
或者手动导入:
org.springframework.boot spring-boot-starter-web de.codecentric spring-boot-admin-starter-client
③ 配置相关信息:server地址等
在配置文件中添加如下:
# 执行admin.server地址 spring.boot.admin.client.url=http://localhost:9000 # 开启健康检查的完整信息 management.endpoint.health.show-details=always # 将所有的监控endpoint暴露出来 management.endpoints.web.exposure.include=*
④ 启动server和client服务,访问server
在浏览器输入http://localhost:9000/
点击在线项,页面如下:
IDEA也提供了快捷功能,如下:
SpringBoot 项目部署SpringBoot 项目开发完毕后,支持两种方式部署到服务器:
① jar包(官方推荐)
② war包
jar包新建SpringBoot(2.7.2)项目,勾选Web依赖坐标,并创建简单控制器,如下:
package com.aiw.springbootdeploy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/all", method = RequestMethod.GET)
public String all() {
return "success";
}
}
在IDEA中打开Maven界面,双击package
出现以下界面,则打包成功:
打包位置位于target目录下:
打开所在文件夹,如下:
按住shift+鼠标右键,点击在此处打开Powershell窗口,输入如下命令:
回车,即可启动服务:
在浏览器输入http://localhost:8080/user/all
war包修改pom.xml的打包方式为war
war
修改主程序,如下:
package com.aiw.springbootdeploy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class SpringbootDeployApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringbootDeployApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringbootDeployApplication.class);
}
}
排除内置的Tomcat
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat
在pom.xml声明使用外部Tomcat服务器
org.springframework.boot spring-boot-starter-tomcat provided
若觉得打包的名称不好看,可以指定名称,在pom.xml的build标签下添加finalName即可:
springboot org.springframework.boot spring-boot-maven-plugin
这样打包的名称就是springboot了
重新打包,如下:
将生成的war包放于外部的Tomcat的webapps目录下,如下:
启动外部Tomcat,双击startup.bat,运行项目之后Tomcat会自动解包,如下:
再在浏览器上访问http://localhost:8080会出现外部Tomcat的信息,如下:
此时再访问http://localhost:8080/user/all,则找不到路径,如下:
需要再加一层目录,也就是项目的打包名称,即http://localhost:8080/springboot/user/all
实测,不知为何加上项目名后,还是报404错误,难道是新版本有改动?
注意,使用war包方式,若还是在配置文件中修改端口地址,则是不生效的,因为此时war包是使用外部服务器;需要到外部服务器配置文件中去改。



