Spring Boot 详细分析Conditional自动化配置注解

目录
  • 1. Spring Boot Condition功能与作用
  • 2. Conditional条件化系列注解介绍
  • 3. Conditional条件化注解的实现原理
  • 4. Conditional核心之matches匹配接口
  • 5. Conditional核心之条件化注解具体实现
  • 6. 总结

1. Spring Boot Condition功能与作用

@Conditional是基于条件的自动化配置注解, 由Spring 4框架推出的新特性。

在一个服务工程, 通常会存在多个配置环境, 比如常见的DEV(开发环境)、SIT(系统内部集成测试环境)、UAT(用户验收测试环境)、PRD(生产环境)等。在Spring3系列版本中通过@Profile实现,传入对应的环境标识, 系统自动加载不同环境的配置。spring4版本正式推出Condition功能, 在spring5版本, @Profile做了改进,底层是通过Condition实现, 看下Condition接口的UML结构:

可以看到两个抽象类应用实现了Condition接口, 一个是Spring Context下的ProfileCondition, 另一个就是SpringBootCondition。

SpringBootCondition下面有很多实现类,也是满足Spring

Boot的各种Condition需要, 图中只是列出了部分实现, 每个实现类下面, 都会有对应的注解来协助处理。

2. Conditional条件化系列注解介绍

Conditional的注解 Conditional的处理类 Conditional的说明
@ConditionalOnBean OnBeanCondition Spring容器中是否存在对应的实例。可以通过实例的类型、类名、注解、昵称去容器中查找(可以配置从当前容器中查找或者父容器中查找或者两者一起查找)
@ConditionalOnClass OnClassCondition 类加载器中是否存在对应的类。可以通过Class指定(value属性)或者Class的全名指定(name属性)如果是多个类或者多个类名的话,关系是”与”关系,也就是说这些类或者类名都必须同时在类加载器中存在
@ConditionalOnExpression OnExpressionCondition 判断SpEL 表达式是否成立
@ConditionalOnMissingBean OnBeanCondition Spring容器中是否缺少对应的实例。可以通过实例的类型、类名、注解、昵称去容器中查找(可以配置从当前容器中查找或者父容器中查找或者两者一起查找)
@ConditionalOnMissingClass OnClassCondition 跟ConditionalOnClass的处理逻辑一样,只是条件相反,在类加载器中不存在对应的类
@ConditionalOnProperty OnPropertyCondition 应用环境中的屬性是否存在。提供prefix、name、havingValue以及matchIfMissing属性。prefix表示属性名的前缀,name是属性名,havingValue是具体的属性值,matchIfMissing是个boolean值,如果属性不存在,这个matchIfMissing为true的话,会继续验证下去,否则属性不存在的话直接就相当于匹配不成功
@ConditionalOnResource OnResourceCondition 是否存在指定的资源文件。只有一个属性resources,是个String数组。会从类加载器中去查询对应的资源文件是否存在
@ConditionalOnSingleCandidate OnBeanCondition Spring容器中是否存在且只存在一个对应的实例。只有3个属性value、type、search。跟ConditionalOnBean中的这3种属性值意义一样
@ConditionalOnWebApplication OnWebApplicationCondition 应用程序是否是Web程序,没有提供属性,只是一个标识。会从判断Web程序特有的类是否存在,环境是否是Servlet环境,容器是否是Web容器等

SpringBootCondition下面包含的主要条件化注解说明:

  • @ConditionalOnBean: 当Spring容器存在某个Bean则触发实现。
  • @ConditionalOnMissingBean: 当Spring容器不存在某个Bean则不触发。
  • @ConditionalOnSingleCandidate: 当Spring容器中只有一个指定Bean,或者多个时是首选 Bean。
  • @ConditionalOnClass: 当环境路径下有指定的类, 则触发实现。
  • @ConditionalOnMissingClass: 当环境路径下没有指定类则不触发实现。
  • @ConditionalOnProperty: 判断属性如果存在指定的值则触发实现。
  • @ConditionalOnResource: 判断存在指定的资源则触发实现。
  • @ConditionalOnExpression: 基于 某个SpEL 表达式作判断实现。
  • @ConditionalOnJava:基于JDK的版本作判断实现。
  • @ConditionalOnJndi:基于指定的 JNDI 作判断实现。
  • @ConditionalOnNotWebApplication:判断当前项目定义如果不是 Web 应用则不触发实现。
  • @ConditionalOnWebApplication:判断当前项目定义如果是 Web 应用则触发实现。

它们内部都是基于@Conditional实现。

3. Conditional条件化注解的实现原理

