SpringBoot AutoConfiguration魔术如何工作?

在我以前的文章中, 为什么选择SpringBoot? 我们已经研究了如何创建SpringBoot应用程序。 但是您可能会也可能不会了解幕后发生的事情。 您可能想了解SpringBoot自动配置背后的魔力。

但是在此之前,您应该了解Spring的@Conditional功能,所有SpringBoot的AutoConfiguration魔术赖以其基础。

探索@Conditional的力量

在开发基于Spring的应用程序时,我们可能会遇到有条件地注册bean的需求。

例如,您可能想在本地运行应用程序时注册一个指向DEV数据库的DataSource bean,而在生产环境中运行时则指向一个不同的PRODUCTION数据库。

您可以将数据库连接参数外部化为属性文件,并使用适合于环境的文件。 但是,每当需要指向其他环境并构建应用程序时,都需要更改配置。

为了解决这个问题,Spring 3.1引入了Profiles的概念。 您可以注册多个相同类型的bean,并将它们与一个或多个概要文件关联。 当您运行该应用程序时,您可以激活所需的概要文件,并且仅与激活的概要文件关联的bean将被注册。

@Configuration
public class AppConfig
{@Bean@Profile("DEV")public DataSource devDataSource() {...}@Bean@Profile("PROD")public DataSource prodDataSource() {...}
}

然后,您可以使用系统属性-Dspring.profiles.active = DEV指定活动配置文件

这种方法适用于简单的情况,例如基于激活的配置文件启用或禁用bean注册。 但是,如果您要基于某些条件逻辑来注册bean,那么Profiles方法本身是不够的。

为了为有条件地注册Spring Bean提供更大的灵活性,Spring 4引入了@Conditional的概念。 通过使用@Conditional方法,您可以根据任意条件有条件地注册bean。

例如,在以下情况下,您可能要注册一个bean:

  • 一个特定的类存在于类路径中
  • 某种类型的Spring bean尚未在ApplicationContext中注册
  • 特定文件存在于某个位置
  • 在配置文件中配置特定的属性值
  • 存在/不存在特定的系统属性

这些仅是几个示例,您可以具有所需的任何条件。

让我们看一下Spring的@Conditional的工作原理。

假设我们有一个UserDAO接口,其中包含从数据存储中获取数据的方法。 我们UserDAO的接口即JdbcUserDAO两种工具进行对话的MySQL数据库和MongoUserDAO进行对话的MongoDB的

我们可能只想基于系统属性dbType启用JdbcUserDAOMongoUserDAO之一。

如果使用java -jar myapp.jar -DdbType = MySQL启动应用程序,则我们要启用JdbcUserDAO ;否则,如果使用java -jar myapp.jar -DdbType = MONGO启动应用程序, 则要启用MongoUserDAO

假设我们有UserDAO接口和JdbcUserDAOMongoUserDAO实现如下:

public interface UserDAO
{List<String> getAllUserNames();
}public class JdbcUserDAO implements UserDAO
{@Overridepublic List<String> getAllUserNames(){System.out.println("**** Getting usernames from RDBMS *****");return Arrays.asList("Siva","Prasad","Reddy");}
}public class MongoUserDAO implements UserDAO
{@Overridepublic List<String> getAllUserNames(){System.out.println("**** Getting usernames from MongoDB *****");return Arrays.asList("Bond","James","Bond");}
}

我们可以实现条件MySQLDatabaseTypeCondition来检查系统属性dbType是否为“ MYSQL” ,如下所示:

public class MySQLDatabaseTypeCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){String enabledDBType = System.getProperty("dbType");return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MYSQL"));}
}

我们可以实现条件MongoDBDatabaseTypeCondition来检查系统属性dbType是否为“ MONGODB ”,如下所示:

public class MongoDBDatabaseTypeCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){String enabledDBType = System.getProperty("dbType");return (enabledDBType != null && enabledDBType.equalsIgnoreCase("MONGODB"));}
}

