能够看到那个注消除了元注解之外,便只要一个@Configuration,那也便是道那个注解相称于@Configuration,以是那两个注解感化是一样的,它让我们可以来注册一些分外的Bean,而且导进一些分外的设置。
那@Configuration另有一个感化便是把负绵酿成一个设置类,没有需求分外的XML停止设置。以是@SpringBootConfiguration便相称于@Configuration。进进@Configuration,发明@Configuration中心是@Component,阐明Spring的设置类也是Spring的一个组件。
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Component
- public @interface Configuration {
- @AliasFor(
- annotation = Component.class
- )
- String value() default "";
- }
赶钙代码
持续去看现位个@EnableAutoConfiguration,那个注解是开启主动设置的功用。
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @AutoConfigurationPackage
- @Import({AutoConfigurationImportSelector.class})
- public @interface EnableAutoConfiguration {
- String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
-
- Class<?>[] exclude() default {};
-
- String[] excludeName() default {};
- }
赶钙代码
能够看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)那两个而构成的,我们先道@AutoConfigurationPackage,他是道:让包中的类和子包中的类可以被主动扫描到spring容器中。
-
- @Target({ElementType.TYPE})
- @Retention(RetentionPolicy.RUNTIME)
- @Documented
- @Inherited
- @Import({Registrar.class})
- public @interface AutoConfigurationPackage {
- }
赶钙代码
利用@Import去给Spring容器中导进一个组件 ,那里导进的是Registrar.class。去看下那个Registrar:
- static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
- Registrar() {
- }
-
- public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
- AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
- }
-
- public Set<Object> determineImports(AnnotationMetadata metadata) {
- return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
- }
- }
赶钙代码
便是经由过程以上那个办法获得扫描的包途径,能够debug检察详细的值:
那metadata是甚么呢,能够看到是标注正在@SpringBootApplication注解上的DemosbApplication,也便是我们的主设置类Application:
实在便是将主设置类(即@SpringBootApplication标注的类)的地点包己谟包内里一切组件扫描减载到Spring容器。因而我们要把DemoApplication放正在项目标第一流中(最中层目次)。
吭哟注解@Import(AutoConfigurationImportSelector.class),@Import注解便史狲Spring容器中导进一些组件,那里传进了一个组件狄住择器:AutoConfigurationImportSelector。
能够从图中看出AutoConfigurationImportSelector 担当了 DeferredImportSelector 担当了 ImportSelector,ImportSelector有一个办法为:selectImports。将一切需求导进的组件以齐类名的方法返回,那些组件便会被增加到容器中。
- public String[] selectImports(AnnotationMetadata annotationMetadata) {
- if (!this.isEnabled(annotationMetadata)) {
- return NO_IMPORTS;
- } else {
- AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
- AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry =
- this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
- return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
- }
- }
赶钙代码
会给容器中导进十分多的主动设置类(xxxAutoConfiguration);便史狲容器中导进那个场景需求的一切组件,并设置好那些组件。
有裂沤设置类,免除了我们脚动编写设置注进功用组件等的事情。那是怎样获得到那些设置类的呢,吭哟上面那个办法:
- protected AutoConfigurationImportSelector.AutoConfigurationEntry
- getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
- if (!this.isEnabled(annotationMetadata)) {
- return EMPTY_ENTRY;
- } else {
- AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
- List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
- configurations = this.removeDuplicates(configurations);
- Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
- this.checkExcludedClasses(configurations, exclusions);
- configurations.removeAll(exclusions);
- configurations = this.filter(configurations, autoConfigurationMetadata);
- this.fireAutoConfigurationImportEvents(configurations, exclusions);
- return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
- }
- }
赶钙代码
我们能够看到getCandidateConfigurations()那个办法,他的感化便是引进体系曾经减载好的一些类,究竟是那些类呢:
- protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
- List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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 (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
- }
赶钙代码
会从META-INF/spring.factories挚与资本,然后经由过程Properties减载资本:
- 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()) {
- Map.Entry<?, ?> entry = (Map.Entry)var6.next();
- String factoryClassName = ((String)entry.getKey()).trim();
- String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
- int var10 = var9.length;
-
- for(int var11 = 0; var11 < var10; ++var11) {
- String factoryName = var9[var11];
- result.add(factoryClassName, factoryName.trim());
- }
- }
- }
-
- cache.put(classLoader, result);
- return result;
- } catch (IOException var13) {
- throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
- }
- }
- }
赶钙代码
能够明白SpringBoot正在启动的时分从类途径下的META-INF/spring.factories挚与EnableAutoConfiguration指定的值,将那些值做为主动设置类导进到容器中,主动设置类便见效,帮我们停止主动设置事情。从前我们需求本人设置的工具,主动设置类皆帮我们完成了。以下图能够发明Spring常睹的一些类曾经主动导进。
接下去看@ComponentScan注解,@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),那个注解便是扫描包,然后放进spring容器。
- @ComponentScan(excludeFilters = {
- @Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
- @Filter(type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})})
- public @interface SpringBootApplication {}
赶钙代码
总结下@SpringbootApplication:便是道,他曾经把许多工具筹办好,详细能否利用与决于我们的法式大概道设置。
接下去持续看run办法:
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
赶钙代码
去看下正在施行run办法到底有无用到哪些主动设置的工具,我们面进run:
- 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);
- //预革新context
- this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
- //革新context
- this.refreshContext(context);
- //革新以后的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);
- }
- }
赶钙代码
那我们存眷的便是 refreshContext(context); 革新context,我们面出去看。
保举:
- private void refreshContext(ConfigurableApplicationContext context) {
- refresh(context);
- if (this.registerShutdownHook) {
- try {
- context.registerShutdownHook();
- }
- catch (AccessControlException ex) {
- // Not allowed in some environments.
- }
- }
- }
赶钙代码
我们持续面进refresh(context);
- protected void refresh(ApplicationContext applicationContext) {
- Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
- ((AbstractApplicationContext) applicationContext).refresh();
- }
赶钙代码
他会挪用 ((AbstractApplicationContext) applicationContext).refresh();办法,我们面出去看:
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- prepareRefresh();
- // Tell the subclass to refresh the internal bean factory.
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
-
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
- // Initialize message source for this context.
- initMessageSource();
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
- // Initialize other special beans in specific context subclasses.
- onRefresh();
- // Check for listener beans and register them.
- registerListeners();
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory);
- // Last step: publish corresponding event.
- finishRefresh();
- }catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during context initialization - " +
- "cancelling refresh attempt: " + ex);
- }
- // Destroy already created singletons to avoid dangling resources.
- destroyBeans();
- // Reset 'active' flag.
- cancelRefresh(ex);
- // Propagate exception to caller.
- throw ex;
- }finally {
- // Reset common introspection caches in Spring's core, since we
- // might not ever need metadata for singleton beans anymore...
- resetCommonCaches();
- }
- }
- }
赶钙代码
由此可知,便是一个spring的bean的减载历程。持续去看一个办法叫做 onRefresh():
- protected void onRefresh() throws BeansException {
- // For subclasses: do nothing by default.
- }
赶钙代码
他正在那里并出有间接完成,可是我们找他的详细完成:
好比Tomcat跟web庸呢,我们能够看到有个ServletWebServerApplicationContext:
- @Override
- protected void onRefresh() {
- super.onRefresh();
- try {
- createWebServer();
- }
- catch (Throwable ex) {
- throw new ApplicationContextException("Unable to start web server", ex);
- }
- }
赶钙代码
能够看到有一个createWebServer();办法他是创立web容器的,而Tomcat没有便是web容器,那是怎样创立的呢,我们持续看:
- private void createWebServer() {
- WebServer webServer = this.webServer;
- ServletContext servletContext = getServletContext();
- if (webServer == null && servletContext == null) {
- ServletWebServerFactory factory = getWebServerFactory();
- this.webServer = factory.getWebServer(getSelfInitializer());
- }
- else if (servletContext != null) {
- try {
- getSelfInitializer().onStartup(servletContext);
- }
- catch (ServletException ex) {
- throw new ApplicationContextException("Cannot initialize servlet context",
- ex);
- }
- }
- initPropertySources();
- }
赶钙代码
factory.getWebServer(getSelfInitializer());他是经由过程工场的方法创立的。
- public interface ServletWebServerFactory {
- WebServer getWebServer(ServletContextInitializer... initializers);
- }
赶钙代码
能够看到 它是一个接心,为何会是接心。由于我们没有行是Tomcat一种web容器。
我们看到另有Jetty,那我们去看TomcatServletWebServerFactory:
- 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);
- }
赶钙代码
那那块代码,便是我们要寻觅的内置Tomcat,正在那个历程傍边,我们能够看到创立Tomcat的一个流程。
假如没有大白的话, 我玫邻用另外一种方法去了解下,各人要该当皆明白stater举面例子。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-freemarker</artifactId>
- </dependency>
赶钙代码
起首捉义一个stater。
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.1.4.RELEASE</version>
- <relativePath/>
- </parent>
- <groupId>com.zgw</groupId>
- <artifactId>gw-spring-boot-starter</artifactId>
- <version>1.0-SNAPSHOT</version>
-
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-autoconfigure</artifactId>
- </dependency>
- </dependencies>
赶钙代码
我们先去看maven设置写进版本号,假如捉义一个stater的话必需依靠spring-boot-autoconfigure那个包,我们先看下项目目次。
- public class GwServiceImpl implements GwService{
- @Autowired
- GwProperties properties;
-
- @Override
- public void Hello()
- {
- String name=properties.getName();
- System.out.println(name+"道:您们好啊");
- }
- }
赶钙代码
我们做的便是经由过程设置文件去定造name那个是详细完成。
- @Component
- @ConfigurationProperties(prefix = "spring.gwname")
- public class GwProperties {
-
- String name="zgw";
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
赶钙代码
那个类能够经由过程@ConfigurationProperties读与设置文件。
- @Configuration
- @ConditionalOnClass(GwService.class) //扫描类
- @EnableConfigurationProperties(GwProperties.class) //让设置类见效
- public class GwAutoConfiguration {
-
- /**
- * 功用形貌 凸能给spring
- * @author zgw
- * @return
- */
- @Bean
- @ConditionalOnMissingBean
- public GwService gwService()
- {
- return new GwServiceImpl();
- }
- }
赶钙代码
那个为设置类,为何那么写由于,spring-boot的stater皆是那么写的,我们能够参照他仿写stater,以到达主动设置的目标,然后我玫邻经由过程spring.factories也去停止设置。
-
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.gw.GwAutoConfiguration
赶钙代码
然后如许一个简朴的stater便完成了,然后能够停止maven的挨包,正在其他项目引进就能够利用。