1.java configuration及WebApplicationInitializer
The following example of the Java configuration registers and initializes the DispatcherServlet, which is auto-detected by the Servlet container (see Servlet Config)--来自spring官网
//首先实现WebApplicationInitializer接口中onStartUp()方法,容器启动时候会调用该方法
public class MyWebApplicationInitializer implements WebApplicationInitializer {
//容器为什么会调用onStartup()方法
//servlet 3.0 提出了spi规范,一个项目自定义的WebApplicationInitializer如果想被容器调用(tomcat),
//只需要在项目中meta-inf/service目录下定义一个文件,
//文件名必须为javax.servlet.ServletContainerInitializer,
//该名字为一个接口,文件内容为实现该接口的类,
//Spring实现该接口的类的名称为org.springframework.web.SpringServletContainerInitializer,spring项目中该文件内容也为该实现类的全路径。
//该接口为servlet中定义的,容器实现该接口,Spring也实现该接口,以此达到容器(tomcat)启动,
//能够调用到自定义的WebApplicationInitializer
@Override
public void onStartup(ServletContext servletCxt) {
// Load Spring web application configuration
//spring 容器对象
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
//AppConfig类为配置
ac.register(AppConfig.class);
ac.refresh();
//spring容器配置完成
// Create and register the DispatcherServlet
//设置spring mvc
DispatcherServlet servlet = new DispatcherServlet(ac);
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
//设置为优先启动
registration.setLoadOnStartup(1);
registration.addMapping("/app/*");
}
}
spring实现servlet3.0规范中,meta-inf/services目录下的文件
实现的接口
spring 对servlet3.0规范中onStartup()方法的实现
@HandlesTypes(WebApplicationInitializer.class)
//该注解将保证容器(tomcat)首先扫描WebApplicationInitializer接口(该接口可以自定义,spring使用的是WebApplicationInitializer接口)的所有实现类,然后赋值到onStartup()方法参数的webAppInitializerClasses set集合中。
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
//存放new 出来的对象
if (webAppInitializerClasses != null) {
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
//for循环,依次调用new 出来的对象,调用其onStartup()方法
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
2.Springboot自动配置原理
Springboot项目启动application
package com.xiayu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@SpringBootApplication//Springboot项目启动注解
@EnableConfigurationProperties
public class XiayuRoutingDatasourceApplication {
public static void main(String[] args) {
SpringApplication.run(XiayuRoutingDatasourceApplication.class);
}
}
@SpringbootApplication注解
@Target(ElementType.TYPE)//定义该注解作用于什么上面,是类,接口,方法,方法参数等等上面,ElementType.TYPE定义在类和接口上面
@Retention(RetentionPolicy.RUNTIME)//注解在运行时生效
@Documented 声明该注解会被在javadoc文档信息中展示
@Inherited 该注解保证注解声明在一个父类上后,子类也会有相同的属性
//上述都是java自带的注解
@SpringBootConfiguration//该注解仅仅声明配置类
@EnableAutoConfiguration//Springboot的核心配置注解
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
@EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
//配置自动扫描包,扫描当前注解所在的目录,如果当前配置在com.xiayu包下,就相当于
//配置了@ComponentScan("com.xiayu")
//这也是为什么Springboot项目启动类与各个模块同级
@Import(AutoConfigurationImportSelector.class)
//真正的核心自动配置类,加载AutoConfigurationImportSelector类到spring容器中,并作为bean管理
public @interface EnableAutoConfiguration {
典型的Springboot项目目录结构
@Import加载的类:AutoConfigurationImportSelector,其实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
DeferredImportSelector接口
package org.springframework.context.annotation;
public interface DeferredImportSelector extends ImportSelector {
}
package org.springframework.context.annotation;
-------------------------------------------------------
import org.springframework.core.type.AnnotationMetadata;
public interface ImportSelector {
String[] selectImports(AnnotationMetadata var1);
//该接口在AutoConfigurationImportSelector类中有具体的实现
}
AutoConfigurationImportSelector类中 selectImports方法的具体实现
//返回自动配置类的路径,交由spring管理,完成整个bean生命周期
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//从meta-inf/spring.factories文件中加载已经定义的配置类的全路径
List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
//返回List<String>,list中包含自动配置类的全路径
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
在SpringFactoriesLoader类中定义了自动配置类全路径保存的文件
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();
meta-inf/spring.factories文件,如果存在多个该文件,也会被扫描到,扫描到后就会将这些类通过@Import注解加载到spring容器中,被Spring进行管理。
3.以rabbitmq举例
springboot中Rabbitmq的配置文件路径为:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
RabbitAutoConfiguration类
RabbitProperties类
@ConfigurationProperties(prefix = "spring.rabbitmq")
//获取配置文件中spring.rabbitmq开头的所有配置,
//映射到RabbitProperties类中的属性中
public class RabbitProperties {
/**
* RabbitMQ host.
*/
private String host = "localhost"; //默认的host
/**
* RabbitMQ port.
*/
private int port = 5672; //默认的port
/**
* Login user to authenticate to the broker.
*/
private String username = "guest"; //默认的用户名
/**
* Login to authenticate against the broker.
*/
private String password = "guest";//默认的密码
/**
* SSL configuration.
*/
private final Ssl ssl = new Ssl();
/**
* Virtual host to use when connecting to the broker.
*/
private String virtualHost;
/**
* Comma-separated list of addresses to which the client should connect.
*/
private String addresses;
/**
* Requested heartbeat timeout; zero for none. If a duration suffix is not specified,
* seconds will be used.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration requestedHeartbeat;
/**
* Whether to enable publisher confirms.
*/
private boolean publisherConfirms;
/**
* Whether to enable publisher returns.
*/
private boolean publisherReturns;
/**
* Connection timeout. Set it to zero to wait forever.
*/
private Duration connectionTimeout;
/**
* Cache configuration.
*/
private final Cache cache = new Cache();
/**
* Listener container configuration.
*/
private final Listener listener = new Listener();
private final Template template = new Template();
private List<Address> parsedAddresses;
public String getHost() {
return this.host;
}
}
4.精简流程
@SpringbootApplication -> @EnableAutoConfiguration->@Import(AutoConfigurationImportSelector.class)->meta-inf/spring.factories>org.springframework.boot.autoconfigure.EnableAutoConfiguration
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。