现在,我们可以使用@Conditional有条件地配置JdbcUserDAOMongoUserDAO Bean,如下所示:

@Configuration
public class AppConfig
{@Bean@Conditional(MySQLDatabaseTypeCondition.class)public UserDAO jdbcUserDAO(){return new JdbcUserDAO();}@Bean@Conditional(MongoDBDatabaseTypeCondition.class)public UserDAO mongoUserDAO(){return new MongoUserDAO();}
}

如果我们像java -jar myapp.jar -DdbType = MYSQL那样运行应用程序,则仅JdbcUserDAO bean将被注册。 但是,如果您-DdbType = MONGODB,则仅MongoUserDAO Bean将被注册。

现在,我们已经看到了如何基于系统属性有条件地注册bean。

假设我们希望,注册MongoUserDAO豆只有当MongoDB的 Java驱动程序类“com.mongodb.Server”可在类路径中,如果不是我们想注册JdbcUserDAO豆。

为此,我们可以创建条件来检查MongoDB驱动程序类“ com.mongodb.Server”的存在与否,如下所示:

public class MongoDriverPresentsCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){try {Class.forName("com.mongodb.Server");return true;} catch (ClassNotFoundException e) {return false;}}
}public class MongoDriverNotPresentsCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){try {Class.forName("com.mongodb.Server");return false;} catch (ClassNotFoundException e) {return true;}}
}

我们刚刚看到了如何根据类路径中是否存在类来有条件地注册bean。

如果仅在尚未注册其他类型为UserDAO的 Spring Bean的情况下才想注册MongoUserDAO Bean,该怎么办?

我们可以创建一个条件来检查是否存在某种特定类型的现有bean,如下所示:

public class UserDAOBeanNotPresentsCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata){UserDAO userDAO = conditionContext.getBeanFactory().getBean(UserDAO.class);return (userDAO == null);}
}

如果仅在属性占位符配置文件中设置属性app.dbType = MONGO时才想注册MongoUserDAO bean,该怎么办

我们可以如下实现该条件:

public class MongoDbTypePropertyCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){String dbType = conditionContext.getEnvironment().getProperty("app.dbType");return "MONGO".equalsIgnoreCase(dbType);}
}

我们刚刚看到了如何实现各种类型的条件。 但是,还有使用注释来实现条件的更优雅的方法。 除了为MYSQL和MongoDB创建Condition实现之外,我们还可以创建一个DatabaseType注释,如下所示:

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Conditional(DatabaseTypeCondition.class)
public @interface DatabaseType
{String value();
}

然后,我们可以实现DatabaseTypeCondition以使用DatabaseType值来确定是启用还是禁用bean注册,如下所示:

public class DatabaseTypeCondition implements Condition
{@Overridepublic boolean matches(ConditionContext conditionContext,AnnotatedTypeMetadata metadata){Map<String, Object> attributes = metadata.getAnnotationAttributes(DatabaseType.class.getName());String type = (String) attributes.get("value");String enabledDBType = System.getProperty("dbType","MYSQL");return (enabledDBType != null && type != null && enabledDBType.equalsIgnoreCase(type));}
}

现在,我们可以在bean定义上使用@DatabaseType批注,如下所示:

@Configuration
@ComponentScan
public class AppConfig
{@DatabaseType("MYSQL")public UserDAO jdbcUserDAO(){return new JdbcUserDAO();}@Bean@DatabaseType("MONGO")public UserDAO mongoUserDAO(){return new MongoUserDAO();}
}

在这里,我们从DatabaseType批注中获取元数据,并对照System Property dbType值进行检查,以确定是启用还是禁用Bean注册。

我们已经看到了很多示例,以了解如何使用@Conditional批注有条件地注册bean。

SpringBoot广泛使用@Conditional功能根据各种条件有条件地注册bean。

您可以在spring-boot-autoconfigure- {version} .jar的 org.springframework.boot.autoconfigure包中找到SpringBoot使用的各种Condition实现。

现在,我们了解了SpringBoot如何使用@Conditional功能有条件地检查是否注册Bean。 但是究竟是什么触发了自动配置机制呢?

