我是靠谱客的博主 炙热刺猬,这篇文章主要介绍springboot自动装配及spi,现在分享给大家,希望可以做个参考。

Java spi机制

 spi全称服务提供接口,Java 提供一些接口,供别人去实现,这种方式实现了设计模式中的单一职责,开闭原则,进行接口编程,不对原有逻辑进行硬编码,对实现类可插拔的使用

如果Java中对数据库驱动的加载就是利用spi机制实现的,如果使用mysql数据库只需添加mysql驱动,如果使用oracle数据库,只需添加oracle驱动

如果需要添加mysql驱动,就要在mysql驱动中添加一些配置

如上所示,java.sql.Driver是在java rt包中,之前在jvm中涉及到了类加载,这个包是启动类加载器来进行加载了

但是当我们加载mysql 驱动的时候,我们是把mysql驱动包加载在classpath下,并没有启动类加载器的扫描范围,所以启动类加载器加载不了这个类

这个时候就需要通过 当前线程类加载器了,当前线程类加载器会把类给启动类加载器去加载

由于启动类加载器是c++实现的,所以我们获取不到这个值

spring spi,是java spi的变种

springboot就是通过spring spi来进行自动装配的,其都是通过EnableAutoConfiguration来进行的

import就是把类加载到ioc容器中去

  

其中DeferredImportSelector实现了ImportSelector,这个就是可以通过一个string[],通过包路径+类名的方式,import到ioc容器中,其实这里只是去创建beanDefinition 

 再看下DeferredImportSelector的这个方法,这个方法就是将返回值中所有 的类导入到ioc容器中

复制代码
1
2
3
4
5
6
7
8
9
10
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }

接下看下,这些类是从哪里获取的

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } 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 new AutoConfigurationEntry(configurations, exclusions); }
复制代码
1
2
3
4
5
6
7
8
9
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; }

此时这个factoryClass 传入的值是 EnableAutoConfiguration

复制代码
1
2
3
4
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<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 = 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); } }

  其中这行就是核心代码Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

这个就是加载配置文件,然后生成一个Map<String, List<String>>对象

从springboot包中看下这个配置文件:

所以之前传入的key是EnableAutoConfiguration,则这里会加载下面所有的value

如加载org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,

这里注意,加载配置文件 可以通过java  io去加载,也可以通过classload去加载

java io可以加载classpath之外的文件

classload只能加载classpath之类的配置文件

springboot内嵌tomcat

 顺序:1、先启动ioc容器  自动配置tomcat,

           2、再启动tomcat  

           3、启动后  new dispatchServlet把dispatchServlet实例注入到tomcat中,实现tomcat向springMvc的转发

 

1、自动配置tomcat ,使用了spring spi技术

 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,

 

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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 { @Bean public 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); } ...................................... }

ServerProperties是扫描  配置文件信息的,

@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") 是classPath中有org.apache.catalina.startup.Tomcat就是启动tomcat容器,springboot自带,且默认的

 

2、启动tomcat  

3、启动后把dispatchServlet注入到tomcat中,实现tomcat向springMvc的转发 这里使用到了sevlet 3.0技术,sevlet spi