上面看到, Spring Boot 有很多内置的多条件化注解, 都是基于@Conditional实现,

那么@Conditionnal又是如何实现? 它的作用范围是什么? 是如何生效的?

Conditional源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
	/**
	 * contion条件的具体实现类, 必须实现Condition接口
	 */
	Class<? extends Condition>[] value();
}

@Target标示它的作用范围是在类或方法上。它是如何被调用生效的? 我们来写下测试类, 进行调试,

分析调用栈。

自定义Conditional

创建com.mirson.spring.boot.research.condition.CustomerMatchCondition

@Log4j2
public class CustomerMatchCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        log.info("Process in CustomerMatchCondition.matches method. ");
        return false;
    }
}

创建引用该Condition的配置类,

com.mirson.spring.boot.research.startup.CusomterConditional

@Configuration
@Conditional(CustomerMatchCondition.class)
@Log4j2
public class CusomterConditional {
    public Object newObj() {
        log.info("Process in CusomterConditional.newObj method.");
        return new Object();
    }
}

启动调试,分析调用栈:

可以看到, 先从第一步调用refresh调用容器初始化,再到第二步处理Bean配置定义信息, 最后调用注解的doScan扫描方法,这样就能够找到我们自定义的CustomerMatchCondition,调用Condtion定义的matches接口实现, 决定是否要执行CustomerConditional 的newObject方法。

4. Conditional核心之matches匹配接口

matchs方法是做规则校验处理, SpringBootCondition源码:

public abstract class SpringBootCondition implements Condition {
	private final Log logger = LogFactory.getLog(getClass());
	@Override
	public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 根据注解信息, 获取类或方法名称
		String classOrMethodName = getClassOrMethodName(metadata);
		try {
            // 获取实现类的处理匹配结果
			ConditionOutcome outcome = getMatchOutcome(context, metadata);
            // 日志打印匹配结果
			logOutcome(classOrMethodName, outcome);
            // ConditionEvaluationReport中记录处理结果信息
			recordEvaluation(context, classOrMethodName, outcome);
			return outcome.isMatch();
		}
		catch (NoClassDefFoundError ex) {
			throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
					+ ex.getMessage() + " not " + "found. Make sure your own configuration does not rely on "
					+ "that class. This can also happen if you are "
					+ "@ComponentScanning a springframework package (e.g. if you "
					+ "put a @ComponentScan in the default package by mistake)", ex);
		}
		catch (RuntimeException ex) {
			throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
		}
	}
    ...
}
  • 获取使用了Conditional的类或方法名称信息。
  • 根据Conditional条件规则判断, 获取返回处理结果。
  • 判断是否开启日志记录功能,打印处理结果。
  • 记录处理结果至ConditionEvaluationReport的outcomes属性中。最后返回布尔值的处理结果。它是通过 ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法调用, 可以看到它是在Bean创建之前就先调用,归属Bean配置定义信息的逻辑处理,且在validate方法之前处理。调用机制要理解清楚,我们管理配置。

5. Conditional核心之条件化注解具体实现

以ConditionalOnBean为例, 进行分析, 源码:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
    ...
}

采用Conditional注解, 具体条件判断逻辑在OnBeanCondition类中实现, 源码:

@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
	/**
	 * Bean definition attribute name for factory beans to signal their product type (if
	 * known and it can't be deduced from the factory bean class).
	 */
	public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;
	@Override
	public ConfigurationPhase getConfigurationPhase() {
		return ConfigurationPhase.REGISTER_BEAN;
	}
   ...
}

OnBeanCondition类的作用是判断容器中有无指定的Bean实例, 如果存在, 则条件生效。

它实现了抽象类FilteringSpringBootCondition的getOutcomes方法,同时实现了SpringBootCondition的getMatchOutcome方法, 两个核心方法接口,一个是获取定义的匹配条件,一个是返回匹配的结果信息, OnBeanCondition子类去实现具体的判断逻辑, 根据定义的条件输出判断结果。

getOutcomes方法

方法源码:

@Override
	protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
			AutoConfigurationMetadata autoConfigurationMetadata) {
		// 创建数组, 记录自动化配置的类信息
        ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
        // 遍历处理
		for (int i = 0; i < outcomes.length; i++) {
			String autoConfigurationClass = autoConfigurationClasses[i];
			if (autoConfigurationClass != null) {
                // 获取具有ConditionalOnBean注解设置的Bean
				Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
                // 记录outcomes, 条件配置信息
				outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
				if (outcomes[i] == null) {
                    // 为空, 则降级获取ConditionalOnSingleCandidate配置信息
					Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
							"ConditionalOnSingleCandidate");
					outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
				}
			}
		}
		return outcomes;
	}