这就是我们将在下一部分中讨论的内容。

SpringBoot自动配置

SpringBoot自动配置魔术的关键是@EnableAutoConfiguration批注。 通常,我们使用@SpringBootApplication注释应用程序入口点类,或者如果要自定义默认值,则可以使用以下注释:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application
{}

@EnableAutoConfiguration批注通过扫描类路径组件并注册与各种条件匹配的bean来启用Spring ApplicationContext的自动配置。

SpringBoot在spring-boot-autoconfigure- {version} .jar中提供了各种AutoConfiguration类,这些类负责注册各种组件。

通常, AutoConfiguration类使用@Configuration注释,以将其标记为Spring配置类,并使用@EnableConfigurationProperties注释,以绑定定制属性和一个或多个条件Bean注册方法。

例如,考虑org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类。

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration 
{......@Conditional(DataSourceAutoConfiguration.EmbeddedDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import(EmbeddedDataSourceConfiguration.class)protected static class EmbeddedConfiguration {}@Configuration@ConditionalOnMissingBean(DataSourceInitializer.class)protected static class DataSourceInitializerConfiguration {@Beanpublic DataSourceInitializer dataSourceInitializer() {return new DataSourceInitializer();}}@Conditional(DataSourceAutoConfiguration.NonEmbeddedDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })protected static class NonEmbeddedConfiguration {@Autowiredprivate DataSourceProperties properties;@Bean@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)public DataSource dataSource() {DataSourceBuilder factory = DataSourceBuilder.create(this.properties.getClassLoader()).driverClassName(this.properties.getDriverClassName()).url(this.properties.getUrl()).username(this.properties.getUsername()).password(this.properties.getPassword());if (this.properties.getType() != null) {factory.type(this.properties.getType());}return factory.build();}}......@Configuration@ConditionalOnProperty(prefix = "spring.datasource", name = "jmx-enabled")@ConditionalOnClass(name = "org.apache.tomcat.jdbc.pool.DataSourceProxy")@Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)@ConditionalOnMissingBean(name = "dataSourceMBean")protected static class TomcatDataSourceJmxConfiguration {@Beanpublic Object dataSourceMBean(DataSource dataSource) {........}}......
}

此处, DataSourceAutoConfiguration带有@ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class})注释,这意味着仅当在类路径上有DataSource.classEmbeddedDatabaseType.class类可用时,才会考虑在DataSourceAutoConfiguration中对bean进行自动配置。

该类还带有@EnableConfigurationProperties(DataSourceProperties.class)批注,该启用了自动将application.properties中的属性绑定到DataSourceProperties类的属性的功能。

@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public class DataSourceProperties implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {public static final String PREFIX = "spring.datasource";......private String driverClassName;private String url;private String username;private String password;...//setters and getters
}

使用此配置,所有以spring.datasource。*开头的属性都将自动绑定到DataSourceProperties对象。

spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

您还可以看到一些内部类和bean定义方法,这些内部类和bean定义方法用SpringBoot的条件注释(例如@ ConditionalOnMissingBean,@ ConditionalOnClass和@ConditionalOnProperty等)进行注释。

仅当这些条件匹配时,这些Bean定义才会在ApplicationContext中注册。

您还可以在spring-boot-autoconfigure- {version} .jar中探索许多其他AutoConfiguration类,例如

  • org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration
  • org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
  • org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration
  • org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration等。

我希望您现在通过使用各种AutoConfiration类以及@Conditional功能来了解SpringBoot自动配置的工作方式。

翻译自: https://www.javacodegeeks.com/2016/03/springboot-autoconfiguration-magic-works.html

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

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

相关文章

linux常用网络命令详解,linux网络命令详解(鸟哥)

[rootlinux ~]# tcpdump [-nn] [-i 介面] [-w 儲存檔名] [-c 次數] [-Ae][-qX] [-r 檔案] [所欲擷取的資料內容]參數&#xff1a;-nn&#xff1a;直接以 IP 及 port number 顯示&#xff0c;而非主機名與服務名稱-i &#xff1a;後面接要『監聽』的網路介面&#xff0c;例如 et…

