springBoot 配置的自动加载
@SpringBootApplication
这个注解中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@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
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
....
}
@import表示需要将里面的class进行导入,于是我们进入AutoConfigurationImportSelector类中,在这个类中能看到很多其中有几个类加载器,我们可以看其中的selectImports方法
@Override
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);
}
我们随后可以进入这个获取默认配置的方法
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate 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;
}
META-INF/spring.factories,中的就是默认配置,我们可以点开这个类所在的位置
查看其中的spring.factories,你会发现里面全部都是默认的配置。
或者从springApplication类出发
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
你能够找到着个方法,随后查看getSpringFactoriesInstance()方法,追溯下去你能看到loadSpringFactories()
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//FACTORIES_RESOURCE_LOCATION="META-INF/spring.factories"
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()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
在这里你也可以看到他将META-INF/spring.factories中的配置全部加载进来了,随后setInitializers()
springboot 内置tomcat的加载
在spring.factories搜索WebServer相关的配置
我们可以点击查看其中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);
}
/**
* {@link Conditional} that only matches when the specified classes are on the classpath.
*
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
/**
* The classes that must be present. Since this annotation is parsed by loading class
* bytecode, it is safe to specify classes here that may ultimately not be on the
* classpath, only if this annotation is directly on the affected component and
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
* use this annotation as a meta-annotation, only use the {@link #name} attribute.
* @return the classes that must be present
*/
Class<?>[] value() default {};
/**
* The classes names that must be present.
* @return the class names that must be present.
*/
String[] name() default {};
}
ConditionalOnClass注解,从注解的注释中读到,在加载类之前,注解中的class必须存在。@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat"),于是可以了解到"org.apache.catalina.startup.Tomcat必须存在。点进去了你就会发现Tomcat类,他就是apache的tomcat,可总上看到他了
* <p>
* There are a variety of 'add' methods to configure servlets and webapps. These
* methods, by default, create a simple in-memory security realm and apply it.
* If you need more complex security processing, you can define a subclass of
* this class.
*
* <p>
* This class provides a set of convenience methods for configuring webapp
* contexts, all overloads of the method <code>addWebapp</code>. These methods
* create a webapp context, configure it, and then add it to a {@link Host}.
* They do not use a global default web.xml; rather, they add a lifecycle
* listener that adds the standard DefaultServlet, JSP processing, and welcome
* files.
*
* <p>
add方法将其加入webapps。
aop加载
同样在spring.factories中搜索aop,你就会看到如下代码
/*
* Copyright 2012-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
*
* http://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.boot.autoconfigure.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.Advice;
import org.aspectj.weaver.AnnotatedElement;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for Spring's AOP support. Equivalent to enabling
* {@link org.springframework.context.annotation.EnableAspectJAutoProxy} in your
* configuration.
* <p>
* The configuration will not be activated if {@literal spring.aop.auto=false}. The
* {@literal proxyTargetClass} attribute will be {@literal true}, by default, but can be
* overridden by specifying {@literal spring.aop.proxy-target-class=false}.
*
* @author Dave Syer
* @author Josh Long
* @see EnableAspectJAutoProxy
*/
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
是不是突然感觉到,EnableAspectJAutoProxy.class, Aspect.class, Advice.class,在你使用apo的时候都是那么的熟悉。
接着看他内部的方法,你会看到JdkDynamic与Cglib,这就是aop动态生成的2种,一种利用jdk的Dynamic动态代理,通过接口生成一个新的类,而Cglib在可以在父类的基础上直接生成代理类。
顺便说一下他们的区别吧
proxy by subclassing. Spring uses CGLIB for proxy if class is not implementing interface.
Sr. No. | Key | JDK dynamic proxy | CGLIB proxy |
---|---|---|---|
1 | Basic | It can be only proxy by interface so target class needs to implement interface | It can create proxy by subclassing |
2 | Package | It is available with the Java | It is a third library. |
3 | Performance | It is a bit slow than CGLIB proxy | It is faster than JDK dynamic proxy |
4. | Final | Final class and Final method can not be proxy | Final class and Final method can not be proxy |
5.. | Use case | Spring uses JDK proxy when is class is implementing one or more interface | Spring uses CGLib proxy when class in not implementing interface |
其他的配置也于此类似