该方法作用是扫描在META-INF的spring.factories文件中定义的配置类, 检测是否包含对应的条件标注,

也就是是否使用了@OnBeanCondition标注,存在则会记录, 进入后续方法逻辑处理。

可以看到, 通过outcomes数组来记录所有采用了Conditional的Autoconfiguration配置类。

扩展分析:

我们讲解的OnBeanCondition只是其中一个条件注解, 跟踪代码分析, 同组的还有OnClassConditional和OnWebApplicationCondition条件注解,启动处理顺序是:

OnClassConditional->OnWebApplicationCondition->OnBeanCondition,

spring.factories中大部份配置的Autoconfiguration都是采用OnClassConditional来作依赖类的条件判断。

getMatchOutcomes方法

@Override
	public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
		ConditionMessage matchMessage = ConditionMessage.empty();
        // 判断注解类型, ConditionalOnBean处理逻辑
		if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
			BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnBean.class);
			MatchResult matchResult = getMatchingBeans(context, spec);
			if (!matchResult.isAllMatched()) {
				String reason = createOnBeanNoMatchReason(matchResult);
				return ConditionOutcome						.noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, spec).because(reason));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec).found("bean", "beans")
					.items(Style.QUOTE, matchResult.getNamesOfAllMatches());
		}
        // ConditionalOnSingleCandidate注解处理逻辑
		if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
			BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
					ConditionalOnSingleCandidate.class);
			MatchResult matchResult = getMatchingBeans(context, spec);
			if (!matchResult.isAllMatched()) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec)
						.didNotFind("any beans").atAll());
			}
			else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
					spec.getStrategy() == SearchStrategy.ALL)) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, spec)
						.didNotFind("a primary bean from beans")
						.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, spec)
					.found("a primary bean from beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches());
		}
        // ConditionalOnMissingBean注解处理逻辑
		if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
			BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
			MatchResult matchResult = getMatchingBeans(context, spec);
			if (matchResult.isAnyMatched()) {
				String reason = createOnMissingBeanNoMatchReason(matchResult);
				return ConditionOutcome
						.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, spec).because(reason));
			}
			matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec).didNotFind("any beans")
					.atAll();
		}
		return ConditionOutcome.match(matchMessage);
	}

上面的getOutcomes方法记录了需要匹配处理的条目,该方法是作具体判断实现。 这里支持三种条件注解: ConditionalOnBean、ConditionalOnSingleCandidate和ConditionalOnMissingBean。实际内部逻辑都会调用getMatchingBeans方法。处理完成之后, 返回ConditionMessage对象,最后通过ConditionOutcome包装返回处理结果。

getMatchingBeans方法

该方法是做具体检测是否符合条件注解所配置的信息,主要包含三种类型判断,

一种是Bean Type 也就是class类型, 第二种是annotation标注, 最后一种是Name属性判断。

protected final MatchResult getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 判断bean的搜寻策略, ANCESTORS为搜索所有父容器的上下文定义
		if (beans.getStrategy() == SearchStrategy.ANCESTORS) {
			BeanFactory parent = beanFactory.getParentBeanFactory();
			Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
            // 父容器转换
			beanFactory = (ConfigurableListableBeanFactory) parent;
		}
		MatchResult matchResult = new MatchResult();
        // 判断bean的搜寻策略, 是否为CURRENT当前上下文
		boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
		TypeExtractor typeExtractor = beans.getTypeExtractor(context.getClassLoader());
		List<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(beans.getIgnoredTypes(), typeExtractor,
				beanFactory, context, considerHierarchy);
        // 根据bean的类型遍历判断是否符合规则
		for (String type : beans.getTypes()) {
            // type类型的具体处理逻辑, 内部为嵌套调用
			Collection<String> typeMatches = getBeanNamesForType(beanFactory, type, typeExtractor,
					context.getClassLoader(), considerHierarchy);
			typeMatches.removeAll(beansIgnoredByType);
			if (typeMatches.isEmpty()) {
				matchResult.recordUnmatchedType(type);
			}
			else {
				matchResult.recordMatchedType(type, typeMatches);
			}
		}
        // 根据bean的注解遍历判断是否符合规则
		for (String annotation : beans.getAnnotations()) {
			List<String> annotationMatches = Arrays.asList(
                    // Annotation类型的具体处理逻辑, 内部为嵌套调用
					getBeanNamesForAnnotation(beanFactory, annotation, context.getClassLoader(), considerHierarchy));
			annotationMatches.removeAll(beansIgnoredByType);
			if (annotationMatches.isEmpty()) {
				matchResult.recordUnmatchedAnnotation(annotation);
			}
			else {
				matchResult.recordMatchedAnnotation(annotation, annotationMatches);
			}
		}
        // 根据bean的名称遍历判断是否符合规则
		for (String beanName : beans.getNames()) {
			if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
				matchResult.recordMatchedName(beanName);
			}
			else {
				matchResult.recordUnmatchedName(beanName);
			}
		}
		return matchResult;
	}