Bzoj2694/Bzoj4659:莫比乌斯反演

Bzoj2694/Bzoj4659:莫比乌斯反演先上题面:首先看到这数据范围显然是反演了&#xff0c;然而第三个限制条件十分不可做。于是我们暂且无视他&#xff0c;大不了补集转化算完再减是吧。于是我们有:这里我们定义:于是这个东西我们可以nlogn筛的说。也就是说&#xff0c;我们求出f的…

linux系统嵌入式编译环境,Ubuntu 12.04嵌入式交叉编译环境arm-linux-gcc搭建过程图解...

Linux版本&#xff1a;Ubuntu 12.04 内核版本&#xff1a;Linux 3.5.0 交叉编译器版本&#xff1a;arm-linux-gcc-4.4.3 交叉编译器下载 见这篇文章http://www.linuxidc.com/Linux/2011-05/35906.htm安装前的絮叨首先简单介绍一下&#xff0c;所谓的搭建交叉编译环境&#xff0…

JUnit 5 –下一代JUnit的初步了解

2月初&#xff0c; JUnit 5&#xff08;又名JUnit Lambda&#xff09;团队发布了一个alpha版本。 由于JUnit 4是我工具箱中使用最频繁的项目之一&#xff0c;因此我认为值得一看下一个主要版本。 我试用了最新版本&#xff0c;并记下了我在这里发现值得注意的更改。 安装JUni…

Geany——Python配置

Geany是一个很不错的编辑器&#xff0c;操作很简单&#xff0c;这里记录一下Geany的入手设置&#xff08;在下是一个Python程序猿&#xff0c;就以Python为例&#xff09;&#xff1a; 1&#xff1a;新建&#xff1a;选择 下拉菜单中的 main.py &#xff0c;然后就能生成Pyth…

linux权限drwx,linux权限基础知识详解

祥哥今天整理一下Linux系统中的权限到底是什么&#xff1f;什么是775&#xff1f;什么又是777&#xff1f;664又代表了什么&#xff1f;1.查看权限可以使用ls -l命令ls -l我们以root文件夹为例来说明&#xff1a;drwx------.2 root rootd:这个代表是目录&#xff0c;也就是文件…

cargo maven_用于集成测试的Maven Cargo插件

cargo maven在项目生命周期中&#xff0c;非常普遍的需求是设置集成测试。 幸运的是&#xff0c;Maven在默认构建生命周期的以下阶段&#xff08;来自Maven 文档 &#xff09;具有对这一确切方案的内置支持&#xff1a; 集成前测试 &#xff1a; 执行集成测试之前所需的操作。…

linux菜单系统,Linux修改grub菜单

1. 保留上一次 grub 菜单选项1.1 问题每次开机时&#xff0c;大部分 Linux 发行版的 grub 菜单都是定位在首选项位置(即当前系统选项位置)。这就导致如果我们电脑安装了多个系统&#xff0c;那么每次开机进入其他系统都要重新选择 grub 菜单选项。而我们一般都是一段时间固定使…

zookeeper zoo.cfg配置文件

一、zookeeper的配置文件 zoo.cfg 配置文件是我们安装zookeeper的时候复制 重命名出来的文件命令&#xff1a; cp zoo_smaple.cfg zoo.cfgzkServer.sh 获取执行进入zookeeper 查看配置文件cd /myapp/zookeeper/conf执行命令 查看配置文件信息命令&#xff1a;vim zoo.cfg这是…

与Spring和Maven签订合约优先SOAP服务

1.简介 在本教程中&#xff0c;我们将学习使用JAX-WS&#xff0c;Spring和Maven实施合同优先的SOAP服务应用程序。 这是使用合同优先还是代码优先方法的更多设计决定。 在开发基于SOAP的Web服务应用程序时使用应用合同优先的方法最显着的好处是&#xff0c;可以在对合同进行必…

