007-Spring Boot @Enable*註解的工作原理
一、@Enable* 啟用某個特性的註解
1、EnableConfigurationProperties
回顧屬性裝配
application.properties中添加
tomcat.host=192.168.2.1
tomcat.port=8080
增加屬性類TomcatProperties
package com.lhx.spring.springboot_enable; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefixView Code= "tomcat") public class TomcatProperties { private String host; private String port; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPort() { return port; }public void setPort(String port) { this.port = port; } @Override public String toString() { return "TomcatProperties [host=" + host + ", port=" + port + "]"; } }
在App中添加代碼
@SpringBootApplication public class App { public static void main(String[] args) { ConfigurableApplicationContext context= SpringApplication.run(App.class, args); System.out.println(context.getBean(TomcatProperties.class)); context.close(); } }
查看註解SpringBootApplication,其中使其起作用的是@EnableAutoConfiguration,繼續追蹤代碼,其實生效的是:@EnableConfigurationProperties
故以上代碼配置
@ComponentScan @EnableConfigurationProperties public class App { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); System.out.println(context.getBean(TomcatProperties.class)); context.close(); } }
註解:是用來啟用一個特性的,特性可以把配置文件的屬性註入到Bean類中,一般和@ConfigurationProperties一起使用
2、EnableAsync
增加一個異步打印類
package com.lhx.spring.springboot_enable; import java.util.concurrent.TimeUnit; import org.springframework.stereotype.Component; @Component public class Jeep implements Runnable { @Override public void run() { try { for (int i = 0; i < 10; i++) { System.out.println("---------------"+i); TimeUnit.SECONDS.sleep(1); } } catch (Exception e) { e.printStackTrace(); } } }View Code
在App中調用
@ComponentScan @EnableConfigurationProperties public class App { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App.class, args); System.out.println(context.getBean(TomcatProperties.class)); context.getBean(Runnable.class).run(); System.out.println("------end------"); context.close(); } }View Code
發現此時並沒有異步執行
需要在異步打印類中啟用@EnableAsync,並在具體方法上標註@Async即可
package com.lhx.spring.springboot_enable; import java.util.concurrent.TimeUnit; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.stereotype.Component; @Component @EnableAsync public class Jeep implements Runnable { @Override @Async public void run() { try { for (int i = 0; i < 10; i++) { System.out.println("---------------"+i); TimeUnit.SECONDS.sleep(1); } } catch (Exception e) { e.printStackTrace(); } } }View Code
這時在調用App方法即可異步
註解:啟用異步,一般和Async一起使用
二、原理
1、註解Import
跟蹤EnableAsync或EnableConfigurationProperties進入發現,共同註解@Import。
/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Indicates one or more {@link Configuration @Configuration} classes to import. * * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML. * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}). * * <p>{@code @Bean} definitions declared in imported {@code @Configuration} classes should be * accessed by using {@link org.springframework.beans.factory.annotation.Autowired @Autowired} * injection. Either the bean itself can be autowired, or the configuration class instance * declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly * navigation between {@code @Configuration} class methods. * * <p>May be declared at the class level or as a meta-annotation. * * <p>If XML or other non-{@code @Configuration} bean definition resources need to be * imported, use the {@link ImportResource @ImportResource} annotation instead. * * @author Chris Beams * @author Juergen Hoeller * @since 3.0 * @see Configuration * @see ImportSelector * @see ImportResource */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { /** * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar} * or regular component classes to import. */ Class<?>[] value(); }View Code
import作用:Indicates one or more {@link Configuration @Configuration} classes to import.
用來導入一個或多個類(bean被spring容器托管)、或者配置類(配置類裏面的Bean都會被spring容器托管)
Enable*其實就是使用了Import,Import其實就是導入了配置類
示例
新建一個User類
package com.lhx.spring.springboot_enable; public class User { }View Code
新建一個Role類
package com.lhx.spring.springboot_enable; public class Role { }View Code
使用Import在App中導入
package com.lhx.spring.springboot_enable; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; @ComponentScan @Import(User.class) public class App2 { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App2.class, args); System.out.println(context.getBean(User.class)); System.out.println(context.getBean(Role.class)); context.close(); } }View Code
可以看到導入一個,另一個會失敗
也可以導入配置類
package com.lhx.spring.springboot_enable; import org.springframework.context.annotation.Bean; public class MyConfiguration { @Bean public Runnable createRunnable() { return () -> { }; } @Bean public Runnable createRunnable2() { return () -> { }; } }View Code
導入
@Import({User.class,Role.class,MyConfiguration.class})
2、ImportSelector接口
作用:方法selectImports的返回值,必須是一個class(全稱),改class會被Spring容器托管
/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import org.springframework.core.type.AnnotationMetadata; /** * Interface to be implemented by types that determine which @{@link Configuration} * class(es) should be imported based on a given selection criteria, usually one or more * annotation attributes. * * <p>An {@link ImportSelector} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective * methods will be called prior to {@link #selectImports}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li> * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li> * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li> * </ul> * * <p>ImportSelectors are usually processed in the same way as regular {@code @Import} * annotations, however, it is also possible to defer selection of imports until all * {@code @Configuration} classes have been processed (see {@link DeferredImportSelector} * for details). * * @author Chris Beams * @since 3.1 * @see DeferredImportSelector * @see Import * @see ImportBeanDefinitionRegistrar * @see Configuration */ public interface ImportSelector { /** * Select and return the names of which class(es) should be imported based on * the {@link AnnotationMetadata} of the importing @{@link Configuration} class. */ String[] selectImports(AnnotationMetadata importingClassMetadata); }View Code
示例
創建一個MyImportSelector
package com.lhx.spring.springboot_enable; 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.lhx.spring.springboot_enable.User",Role.class.getName(),MyConfiguration.class.getName()}; } }View Code
在App中使用
package com.lhx.spring.springboot_enable; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; @ComponentScan @Import(MyImportSelector.class) public class App3 { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App3.class, args); System.out.println(context.getBean(User.class)); System.out.println(context.getBean(Role.class)); System.out.println(context.getBeansOfType(Runnable.class)); context.close(); } }View Code
增加一個Enable開頭的註解,可以結合註解使用
增加一個註解EnableLog
package com.lhx.spring.springboot_enable; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(MyImportSelector2.class) public @interface EnableLog { String name(); }View Code
增加一個ImportSelector實現MyImportSelector2
package com.lhx.spring.springboot_enable; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector2 implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { System.out.println(importingClassMetadata.getAllAnnotationAttributes(EnableLog.class.getName())); return new String[] { "com.lhx.spring.springboot_enable.User", Role.class.getName(), MyConfiguration.class.getName() }; } }View Code
使用
package com.lhx.spring.springboot_enable; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; @ComponentScan @EnableLog(name="my annon") public class App4 { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(App4.class, args); System.out.println(context.getBean(User.class)); System.out.println(context.getBean(Role.class)); System.out.println(context.getBeansOfType(Runnable.class)); context.close(); } }View Code
3、ImportBeanDefinitionRegistrar
作用:方法的參數有一個BeanDefinitionRegistry,BeanDefinitionRegistry可以用來網Spring容器中註入Bean,如此,就可以動態註入bean
/* * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.annotation; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.core.type.AnnotationMetadata; /** * Interface to be implemented by types that register additional bean definitions when * processing @{@link Configuration} classes. Useful when operating at the bean definition * level (as opposed to {@code @Bean} method/instance level) is desired or necessary. * * <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type * may be provided to the @{@link Import} annotation (or may also be returned from an * {@code ImportSelector}). * * <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following * {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective * methods will be called prior to {@link #registerBeanDefinitions}: * <ul> * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li> * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware} * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware} * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware} * </ul> * * <p>See implementations and associated unit tests for usage examples. * * @author Chris Beams * @since 3.1 * @see Import * @see ImportSelector * @see Configuration */ public interface ImportBeanDefinitionRegistrar { /** * Register bean definitions as necessary based on the given annotation metadata of * the importing {@code @Configuration} class. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be * registered here, due to lifecycle constraints related to {@code @Configuration} * class processing. * @param importingClassMetadata annotation metadata of the importing class * @param registry current bean definition registry */ public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }View Code
自定義實現MyImportBeanDefinitionRegistrar
package com.lhx.spring.springboot_enable; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; 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) { registry.registerBeanDefinition("user", BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition()); registry.registerBeanDefinition("role", BeanDefinitionBuilder.rootBeanDefinition(Role.class).getBeanDefinition()); registry.registerBeanDefinition(MyConfiguration.class.getName(), BeanDefinitionBuilder.rootBeanDefinition(MyConfiguration.class).getBeanDefinition()); } }View Code
此時就可以在EnableLog上使用
package com.lhx.spring.springboot_enable; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented //@Import(MyImportSelector2.class) @Import(MyImportBeanDefinitionRegistrar.class) public @interface EnableLog { String name(); }View Code
示例
代碼地址:https://github.com/bjlhx15/spring-boot.git 中的 springboot-enable即可
007-Spring Boot @Enable*註解的工作原理