在tomcat启动后,会去执行上面那个文件的类的onStartup方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web; import java.lang.reflect.Modifier; import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.HandlesTypes; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.lang.Nullable; import org.springframework.util.ReflectionUtils; /** * Servlet 3.0 {@link ServletContainerInitializer} designed to support code-based * configuration of the servlet container using Spring's {@link WebApplicationInitializer} * SPI as opposed to (or possibly in combination with) the traditional * {@code web.xml}-based approach. * * <h2>Mechanism of Operation</h2> * This class will be loaded and instantiated and have its {@link #onStartup} * method invoked by any Servlet 3.0-compliant container during container startup assuming * that the {@code spring-web} module JAR is present on the classpath. This occurs through * the JAR Services API {@link ServiceLoader#load(Class)} method detecting the * {@code spring-web} module's {@code META-INF/services/javax.servlet.ServletContainerInitializer} * service provider configuration file. See the * <a href="https://download.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider"> * JAR Services API documentation</a> as well as section <em>8.2.4</em> of the Servlet 3.0 * Final Draft specification for complete details. * * <h3>In combination with {@code web.xml}</h3> * A web application can choose to limit the amount of classpath scanning the Servlet * container does at startup either through the {@code metadata-complete} attribute in * {@code web.xml}, which controls scanning for Servlet annotations or through an * {@code <absolute-ordering>} element also in {@code web.xml}, which controls which * web fragments (i.e. jars) are allowed to perform a {@code ServletContainerInitializer} * scan. When using this feature, the {@link SpringServletContainerInitializer} * can be enabled by adding "spring_web" to the list of named web fragments in * {@code web.xml} as follows: * * <pre class="code"> * &lt;absolute-ordering&gt; * &lt;name>some_web_fragment&lt;/name&gt; * &lt;name>spring_web&lt;/name&gt; * &lt;/absolute-ordering&gt; * </pre> * * <h2>Relationship to Spring's {@code WebApplicationInitializer}</h2> * Spring's {@code WebApplicationInitializer} SPI consists of just one method: * {@link WebApplicationInitializer#onStartup(ServletContext)}. The signature is intentionally * quite similar to {@link ServletContainerInitializer#onStartup(Set, ServletContext)}: * simply put, {@code SpringServletContainerInitializer} is responsible for instantiating * and delegating the {@code ServletContext} to any user-defined * {@code WebApplicationInitializer} implementations. It is then the responsibility of * each {@code WebApplicationInitializer} to do the actual work of initializing the * {@code ServletContext}. The exact process of delegation is described in detail in the * {@link #onStartup onStartup} documentation below. * * <h2>General Notes</h2> * In general, this class should be viewed as <em>supporting infrastructure</em> for * the more important and user-facing {@code WebApplicationInitializer} SPI. Taking * advantage of this container initializer is also completely <em>optional</em>: while * it is true that this initializer will be loaded and invoked under all Servlet 3.0+ * runtimes, it remains the user's choice whether to make any * {@code WebApplicationInitializer} implementations available on the classpath. If no * {@code WebApplicationInitializer} types are detected, this container initializer will * have no effect. * * <p>Note that use of this container initializer and of {@code WebApplicationInitializer} * is not in any way "tied" to Spring MVC other than the fact that the types are shipped * in the {@code spring-web} module JAR. Rather, they can be considered general-purpose * in their ability to facilitate convenient code-based configuration of the * {@code ServletContext}. In other words, any servlet, listener, or filter may be * registered within a {@code WebApplicationInitializer}, not just Spring MVC-specific * components. * * <p>This class is neither designed for extension nor intended to be extended. * It should be considered an internal type, with {@code WebApplicationInitializer} * being the public-facing SPI. * * <h2>See Also</h2> * See {@link WebApplicationInitializer} Javadoc for examples and detailed usage * recommendations.<p> * * @author Chris Beams * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 3.1 * @see #onStartup(Set, ServletContext) * @see WebApplicationInitializer */ @HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { /** * Delegate the {@code ServletContext} to any {@link WebApplicationInitializer} * implementations present on the application classpath. * <p>Because this class declares @{@code HandlesTypes(WebApplicationInitializer.class)}, * Servlet 3.0+ containers will automatically scan the classpath for implementations * of Spring's {@code WebApplicationInitializer} interface and provide the set of all * such types to the {@code webAppInitializerClasses} parameter of this method. * <p>If no {@code WebApplicationInitializer} implementations are found on the classpath, * this method is effectively a no-op. An INFO-level log message will be issued notifying * the user that the {@code ServletContainerInitializer} has indeed been invoked but that * no {@code WebApplicationInitializer} implementations were found. * <p>Assuming that one or more {@code WebApplicationInitializer} types are detected, * they will be instantiated (and <em>sorted</em> if the @{@link * org.springframework.core.annotation.Order @Order} annotation is present or * the {@link org.springframework.core.Ordered Ordered} interface has been * implemented). Then the {@link WebApplicationInitializer#onStartup(ServletContext)} * method will be invoked on each instance, delegating the {@code ServletContext} such * that each instance may register and configure servlets such as Spring's * {@code DispatcherServlet}, listeners such as Spring's {@code ContextLoaderListener}, * or any other Servlet API componentry such as filters. * @param webAppInitializerClasses all implementations of * {@link WebApplicationInitializer} found on the application classpath * @param servletContext the servlet context to be initialized * @see WebApplicationInitializer#onStartup(ServletContext) * @see AnnotationAwareOrderComparator */ @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); 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 (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }

 其中有个注解,是tomcat实现的,@HandlesTypes(WebApplicationInitializer.class)  这个会加载WebApplicationInitializer实现类作为onStartup方法的参数

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected void registerDispatcherServlet(ServletContext servletContext) { String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return null or empty"); WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null"); FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null"); dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); if (registration == null) { throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " + "Check if there is another servlet registered under the same name."); } registration.setLoadOnStartup(1); registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported()); Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } customizeRegistration(registration); }

 这里做了转发处理

最后

以上就是炙热刺猬最近收集整理的关于springboot自动装配及spi的全部内容,更多相关springboot自动装配及spi内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部