Java spring boot autoconfigure

Annotation Interface EnableAutoConfiguration

Enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined. For example, if you have tomcat-embedded.jar on your classpath you are likely to want a TomcatServletWebServerFactory (unless you have defined your own ServletWebServerFactory bean).

When using @SpringBootApplication , the auto-configuration of the context is automatically enabled and adding this annotation has therefore no additional effect.

Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration. You can always manually exclude() any configuration that you never want to apply (use excludeName() if you don’t have access to them). You can also exclude them through the spring.autoconfigure.exclude property. Auto-configuration is always applied after user-defined beans have been registered.

The package of the class that is annotated with @EnableAutoConfiguration , usually through @SpringBootApplication , has specific significance and is often used as a ‘default’. For example, it will be used when scanning for @Entity classes. It is generally recommended that you place @EnableAutoConfiguration (if you’re not using @SpringBootApplication ) in a root package so that all sub-packages and classes can be searched.

Auto-configuration classes are regular Spring @Configuration beans. They are located using ImportCandidates . Generally auto-configuration beans are @Conditional beans (most often using @ConditionalOnClass and @ConditionalOnMissingBean annotations).

Источник

Как работает Spring Boot Auto-Configuration

Глубокое погружение в мир аннотации Spring Boot @Conditional с проработанными примерами реализаций классов доступа к БД Mongo и MySQL.

Читайте также:  Python list all files in folder

В моем посте «Почему Spring Boot?» было рассмотрено создание Spring Boot приложения, из которого вы едва ли сможете понять, что происходит за кулисами. Возможно, вы хотите понять магию автоконфигурации Spring Boot.

Перед этим вы должны узнать о Spring аннотации @Conditional, от которой зависит вся магия автоконфигурации Spring Boot.

Изучение возможностей @Conditional

При разработке приложений на основе Spring может возникнуть необходимость условной регистрации bean-компонентов.

Например, вы можете зарегистрировать bean-компонент DataSource, указывающий на базу данных DEV при запуске приложения локально, и указывать на другую базу данных PRODUCTION при работе в рабочей среде.

Вы можете перенести параметры подключения к базе данных в файл properties и использовать файл, соответствующий среде, но вам нужно изменить конфигурацию всякий раз, когда вам нужно указать другую среду и создать приложение.

Для решения этой проблемы в Spring 3.1 была введена концепция Profiles (профиль). Вы можете зарегистрировать несколько бинов одного типа и связать их с одним или несколькими профилями. При запуске приложения вы можете активировать нужные профили и компоненты, связанные с активированными профилями, и только эти профили будут зарегистрированы.

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

Затем вы можете указать активный профиль, используя System Property —Dspring.profiles.active = DEV.

Этот подход работает для простых случаев, таких как включение или отключение регистрации компонентов на основе активированных профилей. Но если вы хотите зарегистрировать bean-компоненты, основанные на некоторой условной логике, тогда сам подход профилей недостаточен.

Чтобы обеспечить большую гибкость условной регистрации bean-компонентов Spring, Spring 4 ввел концепцию @Conditional. Используя подход @Conditional, вы можете зарегистрировать компонент, условно основанный на любом произвольном условии.

Например, вы можете зарегистрировать компонент, когда:

  • Определенный класс присутствует в classpath
  • Spring bean определенного типа еще не зарегистрирован в ApplicationContext
  • Определенный файл существует в местоположении
  • Конкретное значение свойства настраивается в файле конфигурации
  • Определенное системное свойство присутствует / отсутствует

Это всего лишь несколько примеров, и вы можете использовать любое условие, которое захотите.

Давайте посмотрим, как работает Spring’s @Conditional.

Предположим, у нас есть интерфейс UserDAO с методами для получения данных из хранилища данных. У нас есть две реализации интерфейса UserDAO, а именно JdbcUserDAO, который общается с базой данных MySQL, и MongoUserDAO, который общается с MongoDB.

Мы можем захотеть включить только один интерфейс JdbcUserDAO и MongoUserDAO на основе System Property, скажем, dbType.

Если приложение запускается с использованием команды: java -jar myapp.jar -DdbType = MySQL, то мы хотим включить JdbcUserDAO. В противном случае, если приложение запускается с использованием команды: java -jar myapp.jar -DdbType = MONGO, мы хотим включить MongoUserDAO.

Пусть реализации JdbcUserDAO и MongoUserDAO интерфейса UserDAO выглядят следующим образом:

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

Мы можем реализовать Condition MySQLDatabaseTypeCondition, позволяющий проверить, что System Property dbType равно «MYSQL» следующим образом:

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

Мы можем реализовать Condition MongoDBDatabaseTypeCondition, чтобы проверить, что System Property dbType равно «MONGODB» следующим образом:

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

Теперь мы можем условно настроить bean-компоненты JdbcUserDAO и MongoUserDAO, используя @Conditional следующим образом:

@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, тогда будет зарегистрирован только bean-компонент JdbcUserDAO.

Но если вы установите System property: -DdbType = MONGODB, то будет зарегистрирован только bean-компонент MongoUserDAO.