linux 下c内存管理,linux内存管理之malloc

对于内核的内存管理&#xff0c;像kmalloc&#xff0c;vmalloc&#xff0c;kmap&#xff0c;ioremap等比较熟悉。而对用户层的管理机制不是很熟悉&#xff0c;下面就从malloc的实现入手.( 这里不探讨linux系统调用的实现机制. ) ,参考了《深入理解计算机系统》和一些网上的资料…

jqGrid 常用方法

方法名参数返回值说明addJSONDatadatanone使用传来的data数据填充表格。使用方法&#xff1a; var mygrid jQuery(”#”grid_id)[0]; var myjsongrid eval(”(”jsonresponse.responseText”)”); mygrid.addJSONData(myjsongrid); myjsongrid null; jsonresponse null;addR…

新生必会的linux命令,jstat命令详解

导读Jstat是JDK自带的一个轻量级小工具。全称“Java Virtual Machine statistics monitoring tool”&#xff0c;它位于java的bin目录下&#xff0c;主要利用JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控&#xff0c;包括了对Heap size和垃圾回收状况的监控…

基于jsf的项目_JSF基于事件的交流:新派方法

基于jsf的项目在上一篇文章中 &#xff0c;我们学习了基于Observer / Event Listener和Mediator模式的基于事件的通信。 由于它们的缺点&#xff0c;我想展示基于事件的通信的更有效方法。 我们将从Google Guava EventBus开始&#xff0c;最后以CDI &#xff08;Java EE平台的上…

linux 天文软件,新闻|开源新闻速递:天文软件 Stellarium 0.15.0 发布

今日关注Stellarium 0.15.0 发布。这是一款全世界最棒的免费、开源、跨平台的天文软件应用&#xff0c;用户可以通过该软件来观看实时的星星、行星还有星云。最新版本进行了非常多的功能完善&#xff0c;修复了若干bug&#xff0c;新增了许多新特性。比如更新了AstroCalc工具&a…

linux javaweb环境单价,linux(centos)下Java Web环境开发

一、安装jdk百度搜索jdk&#xff0c;进入http://www.oracle.com/technetwork/java/javase/downloads/index.html找到自己需要版本的jdk的Linux压缩包&#xff1b;复制出这个压缩包的下载地址(尽量先点击下载&#xff0c;然后在下载的界面复制出资源的链接)在服务器的合适位置创…

带有Java Util日志记录的Java 8延迟调用

在博客文章“在Log4j2中更好地执行非日志记录器调用”中 &#xff0c;我介绍了可以在Log4j 2中使用的方法&#xff0c;这些方法可以减少或避免在基于指定日志级别实际上根本未记录的日志语句中调用方法。 作为讨论的一部分&#xff0c;我介绍了Log4j 2对使用lambda表达式的基于…

python对象分类

1 python对象分类 所有的Python对象都拥有三个特性&#xff1a;身份&#xff0c;类型和值 身份&#xff1a; 每一个对象都有一个唯一的身份标识自己。任何对象的身份可以使用内建函数id()来得到。这个值可以被认为是该对象的内存地址类型&#xff1a; 对象的类型决定了对象可以…

怎样用u盘linux安装ntp协议,电脑中怎么配置NTP服务

NTP服务器是用来使计算机时间同步化的一种协议&#xff0c;可提供高精准度的时间校正&#xff0c;而且能通过加密确认来防止恶毒的协议攻击。下面让学习啦小编为大家介绍如何在电脑中配置NTP服务来实现局域网内设备的时间同步。电脑中怎么配置NTP服务1、先关闭Windows系统自带的…

SetGID 权限

一、SetGID针对文件的作用 1、只有可执行的二进制程序才能设置SGID权限 2、命令执行者要对该程序拥有 x &#xff08;执行&#xff09;权限 3、命令执行在执行程序的时候&#xff0c;组身份升级为该程序的属组 4、SetGID权限同样只在该程序执行过程中有效&#xff0c;也就是说组…