1) 首先会判断搜寻策略,是否需要搜寻父容器上下文, 支持三种模式,CURRENT: 当前上下文; ANCESTORS: 所有父容器的上下文定义; ALL: 就是支持以上两种搜寻策略。

2) 其次就是根据注解的定义信息, 按三种方式进行判断, 内部按这三种, 类型、注解和名称做处理,如果是父级搜索,会采用递归调用, 检测是否存在, 进行匹配判断。方法调用层级:

getBeanNamesForType(…) -》collectBeanNamesForType(…)

getBeanNamesForAnnotation(…) -》collectBeanNamesForAnnotation(…)

以上就是以ConditionalOnBean为例, 对ConditionOnXXX的实现原理做了剖析, SpringBootCondition的其他实现类还有很多, 本章只抽取代表性常见的条件注解作分析,大家有兴趣可再研究其他条件注解的实现机制, 这里就不一一例举。

6. 总结

基于Conditional条件的自动化配置, 从SpringBootCondition实现原理到OnBeanCondition、AutoConfigurationImportFilter的剖析, 综合可以看出Spring Boot对于条件化注解的实现, 无论从层次结构, 还是内部逻辑处理的关联性, 都比较清晰明了,值得借鉴的是它的良好的扩展性设计,比如策略模式, 模板模式等,抽象类的合理运用设计, 没有出现接口泛滥, 强耦合性等问题, 也便于Spring Boot后续版本的功能扩展。