Теперь мы увидели, как условно зарегистрировать бин на основе System Property.

Предположим, что мы хотим зарегистрировать bean-компонент MongoUserDAO только в том случае, если в classpath доступен Java класс MongoDB драйвера «com.mongodb.Server«, если нет, мы хотим зарегистрировать bean-компонент JdbcUserDAO.

Для этого мы можем реализовать Condition для проверки наличия или отсутствия класса драйвера MongoDB «com.mongodb.Server» следующим образом:

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

Мы только что увидели, как зарегистрировать bean-компоненты условно на основании наличия или отсутствия класса в classpath.

Что если мы хотим зарегистрировать bean-компонент MongoUserDAO, только если другой Spring-компонент типа UserDAO уже не зарегистрирован.

Мы можем реализовать Condition, чтобы проверить, существует ли какой-либо bean-компонент определенного типа, следующим образом:

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

Что если мы хотим зарегистрировать bean-компонент MongoUserDAO, только если в файле конфигурации установлено свойство app.dbType = MONGO?

Мы можем реализовать интерфейс Condition следующим образом:

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

Мы только что увидели различные варианты реализации интерфейса Condition. Но есть еще более элегантный способ реализовать Condition с использованием Аннотаций. Вместо создания реализации Condition для MYSQL и MongoDB мы можем создать аннотацию DatabaseType следующим образом:

@Target(< ElementType.TYPE, ElementType.METHOD >) @Retention(RetentionPolicy.RUNTIME) @Conditional(DatabaseTypeCondition.class) public @interface DatabaseType

Затем мы можем реализовать класс DatabaseTypeCondition, чтобы использовать значение DatabaseType для определения включить или отключить регистрацию компонента следующим образом:

public class DatabaseTypeCondition implements Condition < @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) < Mapattributes = 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)); > >

Теперь мы можем использовать аннотацию @DatabaseType в наших определениях компонентов следующим образом:

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

Здесь мы получаем метаданные из аннотации DatabaseType и проверяем значение dbType свойства системы, чтобы определить, включить или отключить регистрацию компонента.

Мы увидели множество примеров, позволяющих понять, как мы можем зарегистрировать бины условно, используя аннотацию @Conditional.

Spring Boot широко использует функцию @Conditional для регистрации bean-компонентов условно на основе различных критериев.

Вы можете найти различные реализации интерфейса Condition, которые SpringBoot использует в классе org.springframework.boot.autoconfigure пакета spring-boot-autoconfigure-.jar.

Мы узнали как Spring Boot использует функцию @Conditional для условной проверки, регистрировать бин или нет.

Но что именно запускает механизм автоконфигурации?

Это то, что мы рассмотрим в следующем разделе.

Spring Boot AutoConfiguration

Ключ к волшебству автоконфигурации Spring Boot — аннотация @EnableAutoConfiguration. Обычно мы аннотируем наш класс, являющейся точкой входа в приложение, либо с помощью @SpringBootApplication, либо, если мы хотим настроить значения по умолчанию, мы можем использовать следующие аннотации:

@Configuration @EnableAutoConfiguration @ComponentScan public class Application

Аннотация @EnableAutoConfiguration включает автоматическую настройку Spring ApplicationContext путем сканирования компонентов пути к классам и регистрации бинов, соответствующих различным условиям.

Spring Boot предоставляет различные классы AutoConfiguration в spring-boot-autoconfigure-.jar, которые отвечают за регистрацию различных компонентов.

Обычно классы AutoConfiguration аннотируются @Configuration, чтобы пометить его как класс конфигурации Spring, и аннотируются @EnableConfigurationProperties для привязки свойств настройки и одного или нескольких методов регистрации условного компонента.

Например, рассмотрим класс 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 < @Bean public DataSourceInitializer dataSourceInitializer() < return new DataSourceInitializer(); >> @Conditional(DataSourceAutoConfiguration.NonEmbeddedDataSourceCondition.class) @ConditionalOnMissingBean(< DataSource.class, XADataSource.class >) protected static class NonEmbeddedConfiguration < @Autowired private 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 < @Bean public Object dataSourceMBean(DataSource dataSource) < . . >> . . >

Здесь DataSourceAutoConfiguration аннотируется @ConditionalOnClass(), что означает, что автоконфигурация bean-компонентов в DataSourceAutoConfiguration будет рассматриваться, только если классы DataSource.class и EmbeddedDatabaseType.class доступны в classpath.

Класс также аннотируется @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-компонентов, которые аннотируются условными аннотациями SpringBoot, такими как @ConditionalOnMissingBean, @ConditionalOnClass и @ConditionalOnProperty и т.д.

Эти определения bean-компонентов будут зарегистрированы в ApplicationContext, только если эти условия будут выполнены.

Вы также можете изучить многие другие классы автоконфигурации в spring-boot-autoconfigure-.jar, такие как:

  • 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.JacksonAutoConfigurationetc и т. д.

Я надеюсь, что теперь у вас есть понимание того, как работает автоконфигурация Spring Boot используя различные классы автоконфигурации вместе с функциями @Conditional.

Источник

Оцените статью