一、spring注解之如何导入Bean的几种方式
1)@Bean注解
2) 包扫描来加载Bean 比如标识@Controller @Service @Repository @Compent
3) @Import几种取值来注册bean
①:实现ImportSelector接口的类
写一个配置类在配置类上标注一个@Import的注解,在@Import注解的value值 写自己需要导入的组件
@Configuration@Import(value = {TestSelector.class})public class TestgConfig {}public class TestSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.zhao.service.TestServiceImpl"};}}
②:实现ImportBeanDefinitionRegistrar接口來注冊bean
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {//定义一个BeanDefinitionRootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDao.class);//把自定义的bean定义导入到容器中beanDefinitionRegistry.registerBeanDefinition("testDao",rootBeanDefinition);}}
4)实现factoryBean的方式来导入组件
二、spring底层条件装配的原理@Conditional
通过实现Condition接口,定义匹配方法,来达到条件加载
public class TestConditional implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {//容器中包含testAspect组件才返回Tureif(conditionContext.getBeanFactory().containsBean("testAspect")){return true;}else{return false;}}}@Beanpublic TestAspect testAspect() {System.out.println("testAspect组件自动装配到容器中");return new TestAspect();}@Bean@Conditional(value = TestConditional.class)public TestLog testLog() {System.out.println("testgLog组件自动装配到容器中");return new TestLog();}
三、springboot自动装配原理
1、Springboot注解组合图

先分析AutoConfigurationImportSelector做了什么事情
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);AnnotationAttributes attributes = getAttributes(annotationMetadata);//获取候选的配置类List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);//移除重复的configurations = removeDuplicates(configurations);Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = filter(configurations, autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations, exclusions);//返回出去return StringUtils.toStringArray(configurations);}//获取候选的配置类protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());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;}//加载配置类public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {//"META-INF/spring.factories" 去类路径下该文件中加载 EnableAutoConfiguration.classEnumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();//遍历解析出来的集合while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);//放在Properties中Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);//返回return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}
主要是扫描spring-boot-autoconfigure\2.1.5.RELEASE\spring-boot-autoconfigure-
2.0.8.RELEASE.jar!\META-INF\spring.factories 中EnableAutoConfiguration对应的全类名,这里的都是自动配置类
分析源码1: org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
@Configuration //标识是一个自动配置类@EnableConfigurationProperties(HttpEncodingProperties.class) //启动指定类的配置功能,并且把配置文件中的属性和HttpEncodingProperties类的属性进行绑定@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //spring底层的@Conditional注解的变种 就是用来进行条件判断的,判断是否为web环境@ConditionalOnClass(CharacterEncodingFilter.class)//判断环境中是否没有这个类//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)public class HttpEncodingAutoConfiguration {//自动配置类的属性映射private final HttpEncodingProperties properties;public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {this.properties = properties;}//配置一个 CharacterEncodingFilter 是springmvc解决乱码的 ,若容器中没有该组件 ,那么就会创建该组件@Bean@ConditionalOnMissingBeanpublic CharacterEncodingFilter characterEncodingFilter() {CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();filter.setEncoding(this.properties.getCharset().name());filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));return filter;}@Beanpublic LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {return new LocaleCharsetMappingsCustomizer(this.properties);}private static class LocaleCharsetMappingsCustomizer implementsWebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {private final HttpEncodingProperties properties;LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {this.properties = properties;}@Overridepublic void customize(ConfigurableServletWebServerFactory factory) {if (this.properties.getMapping() != null) {factory.setLocaleCharsetMappings(this.properties.getMapping());}}@Overridepublic int getOrder() {return 0;}}}
四、spring Boot的jar包的启动流程