到此这篇关于Spring Boot 详细分析Conditional自动化配置注解的文章就介绍到这了,更多相关Spring Boot Conditional内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Jenkins自动化部署springboot代码实例

    一.linux按jar包名称部署 1.执行shell PID=$(ps -ef | grep app.jar | grep -v grep | awk '{ print $2 }') if [ -z "$PID" ] then echo Application is already stopped else echo kill $PID kill $PID fi 2.调用顶层maven目标 clean package -Dmaven.test.skip=true 3.执行shell c

  • Spring Boot中自动化配置的利弊以及解决方法

    本文主要给大家介绍了关于Spring Boot自动化配置的利弊及解决方法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: Spring Boot中的双刃剑:自动化配置 在之前的文章中,我们通过各种功能性示例体验了Spring Boot的自动化配置给我们所带来的超便利的新开发方式.但是,在一些情况下Spring Boot的自动化配置也会给我们惹来不少的麻烦,比如这些场景: 项目依赖复杂的情况下,由于依赖方的依赖组织不够严格,可能引入了一些实际我们不需要的依赖,从而导致我们

  • Spring Boot 深入分析AutoConfigurationImportFilter自动化条件配置源码

    目录 1. AutoConfigurationImportFilter的作用 2. AutoConfigurationImportFilter UML类图说明 3. FilteringSpringBootCondition抽象类 4. AutoConfigurationImportSelector类 5. 总结 1. AutoConfigurationImportFilter的作用 之前讲解了SpringBoot的Conditional的自动化条件配置,我们分析了内部是如何具体实现,在整个实现当

  • 阿里SpringBoot应用自动化部署实现IDEA版Jenkins

    目录 CloudToolkit简介 安装 使用 自动化部署 常用功能 总结 之前分享过一些使用Jenkins进行自动化部署的技巧 ,最近发现一款阿里出品的IDEA插件CloudToolkit,不仅支持直接打包应用部署到远程服务器上,而且还能当终端工具使用.试用了一把这个插件,非常不错,推荐给大家!装上这个插件,IDEA一站式开发又近了一步! SpringBoot实战电商项目mall(50k+star)地址:github.com/macrozheng/… CloudToolkit简介 CloudT

  • Spring Boot示例分析讲解自动化装配机制核心注解

    目录 1. 自动化装配介绍 2. Spring Boot 自动化配置UML图解 3. Spring Boot 自动化配置核心注解分析 3.1 @Inherited 3.2 @SpringBootConfiguration 3.3 @EnableAutoConfiguration 3.4 @ComponentScan 3.5 @ConfigurationPropertiesScan 3.6 @AutoConfigurationImportSelector 3.7 @AutoConfiguratio

  • 浅析SpringBoot自动化配置原理实现

    绪论 什么是SpringBoot? Spring Boot 是所有基于 Spring 开发的项目的起点.Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件.简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架(不知道这样比喻是否合适). SpringBoot四个主要特性 SpringBoot Starter:他将常用的依赖分组进行了整

  • Spring Boot 详细分析Conditional自动化配置注解

    目录 1. Spring Boot Condition功能与作用 2. Conditional条件化系列注解介绍 3. Conditional条件化注解的实现原理 4. Conditional核心之matches匹配接口 5. Conditional核心之条件化注解具体实现 6. 总结 1. Spring Boot Condition功能与作用 @Conditional是基于条件的自动化配置注解, 由Spring 4框架推出的新特性. 在一个服务工程, 通常会存在多个配置环境, 比如常见的DEV

  • 详解Spring Boot中如何自定义SpringMVC配置

    目录 前言 一.SpringBoot 中 SpringMVC 配置概述 二.WebMvcConfigurerAdapter 抽象类 三.WebMvcConfigurer 接口 四.WebMvcConfigurationSupport 类-自定义配置 五.WebMvcAutoConfiguration 配置类 – 自动化配置 六.@EnableWebMvc 注解 七.总结 前言 在 Spring Boot 框架中只需要在项目中引入 spring-boot-starter-web 依赖,Spring

  • Spring Boot实战教程之自动配置详解

    前言 大家应该都有所了解,随着Ruby.Groovy等动态语言的流行,相比较之下Java的开发显得格外笨重.繁多的配置.低下的开发效率.复杂的部署流程以及第三方技术集成难度大等问题一直被人们所诟病.随着Spring家族中的新星Spring Boot的诞生,这些问题都在逐渐被解决. 个人觉得Spring Boot中最重要的两个优势就是可以使用starter简化依赖配置和Spring的自动配置.下面这篇文章将给大家详细介绍Spring Boot自动配置的相关内容,话不多说,来一起看看详细的介绍. 使

  • 详解Spring Boot最核心的27个注解,你了解多少?

    导读  Spring Boot方式的项目开发已经逐步成为Java应用开发领域的主流框架,它不仅可以方便地创建生产级的Spring应用程序,还能轻松地通过一些注解配置与目前比较火热的微服务框架SpringCloud集成. 而Spring Boot之所以能够轻松地实现应用的创建及与其他框架快速集成,最核心的原因就在于它极大地简化了项目的配置,最大化地实现了"约定大于配置"的原则.然而基于Spring Boot虽然极大地方便了开发,但是也很容易让人"云里雾里",特别是各种

  • spring boot中的properties参数配置详解

    application.properties application.properties是spring boot默认的配置文件,spring boot默认会在以下两个路径搜索并加载这个文件 src\main\resources src\main\resources\config 配置系统参数 在application.properties中可配置一些系统参数,spring boot会自动加载这个参数到相应的功能,如下 #端口,默认为8080 server.port=80 #访问路径,默认为/

  • 关于Spring Boot WebSocket整合以及nginx配置详解

    前言 本文主要给大家介绍了关于Spring Boot WebSocket整合及nginx配置的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一:Spring Boot WebSocket整合 创建一个maven项目,加入如下依赖 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId>

  • Spring Boot使用Druid和监控配置方法

    Spring Boot默认的数据源是:org.apache.tomcat.jdbc.pool.DataSource Druid是Java语言中最好的数据库连接池,并且能够提供强大的监控和扩展功能. 下面来说明如何在 Spring Boot 中配置使用Druid (1)添加Maven依赖 (或jar包)\ <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId&g

  • 在Spring Boot中加载XML配置的完整步骤

    开篇 在SpringBoot中我们通常都是基于注解来开发的,实话说其实这个功能比较鸡肋,但是,SpringBoot中还是能做到的.所以用不用是一回事,会不会又是另外一回事. 涛锅锅在个人能力能掌握的范围之内,一般是会得越多越好,都是细小的积累,发生质的改变,所以今天和小伙伴们一起分享一下. 实践 1.首先我们新建一个SpringBoot Project ,工程名为 xml 2.添加web依赖,点击Finish完成构建 3.我们新建一个类 SayHello 不做任何配置 package org.t

  • 解决spring boot启动扫描不到自定义注解的问题

    对于自定义注解这里就不唠叨了,百度一大堆,这里有我一个自定义注解 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface MsgEvent { RetailOrderEvent msgEvent(); } 注解实现类 @Component public class MsgEventProcessor implements BeanPostProcessor { /** * 事件消息

随机推荐