注解装配即在应用中扮演组件角色的注解,例如@Configuration,@Component.在SSM项目中我们一般通过 context:component-scan 进行装配,在springboot项目中则通过@ComponentScan
<!-- 激活注解驱动特性 --> <context:annotation-config /> <!-- 找寻被 @Component 或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean --> <context:component-scan base-package="com.springbootlearning.test" /> 复制代码
@ComponentScan(basePackages = "com.springbootlearning.test") public class TestApplication { ... } 复制代码
Spring Framework 注解 | 描述 |
---|---|
@Component | 通用组件模式注解 |
@Configuration | 配置类模式注解 |
@Controller | Web 控制器模式注解 |
@Service | 服务模式注解 |
@Repository | 数据仓储模式注解 |
简单比对@Service和@Controller,我们不能发现两者均为@Component的派生注解(值(此处为value)必须一致),只是被赋予了不同的含义,本质是一样的
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Service { @AliasFor( annotation = Component.class ) String value() default ""; } 复制代码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller { @AliasFor( annotation = Component.class ) String value() default ""; } 复制代码
@Enable 模块即具备相同领域的功能组件集合, 组合所形成一个独立 的单元。比如 Web MVC 模块、AspectJ代理模块等
框架 | @Enable 模块注解 | 相关模块 |
---|---|---|
Spring Framework | @EnableWebMvc | Web MVC 模块 |
@EnableTransactionManagement | 事务管理模块 | |
@EnableCaching | Caching 模块 | |
@EnableMBeanExport | JMX 模块 | |
@EnableAsync | 异步处理模块 | |
@EnableWebFlux | Web Flux 模块 | |
@EnableAspectJAutoProxy | AspectJ 代理模块 | |
Spring Boot | @EnableAutoConfiguration | 自动装配模块 |
@EnableManagementContext | Actuator 管理模块 | |
@EnableConfigurationProperties | 配置属性绑定模块 | |
@EnableOAuth2Sso | OAuth2 单点登录模块 | |
Spring Cloud | @EnableEurekaServer | Eureka服务器模块 |
@EnableConfigServer | 配置服务器模块 | |
@EnableFeignClients | Feign客户端模块 | |
@EnableZuulProxy | 服务网关 Zuul 模块 |
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { } 复制代码
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite(); public DelegatingWebMvcConfiguration() { } @Autowired( required = false ) public void setConfigurers(List<WebMvcConfigurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } } protected void configurePathMatch(PathMatchConfigurer configurer) { this.configurers.configurePathMatch(configurer); } protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) { this.configurers.configureContentNegotiation(configurer); } protected void configureAsyncSupport(AsyncSupportConfigurer configurer) { this.configurers.configureAsyncSupport(configurer); } protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { this.configurers.configureDefaultServletHandling(configurer); } protected void addFormatters(FormatterRegistry registry) { this.configurers.addFormatters(registry); } protected void addInterceptors(InterceptorRegistry registry) { this.configurers.addInterceptors(registry); } protected void addResourceHandlers(ResourceHandlerRegistry registry) { this.configurers.addResourceHandlers(registry); } protected void addCorsMappings(CorsRegistry registry) { this.configurers.addCorsMappings(registry); } protected void addViewControllers(ViewControllerRegistry registry) { this.configurers.addViewControllers(registry); } protected void configureViewResolvers(ViewResolverRegistry registry) { this.configurers.configureViewResolvers(registry); } protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { this.configurers.addArgumentResolvers(argumentResolvers); } protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) { this.configurers.addReturnValueHandlers(returnValueHandlers); } protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) { this.configurers.configureMessageConverters(converters); } protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { this.configurers.extendMessageConverters(converters); } protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { this.configurers.configureHandlerExceptionResolvers(exceptionResolvers); } protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) { this.configurers.extendHandlerExceptionResolvers(exceptionResolvers); } @Nullable protected Validator getValidator() { return this.configurers.getValidator(); } @Nullable protected MessageCodesResolver getMessageCodesResolver() { return this.configurers.getMessageCodesResolver(); } } 复制代码
自定义@Enable模块
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({MyServiceConfiguration.class}) public @interface EnableMyService { } 复制代码
@Configuration public class MyServiceConfiguration { @Bean public String test() { return "测试服务"; } } 复制代码
@EnableMyService public class TestApplication { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext = new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); } } 复制代码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Import({CachingConfigurationSelector.class}) public @interface EnableCaching { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default 2147483647; } 复制代码
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> { private static final String PROXY_JCACHE_CONFIGURATION_CLASS = "org.springframework.cache.jcache.config.ProxyJCacheConfiguration"; private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJCachingConfiguration"; private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJJCacheConfiguration"; private static final boolean jsr107Present = ClassUtils.isPresent("javax.cache.Cache", CachingConfigurationSelector.class.getClassLoader()); private static final boolean jcacheImplPresent = ClassUtils.isPresent("org.springframework.cache.jcache.config.ProxyJCacheConfiguration", CachingConfigurationSelector.class.getClassLoader()); public CachingConfigurationSelector() { } public String[] selectImports(AdviceMode adviceMode) { switch(adviceMode) { case PROXY: return this.getProxyImports(); case ASPECTJ: return this.getAspectJImports(); default: return null; } } private String[] getProxyImports() { List<String> result = new ArrayList(); result.add(AutoProxyRegistrar.class.getName()); result.add(ProxyCachingConfiguration.class.getName()); if (jsr107Present && jcacheImplPresent) { result.add("org.springframework.cache.jcache.config.ProxyJCacheConfiguration"); } return StringUtils.toStringArray(result); } private String[] getAspectJImports() { List<String> result = new ArrayList(); result.add("org.springframework.cache.aspectj.AspectJCachingConfiguration"); if (jsr107Present && jcacheImplPresent) { result.add("org.springframework.cache.aspectj.AspectJJCacheConfiguration"); } return StringUtils.toStringArray(result); } } 复制代码
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector { public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode"; public AdviceModeImportSelector() { } protected String getAdviceModeAttributeName() { return "mode"; } public final String[] selectImports(AnnotationMetadata importingClassMetadata) { Class<?> annType = GenericTypeResolver.resolveTypeArgument(this.getClass(), AdviceModeImportSelector.class); Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector"); AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType); if (attributes == null) { throw new IllegalArgumentException(String.format("@%s is not present on importing class '%s' as expected", annType.getSimpleName(), importingClassMetadata.getClassName())); } else { AdviceMode adviceMode = (AdviceMode)attributes.getEnum(this.getAdviceModeAttributeName()); String[] imports = this.selectImports(adviceMode); if (imports == null) { throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode)); } else { return imports; } } } @Nullable protected abstract String[] selectImports(AdviceMode var1); } 复制代码
public interface ImportSelector { String[] selectImports(AnnotationMetadata var1); } 复制代码
自定义@Enable模块
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import(MyServiceConfigurationSelector.class) public @interface EnableMyService { } 复制代码
public class MyServiceConfigurationSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[] {MyServiceConfiguration.class.getName()}; } } 复制代码
@EnableMyService public class TestApplication { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext = new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); configurableApplicationContext.close(); } } 复制代码
在例子中加入@Profile("dev")启动会发生空指针异常
@Configuration public class MyServiceConfiguration { @Bean @Profile("dev") public String test() { return "测试服务"; } } 复制代码
指定启动profile
@EnableMyService public class TestApplication { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext = new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).profiles("dev").run(args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); configurableApplicationContext.close(); } } 复制代码
@Profile也是基于@Conditional来实现的
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional({ProfileCondition.class}) public @interface Profile { String[] value(); } 复制代码
Condition
class ProfileCondition implements Condition { ProfileCondition() { } public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName()); if (attrs != null) { Iterator var4 = ((List)attrs.get("value")).iterator(); Object value; do { if (!var4.hasNext()) { return false; } value = var4.next(); } while(!context.getEnvironment().acceptsProfiles((String[])((String[])value))); return true; } else { return true; } } } 复制代码
模仿写一个,Conditional
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented @Conditional(OnSystemPropertyCondition.class) public @interface ConditionalOnSystemProperty { // Java 系统属性名称 String key(); // Java 系统属性值 String value(); } 复制代码
Condition
public class OnSystemPropertyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName()); String propertyName = String.valueOf(attributes.get("key")); String propertyValue = String.valueOf(attributes.get("value")); String javaPropertyValue = System.getProperty(propertyName); return propertyValue.equals(javaPropertyValue); } } 复制代码
@Configuration public class MyServiceConfiguration { @Bean @ConditionalOnSystemProperty(key = "user.name", value = "zhaozhihao") public String test() { return "测试服务"; } } 复制代码
@EnableMyService public class TestApplication { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext = new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); configurableApplicationContext.close(); } } 复制代码
public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class); private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap(); public SpringFactoriesLoader() { } public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) { Assert.notNull(factoryClass, "'factoryClass' must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List<T> result = new ArrayList(factoryNames.size()); Iterator var5 = factoryNames.iterator(); while(var5.hasNext()) { String factoryName = (String)var5.next(); result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse)); } AnnotationAwareOrderComparator.sort(result); return result; } public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue())); result.addAll((String)entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException var9) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9); } } } private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) { try { Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader); if (!factoryClass.isAssignableFrom(instanceClass)) { throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]"); } else { return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance(); } } catch (Throwable var4) { throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4); } } } 复制代码
# Initializers org.springframework.context.ApplicationContextInitializer=/ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,/ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=/ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=/ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=/ org.springframework.boot.autoconfigure.condition.OnClassCondition # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=/ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,/ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,/ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,/ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,/ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,/ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,/ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,/ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,/ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,/ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,/ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,/ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,/ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,/ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,/ org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,/ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,/ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,/ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,/ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,/ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,/ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,/ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,/ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,/ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,/ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,/ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,/ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,/ org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,/ org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,/ org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,/ org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,/ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,/ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,/ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,/ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,/ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,/ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,/ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,/ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,/ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,/ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,/ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,/ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,/ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,/ org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,/ org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,/ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,/ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,/ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,/ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,/ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,/ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,/ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,/ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,/ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,/ org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,/ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,/ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,/ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,/ org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,/ org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,/ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,/ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,/ org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,/ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,/ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,/ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,/ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,/ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,/ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,/ org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,/ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,/ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,/ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,/ org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,/ org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,/ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,/ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,/ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,/ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,/ org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=/ org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,/ org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,/ org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,/ org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer # Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=/ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,/ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,/ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,/ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,/ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider 复制代码
@EnableAutoConfiguration public class TestApplication { public static void main(String[] args) { ConfigurableApplicationContext configurableApplicationContext = new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); configurableApplicationContext.close(); } } 复制代码
@Configuration //Spring 模式注解装配 @EnableMyService //Spring @Enable 模块装配 @ConditionalOnSystemProperty(key = "user.name", value = "zhaozhihao") //Spring 条件装配 public class MyServiceAutoConfiguration { } 复制代码
节选自SpringApplication,从构造器中我们大概可以推断出准备阶段需要经过primarySources合规性校验、推断 Web 应用类型、加载应用上下文初始器、加载应用事件监听器、推断引导类等步骤。
public class SpringApplication { ... public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = this.deduceWebApplicationType(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); } ... } 复制代码
节选自SpringApplication,加载primarySources调用的是BeanDefinitionLoader
public class SpringApplication { ... protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); } ... } 复制代码
节选BeanDefinitionLoader,可见其可以通过xml/注解加载
class BeanDefinitionLoader { ... BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); this.xmlReader = new XmlBeanDefinitionReader(registry); if (this.isGroovyPresent()) { this.groovyReader = new GroovyBeanDefinitionReader(registry); } this.scanner = new ClassPathBeanDefinitionScanner(registry); this.scanner.addExcludeFilter(new BeanDefinitionLoader.ClassExcludeFilter(sources)); } ... } 复制代码
三种方式都是可以的,传入的source只要是@Component的派生注解就行了,@SpringBootApplication只是提供了一些默认配置的组合注解
@EnableAutoConfiguration public class TestApplication { public static void main(String[] args) { // 方式一 ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(TestApplication.class, args); String test = configurableApplicationContext.getBean("test", String.class); System.out.println(test); configurableApplicationContext.close(); // 方式二 // Set<String> sources = new HashSet<>(); // sources.add(TestApplication.class.getName()); // SpringApplication springApplication = new SpringApplication(); // springApplication.setSources(sources); // ConfigurableApplicationContext configurableApplicationContext = // springApplication.run(args); // String test = configurableApplicationContext.getBean("test", String.class); // System.out.println(test); // configurableApplicationContext.close(); // 方式三 // ConfigurableApplicationContext configurableApplicationContext = // new // SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args); // String test = configurableApplicationContext.getBean("test", String.class); // System.out.println(test); // configurableApplicationContext.close(); } } 复制代码
节选SpringApplication代码,推断逻辑在deduceWebApplicationType方法中,根据相关的类是否被装配来进行判断应用类型。显然源码提示REACTIVE和SERVLET共存的时候,优先SERVLET,所以在引入netty的时候要把tomcat排除出去。
public class SpringApplication { ... private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null)) { return WebApplicationType.REACTIVE; } else { String[] var1 = WEB_ENVIRONMENT_CLASSES; int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { String className = var1[var3]; if (!ClassUtils.isPresent(className, (ClassLoader)null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } } ... } 复制代码
除此之外,我们也可以通过配置强制选型
springApplication.setWebApplicationType(WebApplicationType.SERVLET); 复制代码
节选自SpringApplication,通过SpringFactoriesLoader加载META-INF/spring.factories中的bean,通过AnnotationAwareOrderComparator.sort()方法进行排序加载
public class SpringApplication { ... private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } ... } 复制代码
自定义MyFirstApplicationContextInitializer,通过实现Ordered接口进行排序
public class MyFirstApplicationContextInitializer implements ApplicationContextInitializer, Ordered { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { System.out.println("MyFirstApplicationContextInitializer"); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } } 复制代码
自定义MySecondApplicationContextInitializer,通过@Order注解进行排序
@Order(Ordered.LOWEST_PRECEDENCE) public class MySecondApplicationContextInitializer<C extends ConfigurableApplicationContext> implements ApplicationContextInitializer<C> { @Override public void initialize(C c) { System.out.println("MySecondApplicationContextInitializer"); } } 复制代码
META-INF/spring.factories,此处顺序和加载顺序无关
# Initializers org.springframework.context.ApplicationContextInitializer=/ com.springbootlearning.test.context.MySecondApplicationContextInitializer,/ com.springbootlearning.test.context.MyFirstApplicationContextInitializer 复制代码
执行
@SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class,args); } } 复制代码
效果
实现原理类似加载应用上下文初始器,只不过装配是要等待监听的事件发生后
@Order(Ordered.HIGHEST_PRECEDENCE) public class MyFirstApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { System.out.println("MyFirstApplicationListener"); } } 复制代码
public class MySecondApplicationListener implements ApplicationListener<ContextRefreshedEvent>, Ordered { @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { System.out.println("MySecondApplicationListener"); } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } } 复制代码
META-INF/spring.factories
# Application Listeners org.springframework.context.ApplicationListener=/ com.springbootlearning.test.listener.MyFirstApplicationListener,/ com.springbootlearning.test.listener.MySecondApplicationListener 复制代码
效果
节选SpringApplication代码,通过循环匹配栈内元素
public class SpringApplication { ... private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace(); StackTraceElement[] var2 = stackTrace; int var3 = stackTrace.length; for(int var4 = 0; var4 < var3; ++var4) { StackTraceElement stackTraceElement = var2[var4]; if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException var6) { ; } return null; } ... } 复制代码
节选SpringApplication,通过查看源码,我们大致可以发现整个过程:加载SpringApplicationRunListeners,然后开始运行,伴随着各大监听事件的发生,相应的监听器被激活,加载各自Bean,特别地environmentPrepared后开始加载context
public class SpringApplication { public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } } } 复制代码
利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } 复制代码
监听以下运行状态
public interface SpringApplicationRunListener { void starting(); void environmentPrepared(ConfigurableEnvironment environment); void contextPrepared(ConfigurableApplicationContext context); void contextLoaded(ConfigurableApplicationContext context); void started(ConfigurableApplicationContext context); void running(ConfigurableApplicationContext context); void failed(ConfigurableApplicationContext context, Throwable exception); } 复制代码
Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件 API ,广播 Spring Boot 事件(initialMulticaster)
# Run Listeners org.springframework.boot.SpringApplicationRunListener=/ org.springframework.boot.context.event.EventPublishingRunListener 复制代码
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); Iterator var3 = application.getListeners().iterator(); while(var3.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var3.next(); this.initialMulticaster.addApplicationListener(listener); } } public int getOrder() { return 0; } public void starting() { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } public void contextPrepared(ConfigurableApplicationContext context) { } public void contextLoaded(ConfigurableApplicationContext context) { ApplicationListener listener; for(Iterator var2 = this.application.getListeners().iterator(); var2.hasNext(); context.addApplicationListener(listener)) { listener = (ApplicationListener)var2.next(); if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware)listener).setApplicationContext(context); } } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); } public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); } public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { context.publishEvent(event); } else { if (context instanceof AbstractApplicationContext) { Iterator var4 = ((AbstractApplicationContext)context).getApplicationListeners().iterator(); while(var4.hasNext()) { ApplicationListener<?> listener = (ApplicationListener)var4.next(); this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new EventPublishingRunListener.LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } private static class LoggingErrorHandler implements ErrorHandler { private static Log logger = LogFactory.getLog(EventPublishingRunListener.class); private LoggingErrorHandler() { } public void handleError(Throwable throwable) { logger.warn("Error calling ApplicationEventListener", throwable); } } } 复制代码
代码
// 创建上下文 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 注册应用事件监听器 context.addApplicationListener(event -> { System.out.println("监听到事件: " + event); }); // 启动上下文 context.refresh(); // 发送事件PayloadApplicationEvent context.publishEvent("first"); context.publishEvent("second"); // 关闭上下文 context.close(); 复制代码
效果
MyRunListener
public class MyRunListener implements SpringApplicationRunListener { public MyRunListener(SpringApplication application, String[] args){} @Override public void starting() { System.out.println("1.MyRunListener starting"); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { System.out.println("2.MyRunListener environmentPrepared"); } @Override public void contextPrepared(ConfigurableApplicationContext context) { System.out.println("3.MyRunListener contextPrepared"); } @Override public void contextLoaded(ConfigurableApplicationContext context) { System.out.println("4.MyRunListener contextLoaded"); } @Override public void started(ConfigurableApplicationContext context) { System.out.println("5.MyRunListener started"); } @Override public void running(ConfigurableApplicationContext context) { System.out.println("6.MyRunListener running"); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { System.out.println("7.MyRunListener failed"); } } 复制代码
META-INF/spring.factorie
# Run Listeners org.springframework.boot.SpringApplicationRunListener=/ com.springbootlearning.test.run.MyRunListener 复制代码
引导类
@SpringBootApplication public class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class,args); } } 复制代码
效果
1.MyRunListener starting 2.MyRunListener environmentPrepared . ____ _ __ _ _ /// / ___'_ __ _ _(_)_ __ __ _ / / / / ( ( )/___ | '_ | '_| | '_ // _` | / / / / /// ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_/__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.0.1.RELEASE) MyFirstApplicationContextInitializer MySecondApplicationContextInitializer 3.MyRunListener contextPrepared 2019-12-08 23:02:15.139 INFO 3110 --- [ main] c.s.test.TestApplication : Starting TestApplication on zzh.local with PID 3110 (/Users/zhaozhihao/Documents/test/target/classes started by zhaozhihao in /Users/zhaozhihao/Documents/test) 2019-12-08 23:02:15.141 INFO 3110 --- [ main] c.s.test.TestApplication : No active profile set, falling back to default profiles: default 4.MyRunListener contextLoaded 2019-12-08 23:02:15.188 INFO 3110 --- [ main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5be6e01c: startup date [Sun Dec 08 23:02:15 CST 2019]; root of context hierarchy 2019-12-08 23:02:15.736 INFO 3110 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup MyFirstApplicationListener MySecondApplicationListener 2019-12-08 23:02:15.746 INFO 3110 --- [ main] c.s.test.TestApplication : Started TestApplication in 0.898 seconds (JVM running for 1.446) 5.MyRunListener started 6.MyRunListener running 2019-12-08 23:02:15.748 INFO 3110 --- [ Thread-4] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5be6e01c: startup date [Sun Dec 08 23:02:15 CST 2019]; root of context hierarchy 2019-12-08 23:02:15.749 INFO 3110 --- [ Thread-4] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown 复制代码
图和步骤转载自 原文连接
<servlet> <servlet-name>app</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/app-context.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>app</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> 复制代码
<context:component-scan base-package="com.test"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> 复制代码
Servlet 3.0+ & Spring Framework 3.1 +就可以实现注解装配,Servlet SPI 的ServletContainerInitializer 被 Spring Framework 封装为SpringServletContainerInitializer
扫描
@ComponentScan(basePackages = "com.test") public class DispatcherServletConfiguration { } 复制代码
WebMvcConfigurer
@Configuration @EnableWebMvc public class WebMvcConfig implements WebMvcConfigurer { @Bean public ViewResolver viewResolver(){ InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/jsp/"); viewResolver.setSuffix(".jsp"); return viewResolver; } } 复制代码
AbstractAnnotationConfigDispatcherServletInitializer
public class DefaultAnnotationConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { // web.xml return new Class[0]; } @Override protected Class<?>[] getServletConfigClasses() { // DispatcherServlet return new Class[]{DispatcherServletConfiguration.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } } 复制代码
Controller
@Controller public class HelloWorldController { @RequestMapping("/test") public String index() { return "index"; } } 复制代码
其他相关MVC注解
注解 | 描述 |
---|---|
@ModelAttribute | 注册模型属性 |
@RequestHeader | 读取请求头 |
@CookieValue | 读取Cookie |
@Valid | 校验参数 |
@Validated | 校验参数 |
@ExceptionHandler | 异常处理 |
@ControllerAdvice | 切面通知 |
DispatcherServletAutoConfiguration(spring.factories)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) @EnableConfigurationProperties(ServerProperties.class) public class DispatcherServletAutoConfiguration { ... } 复制代码
由源码可以分析出,装配DispatcherServletAutoConfiguration需要满足一些条件
WebMvcAutoConfiguration(spring.factories)
@Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { ... public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware { ... public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, @Lazy HttpMessageConverters messageConverters, ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) { this.resourceProperties = resourceProperties; this.mvcProperties = mvcProperties; this.beanFactory = beanFactory; this.messageConverters = messageConverters; this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider .getIfAvailable(); } ... @Configuration public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration { } ... } 复制代码
由源码可以分析出,装配WebMvcAutoConfiguration中有一个条件即@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),也就是说WebMvcConfigurationSupport在的时候就不会装配,而@EnableWebMvc注解的DelegatingWebMvcConfiguration就继承了WebMvcConfigurationSupport,所以当@EnableWebMvc存在的时候,我们可以自由定义,其实在WebMvcAutoConfiguration中也装配了继承DelegatingWebMvcConfiguration的EnableWebMvcConfiguration
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { } 复制代码
@Configuration public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { ... } 复制代码