EmbeddedWebServerFactoryCustomizerAutoConfiguration(内嵌web容器工厂自定义定制器装配类)
@Configuration@ConditionalOnWebApplication@EnableConfigurationProperties(ServerProperties.class)public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {//配置tomcat的@Configuration@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })public static class TomcatWebServerFactoryCustomizerConfiguration {@Beanpublic TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {return new TomcatWebServerFactoryCustomizer(environment, serverProperties);}}//配置jetty@Configuration@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })public static class JettyWebServerFactoryCustomizerConfiguration {@Beanpublic JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {return new JettyWebServerFactoryCustomizer(environment, serverProperties);}}//配置undertow的@Configuration@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })public static class UndertowWebServerFactoryCustomizerConfiguration {@Beanpublic UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {return new UndertowWebServerFactoryCustomizer(environment, serverProperties);}}}
tomat 工厂定制器 是用来修改设置容器的内容的(把serverProperties的属性设置到tomcat的创建工厂中)
public class TomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {//..........................其他代码省略。。。。。。。。。。。。。。@Overridepublic void customize(ConfigurableTomcatWebServerFactory factory) {ServerProperties properties = this.serverProperties;ServerProperties.Tomcat tomcatProperties = properties.getTomcat();PropertyMapper propertyMapper = PropertyMapper.get();propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);customizeRemoteIpValve(factory);propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive).to((maxThreads) -> customizeMaxThreads(factory,tomcatProperties.getMaxThreads()));propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive).to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));propertyMapper.from(() -> determineMaxHttpHeaderSize()).when(this::isPositive).to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,maxHttpHeaderSize));propertyMapper.from(tomcatProperties::getMaxHttpPostSize).when((maxHttpPostSize) -> maxHttpPostSize != 0).to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,maxHttpPostSize));propertyMapper.from(tomcatProperties::getAccesslog).when(ServerProperties.Tomcat.Accesslog::isEnabled).to((enabled) -> customizeAccessLog(factory));propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);propertyMapper.from(properties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> customizeConnectionTimeout(factory,connectionTimeout));propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> customizeMaxConnections(factory, maxConnections));propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> customizeAcceptCount(factory, acceptCount));customizeStaticResources(factory);customizeErrorReportValve(properties.getError(), factory);}}
ServletWebServerFactoryAutoConfiguration Servletweb工厂自动配置类
@Configuration@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)@ConditionalOnClass(ServletRequest.class)@ConditionalOnWebApplication(type = Type.SERVLET)@EnableConfigurationProperties(ServerProperties.class)@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,ServletWebServerFactoryConfiguration.EmbeddedJetty.class,ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })public class ServletWebServerFactoryAutoConfiguration {@Beanpublic ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new ServletWebServerFactoryCustomizer(serverProperties);}@Bean@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {return new TomcatServletWebServerFactoryCustomizer(serverProperties);}}//.....................................................ServletWebServerFactoryCustomizer核心代码...........................................public void customize(ConfigurableServletWebServerFactory factory) {PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();map.from(this.serverProperties::getPort).to(factory::setPort);map.from(this.serverProperties::getAddress).to(factory::setAddress);map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);map.from(this.serverProperties::getSsl).to(factory::setSsl);map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);map.from(this.serverProperties::getCompression).to(factory::setCompression);map.from(this.serverProperties::getHttp2).to(factory::setHttp2);map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);}//---------------------------------------------TomcatServletWebServerFactoryCustomizer核心定制代码-----------------------------------public void customize(TomcatServletWebServerFactory factory) {ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());}if (tomcatProperties.getRedirectContextRoot() != null) {customizeRedirectContextRoot(factory,tomcatProperties.getRedirectContextRoot());}if (tomcatProperties.getUseRelativeRedirects() != null) {customizeUseRelativeRedirects(factory,tomcatProperties.getUseRelativeRedirects());}}
ServletWebServerFactoryConfiguration 容器工厂配置类
@Configurationclass ServletWebServerFactoryConfiguration {@Configuration@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)public static class EmbeddedTomcat {//配置tomcat 容器工厂@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {return new TomcatServletWebServerFactory();}}
启动的主流程
org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String…)
1、传入主配置类,以及命令行参数
2、创建SpringApplication对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");//保存主配置类this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//保存web应用的类型this.webApplicationType = WebApplicationType.deduceFromClasspath();//保存 容器初始化器(ApplicationContextInitializer类型的)setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));//把监听器保存到 SpringApplication中[ApplicationListener]setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//保存主配置类this.mainApplicationClass = deduceMainApplicationClass();}//还是去META-INFO/spring.factories 中获取ApplicationContextInitializer 类型,用于初始化容器private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicatesSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes,classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}//查找主配置类 查询的依据就是看哪个方法是否有main方法private Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())) {return Class.forName(stackTraceElement.getClassName());}}}catch (ClassNotFoundException ex) {// Swallow and continue}return null;}
3、运行SpringbootApplication的run方法行
public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();//创建一个 容器对象ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();//去meta-info/spring.factories中获取SpringApplicationRunListener 监听器(事件发布监听器)SpringApplicationRunListeners listeners = getRunListeners(args);//发布容器 starting事件(通过spring的事件多播器)listeners.starting();try {//封装命令行参数ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);//准备容器环境//1:获取或者创建环境//2:把命令行参数设置到环境中//3:通过监听器发布环境准备事件ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);configureIgnoreBeanInfo(environment);//打印springboot的图标Banner printedBanner = printBanner(environment);//创建容器 根据webApplicationType 来创建容器 通过反射创建context = createApplicationContext();//去meta-info类中 获取异常报告exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context);//准备环境//1:把环境设置到容器中//2: 循环调用AppplicationInitnazlier 进行容器初始化工作//3:发布容器上下文准备完成事件//4:注册关于springboot特性的相关单例Bean//5:发布容器上下文加载完毕事件prepareContext(context, environment, listeners, applicationArguments,printedBanner);refreshContext(context);//运行 ApplicationRunner 和CommandLineRunnerafterRefresh(context, applicationArguments);stopWatch.stop();if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}//发布容器启动事件listeners.started(context);//运行 ApplicationRunner 和CommandLineRunnercallRunners(context, applicationArguments);}catch (Throwable ex) {//出现异常;调用异常分析保护类进行分析handleRunFailure(context, ex, exceptionReporters, listeners);throw new IllegalStateException(ex);}try {//发布容器运行事件listeners.running(context);}catch (Throwable ex) {handleRunFailure(context, ex, exceptionReporters, null);throw new IllegalStateException(ex);}return context;}
4、org.springframework.boot.SpringApplication#refreshContext
5、org.springframework.context.support.AbstractApplicationContext#refresh
6、org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
7、org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
7.1)org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#getWebServer
7.2)org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor#postProces
7.3)org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor#postProcessBeforeInitialization
我们往容器中导入了 BeanPostProcessorsRegistrar 他实现了 ImportBeanDefinitionRegistrar 在他的 registerBeanDefinitions注册Bean定义的时候 注册了 webServerFactoryCustomizerBeanPostProcessor
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {private ConfigurableListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {if (beanFactory instanceof ConfigurableListableBeanFactory) {this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;}}@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry) {if (this.beanFactory == null) {return;}registerSyntheticBeanIfMissing(registry,"webServerFactoryCustomizerBeanPostProcessor",WebServerFactoryCustomizerBeanPostProcessor.class);registerSyntheticBeanIfMissing(registry,"errorPageRegistrarBeanPostProcessor",ErrorPageRegistrarBeanPostProcessor.class);}}
8、org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
创建tomcat 并且容器启动
public WebServer getWebServer(ServletContextInitializer... initializers) {Tomcat tomcat = new Tomcat();File baseDir = (this.baseDirectory != null) ? this.baseDirectory: createTempDir("tomcat");tomcat.setBaseDir(baseDir.getAbsolutePath());Connector connector = new Connector(this.protocol);tomcat.getService().addConnector(connector);customizeConnector(connector);tomcat.setConnector(connector);tomcat.getHost().setAutoDeploy(false);configureEngine(tomcat.getEngine());for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);}prepareContext(tomcat.getHost(), initializers);return getTomcatWebServer(tomcat);}protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {//端口大于0启动启动return new TomcatWebServer(tomcat, getPort() >= 0);}public TomcatWebServer(Tomcat tomcat, boolean autoStart) {Assert.notNull(tomcat, "Tomcat Server must not be null");this.tomcat = tomcat;this.autoStart = autoStart;initialize();}//tomcat启动流程private void initialize() throws WebServerException {TomcatWebServer.logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));synchronized (this.monitor) {try {addInstanceIdToEngineName();Context context = findContext();context.addLifecycleListener((event) -> {if (context.equals(event.getSource())&& Lifecycle.START_EVENT.equals(event.getType())) {// Remove service connectors so that protocol binding doesn't// happen when the service is started.removeServiceConnectors();}});// Start the server to trigger initialization listenersthis.tomcat.start();// We can re-throw failure exception directly in the main threadrethrowDeferredStartupExceptions();try {ContextBindings.bindClassLoader(context, context.getNamingToken(),getClass().getClassLoader());}catch (NamingException ex) {// Naming is not enabled. Continue}// Unlike Jetty, all Tomcat threads are daemon threads. We create a// blocking non-daemon to stop immediate shutdownstartDaemonAwaitThread();}catch (Exception ex) {stopSilently();throw new WebServerException("Unable to start embedded Tomcat", ex);}}}
9、 在IOC 容器中的
org.springframework.context.support.AbstractApplicationContext#refresh 的
onReFresh()带动tomcat启动
然后在接着执行 ioc容器的其他步骤。
