JAVA基础知识复习-Springboot原理

1,617 阅读16分钟

优点

  • 组件自动装配:规约大于配置,专注核心业务
  • 外部化配置:一次构建,按需调配,到处运行
  • 嵌入式容器:内置容器,无需部署,独立运行
  • Spring Boot Starter:简化依赖,按需装配,自我包含
  • Production-Ready:一站式运维,生态无缝整合

组件自动装配

Spring模式注解装配

注解装配即在应用中扮演组件角色的注解,例如@Configuration,@Component.在SSM项目中我们一般通过context:component-scan进行装配,在springboot项目中则通过@ComponentScan

  • SSM(xml文件配置)
<!-- 激活注解驱动特性 --> 
<context:annotation-config />
<!-- 找寻被 @Component 或者其派生 Annotation 标记的类(Class),将它们注册为 Spring Bean --> 
<context:component-scan base-package="com.springbootlearning.test" />
  • Spring Boot
@ComponentScan(basePackages = "com.springbootlearning.test")
public class TestApplication {
 ...
}
  • 模式注解
Spring Framework 注解 描述
@Component 通用组件模式注解
@Configuration 配置类模式注解
@Controller Web 控制器模式注解
@Service 服务模式注解
@Repository 数据仓储模式注解

简单比对@Service和@Controller,我们不能发现两者均为@Component的派生注解(值(此处为value)必须一致),只是被赋予了不同的含义,本质是一样的

  • @Service
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}
  • @Controller
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

Spring @Enable 模块装配

@Enable 模块即具备相同领域的功能组件集合, 组合所形成一个独立 的单元。比如 Web MVC 模块、AspectJ代理模块等

框架 @Enable 模块注解 相关模块
Spring Framework @EnableWebMvc Web MVC 模块
@EnableTransactionManagement 事务管理模块
@EnableCaching Caching 模块
@EnableMBeanExport JMX 模块
@EnableAsync 异步处理模块
@EnableWebFlux Web Flux 模块
@EnableAspectJAutoProxy AspectJ 代理模块
Spring Boot @EnableAutoConfiguration 自动装配模块
@EnableManagementContext Actuator 管理模块
@EnableConfigurationProperties 配置属性绑定模块
@EnableOAuth2Sso OAuth2 单点登录模块
Spring Cloud @EnableEurekaServer Eureka服务器模块
@EnableConfigServer 配置服务器模块
@EnableFeignClients Feign客户端模块
@EnableZuulProxy 服务网关 Zuul 模块

基于模式注解驱动(以@EnableWebMvc为例)

  • 注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
  • DelegatingWebMvcConfiguration
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();

    public DelegatingWebMvcConfiguration() {
    }

    @Autowired(
        required = false
    )
    public void setConfigurers(List<WebMvcConfigurer> configurers) {
        if (!CollectionUtils.isEmpty(configurers)) {
            this.configurers.addWebMvcConfigurers(configurers);
        }

    }

    protected void configurePathMatch(PathMatchConfigurer configurer) {
        this.configurers.configurePathMatch(configurer);
    }

    protected void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        this.configurers.configureContentNegotiation(configurer);
    }

    protected void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        this.configurers.configureAsyncSupport(configurer);
    }

    protected void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        this.configurers.configureDefaultServletHandling(configurer);
    }

    protected void addFormatters(FormatterRegistry registry) {
        this.configurers.addFormatters(registry);
    }

    protected void addInterceptors(InterceptorRegistry registry) {
        this.configurers.addInterceptors(registry);
    }

    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        this.configurers.addResourceHandlers(registry);
    }

    protected void addCorsMappings(CorsRegistry registry) {
        this.configurers.addCorsMappings(registry);
    }

    protected void addViewControllers(ViewControllerRegistry registry) {
        this.configurers.addViewControllers(registry);
    }

    protected void configureViewResolvers(ViewResolverRegistry registry) {
        this.configurers.configureViewResolvers(registry);
    }

    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        this.configurers.addArgumentResolvers(argumentResolvers);
    }

    protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        this.configurers.addReturnValueHandlers(returnValueHandlers);
    }

    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.configureMessageConverters(converters);
    }

    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        this.configurers.extendMessageConverters(converters);
    }

    protected void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        this.configurers.configureHandlerExceptionResolvers(exceptionResolvers);
    }

    protected void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        this.configurers.extendHandlerExceptionResolvers(exceptionResolvers);
    }

    @Nullable
    protected Validator getValidator() {
        return this.configurers.getValidator();
    }

    @Nullable
    protected MessageCodesResolver getMessageCodesResolver() {
        return this.configurers.getMessageCodesResolver();
    }
}

自定义@Enable模块

  • 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MyServiceConfiguration.class})
public @interface EnableMyService {
}
  • 自定义Configuration
@Configuration
public class MyServiceConfiguration {
  @Bean
  public String test() {
    return "测试服务";
  }
}
  • 调用
@EnableMyService
public class TestApplication {
  public static void main(String[] args) {
    ConfigurableApplicationContext configurableApplicationContext =
        new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);
    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
  }
}

基于ImportSelector接口(以@EnableCaching为例)

  • 注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({CachingConfigurationSelector.class})
public @interface EnableCaching {
    boolean proxyTargetClass() default false;

    AdviceMode mode() default AdviceMode.PROXY;

    int order() default 2147483647;
}
  • ConfigurationSelector
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
    private static final String PROXY_JCACHE_CONFIGURATION_CLASS = "org.springframework.cache.jcache.config.ProxyJCacheConfiguration";
    private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJCachingConfiguration";
    private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.cache.aspectj.AspectJJCacheConfiguration";
    private static final boolean jsr107Present = ClassUtils.isPresent("javax.cache.Cache", CachingConfigurationSelector.class.getClassLoader());
    private static final boolean jcacheImplPresent = ClassUtils.isPresent("org.springframework.cache.jcache.config.ProxyJCacheConfiguration", CachingConfigurationSelector.class.getClassLoader());

    public CachingConfigurationSelector() {
    }

    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return this.getProxyImports();
        case ASPECTJ:
            return this.getAspectJImports();
        default:
            return null;
        }
    }

    private String[] getProxyImports() {
        List<String> result = new ArrayList();
        result.add(AutoProxyRegistrar.class.getName());
        result.add(ProxyCachingConfiguration.class.getName());
        if (jsr107Present && jcacheImplPresent) {
            result.add("org.springframework.cache.jcache.config.ProxyJCacheConfiguration");
        }

        return StringUtils.toStringArray(result);
    }

    private String[] getAspectJImports() {
        List<String> result = new ArrayList();
        result.add("org.springframework.cache.aspectj.AspectJCachingConfiguration");
        if (jsr107Present && jcacheImplPresent) {
            result.add("org.springframework.cache.aspectj.AspectJJCacheConfiguration");
        }

        return StringUtils.toStringArray(result);
    }
}
  • AdviceModeImportSelector(实现对selectImports重新封装)
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
    public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";

    public AdviceModeImportSelector() {
    }

    protected String getAdviceModeAttributeName() {
        return "mode";
    }

    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        Class<?> annType = GenericTypeResolver.resolveTypeArgument(this.getClass(), AdviceModeImportSelector.class);
        Assert.state(annType != null, "Unresolvable type argument for AdviceModeImportSelector");
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (attributes == null) {
            throw new IllegalArgumentException(String.format("@%s is not present on importing class '%s' as expected", annType.getSimpleName(), importingClassMetadata.getClassName()));
        } else {
            AdviceMode adviceMode = (AdviceMode)attributes.getEnum(this.getAdviceModeAttributeName());
            String[] imports = this.selectImports(adviceMode);
            if (imports == null) {
                throw new IllegalArgumentException(String.format("Unknown AdviceMode: '%s'", adviceMode));
            } else {
                return imports;
            }
        }
    }

    @Nullable
    protected abstract String[] selectImports(AdviceMode var1);
}
  • ImportSelector
public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);
}

自定义@Enable模块

  • 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import(MyServiceConfigurationSelector.class)
public @interface EnableMyService {
}
  • ConfigurationSelector
public class MyServiceConfigurationSelector implements ImportSelector {
  @Override
  public String[] selectImports(AnnotationMetadata annotationMetadata) {
    return new String[] {MyServiceConfiguration.class.getName()};
  }
}
  • 调用
@EnableMyService
public class TestApplication {
  public static void main(String[] args) {
    ConfigurableApplicationContext configurableApplicationContext =
        new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);
    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
    configurableApplicationContext.close();
  }
}

条件装配@Profile

在例子中加入@Profile("dev")启动会发生空指针异常

@Configuration
public class MyServiceConfiguration {
  @Bean
  @Profile("dev")
  public String test() {
    return "测试服务";
  }
}

指定启动profile

@EnableMyService
public class TestApplication {
  public static void main(String[] args) {
    ConfigurableApplicationContext configurableApplicationContext =
        new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).profiles("dev").run(args);
    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
    configurableApplicationContext.close();
  }
}

条件装配@Conditional

@Profile也是基于@Conditional来实现的

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
    String[] value();
}

Condition

class ProfileCondition implements Condition {
    ProfileCondition() {
    }

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            Iterator var4 = ((List)attrs.get("value")).iterator();

            Object value;
            do {
                if (!var4.hasNext()) {
                    return false;
                }

                value = var4.next();
            } while(!context.getEnvironment().acceptsProfiles((String[])((String[])value)));

            return true;
        } else {
            return true;
        }
    }
}

模仿写一个,Conditional

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {
  // Java 系统属性名称
  String key();
  // Java 系统属性值
  String value();
}

Condition

public class OnSystemPropertyCondition implements Condition {

  @Override
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    Map<String, Object> attributes =
        metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
    String propertyName = String.valueOf(attributes.get("key"));
    String propertyValue = String.valueOf(attributes.get("value"));
    String javaPropertyValue = System.getProperty(propertyName);
    return propertyValue.equals(javaPropertyValue);
  }
}
@Configuration
public class MyServiceConfiguration {
  @Bean
  @ConditionalOnSystemProperty(key = "user.name", value = "zhaozhihao")
  public String test() {
    return "测试服务";
  }
}
@EnableMyService
public class TestApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext configurableApplicationContext =
        new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);

    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
    configurableApplicationContext.close();
  }
}

Springboot自动装配

  • 定义:基于约定大于配置的原则,实现Spring组件自动装配
  • 装配:
    • 模式注解
    • @Enable模块
    • 条件装配
    • 工厂加载机制
      • 实现类:SpringFactoriesLoader
      • 配置:META-INF/spring.factories
  • 实现
    • 激活自动装配:@EnableAutoConfiguration
    • 实现自动装配:XXXAutoConfiguration
    • 配置自动装配:META-INF/spring.factories

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();

    public SpringFactoriesLoader() {
    }

    public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
        Assert.notNull(factoryClass, "'factoryClass' must not be null");
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
        if (logger.isTraceEnabled()) {
            logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
        }

        List<T> result = new ArrayList(factoryNames.size());
        Iterator var5 = factoryNames.iterator();

        while(var5.hasNext()) {
            String factoryName = (String)var5.next();
            result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
        }

        AnnotationAwareOrderComparator.sort(result);
        return result;
    }

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        String factoryClassName = factoryClass.getName();
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

    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()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
                        result.addAll((String)entry.getKey(), factoryClassNames);
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var9) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
            }
        }
    }

    private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
        try {
            Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
            if (!factoryClass.isAssignableFrom(instanceClass)) {
                throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");
            } else {
                return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();
            }
        } catch (Throwable var4) {
            throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);
        }
    }
}

spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration

# Failure analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer,\
org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer,\
org.springframework.boot.autoconfigure.session.NonUniqueSessionRepositoryFailureAnalyzer

# Template availability providers
org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\
org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider

自定义实现

  • 激活自动装配
@EnableAutoConfiguration
public class TestApplication {

  public static void main(String[] args) {
    ConfigurableApplicationContext configurableApplicationContext =
        new SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);
    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
    configurableApplicationContext.close();
  }
}
  • 实现自动装配
@Configuration //Spring 模式注解装配
@EnableMyService //Spring @Enable 模块装配
@ConditionalOnSystemProperty(key = "user.name", value = "zhaozhihao") //Spring 条件装配
public class MyServiceAutoConfiguration {
}
  • 配置自动装配实现

SpringApplication

准备阶段

节选自SpringApplication,从构造器中我们大概可以推断出准备阶段需要经过primarySources合规性校验、推断 Web 应用类型、加载应用上下文初始器、加载应用事件监听器、推断引导类等步骤。

public class SpringApplication {
    ...
    public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = this.deduceWebApplicationType();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }
    ...
}    

primarySources合规性校验

节选自SpringApplication,加载primarySources调用的是BeanDefinitionLoader

public class SpringApplication {
    ...
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }

        BeanDefinitionLoader loader = this.createBeanDefinitionLoader(this.getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }

        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }

        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }

        loader.load();
    }
    ...
}    

节选BeanDefinitionLoader,可见其可以通过xml/注解加载

class BeanDefinitionLoader {
    ...

    BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
        Assert.notNull(registry, "Registry must not be null");
        Assert.notEmpty(sources, "Sources must not be empty");
        this.sources = sources;
        this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
        this.xmlReader = new XmlBeanDefinitionReader(registry);
        if (this.isGroovyPresent()) {
            this.groovyReader = new GroovyBeanDefinitionReader(registry);
        }

        this.scanner = new ClassPathBeanDefinitionScanner(registry);
        this.scanner.addExcludeFilter(new BeanDefinitionLoader.ClassExcludeFilter(sources));
    }
    ...
}    

三种方式都是可以的,传入的source只要是@Component的派生注解就行了,@SpringBootApplication只是提供了一些默认配置的组合注解

@EnableAutoConfiguration
public class TestApplication {

  public static void main(String[] args) {
    // 方式一
    ConfigurableApplicationContext configurableApplicationContext =
        SpringApplication.run(TestApplication.class, args);
    String test = configurableApplicationContext.getBean("test", String.class);
    System.out.println(test);
    configurableApplicationContext.close();
    // 方式二
    //    Set<String> sources = new HashSet<>();
    //    sources.add(TestApplication.class.getName());
    //    SpringApplication springApplication = new SpringApplication();
    //    springApplication.setSources(sources);
    //    ConfigurableApplicationContext configurableApplicationContext =
    // springApplication.run(args);
    //    String test = configurableApplicationContext.getBean("test", String.class);
    //    System.out.println(test);
    //    configurableApplicationContext.close();

    // 方式三
    //    ConfigurableApplicationContext configurableApplicationContext =
    //        new
    // SpringApplicationBuilder(TestApplication.class).web(WebApplicationType.NONE).run(args);
    //    String test = configurableApplicationContext.getBean("test", String.class);
    //    System.out.println(test);
    //    configurableApplicationContext.close();
  }
}

推断Web应用类型

节选SpringApplication代码,推断逻辑在deduceWebApplicationType方法中,根据相关的类是否被装配来进行判断应用类型。显然源码提示REACTIVE和SERVLET共存的时候,优先SERVLET,所以在引入netty的时候要把tomcat排除出去。

public class SpringApplication {
    ...
    private WebApplicationType deduceWebApplicationType() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null)) {
            return WebApplicationType.REACTIVE;
        } else {
            String[] var1 = WEB_ENVIRONMENT_CLASSES;
            int var2 = var1.length;

            for(int var3 = 0; var3 < var2; ++var3) {
                String className = var1[var3];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return WebApplicationType.NONE;
                }
            }

            return WebApplicationType.SERVLET;
        }
    }
    ...
}    

除此之外,我们也可以通过配置强制选型

 springApplication.setWebApplicationType(WebApplicationType.SERVLET);

加载应用上下文初始器

节选自SpringApplication,通过SpringFactoriesLoader加载META-INF/spring.factories中的bean,通过AnnotationAwareOrderComparator.sort()方法进行排序加载

public class SpringApplication {
    ...
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }
    ...
}   

自定义MyFirstApplicationContextInitializer,通过实现Ordered接口进行排序

public class MyFirstApplicationContextInitializer
    implements ApplicationContextInitializer, Ordered {
  @Override
  public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
    System.out.println("MyFirstApplicationContextInitializer");
  }

  @Override
  public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE;
  }
}

自定义MySecondApplicationContextInitializer,通过@Order注解进行排序

@Order(Ordered.LOWEST_PRECEDENCE)
public class MySecondApplicationContextInitializer<C extends ConfigurableApplicationContext>
    implements ApplicationContextInitializer<C> {
  @Override
  public void initialize(C c) {
    System.out.println("MySecondApplicationContextInitializer");
  }
}

META-INF/spring.factories,此处顺序和加载顺序无关

# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.springbootlearning.test.context.MySecondApplicationContextInitializer,\
com.springbootlearning.test.context.MyFirstApplicationContextInitializer

执行

@SpringBootApplication
public class TestApplication {

  public static void main(String[] args) {
      SpringApplication.run(TestApplication.class,args);
  }
}

效果

加载应用事件监听器

实现原理类似加载应用上下文初始器,只不过装配是要等待监听的事件发生后

@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyFirstApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
  @Override
  public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    System.out.println("MyFirstApplicationListener");
  }
}
public class MySecondApplicationListener
    implements ApplicationListener<ContextRefreshedEvent>, Ordered {

  @Override
  public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    System.out.println("MySecondApplicationListener");
  }

  @Override
  public int getOrder() {
    return Ordered.LOWEST_PRECEDENCE;
  }
}

META-INF/spring.factories

# Application Listeners
org.springframework.context.ApplicationListener=\
com.springbootlearning.test.listener.MyFirstApplicationListener,\
com.springbootlearning.test.listener.MySecondApplicationListener

效果

推断引导类

节选SpringApplication代码,通过循环匹配栈内元素

public class SpringApplication {
    ...
    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
            ;
        }

        return null;
    }
    ...
}   

运行阶段

节选SpringApplication,通过查看源码,我们大致可以发现整个过程:加载SpringApplicationRunListeners,然后开始运行,伴随着各大监听事件的发生,相应的监听器被激活,加载各自Bean,特别地environmentPrepared后开始加载context

public class SpringApplication {
    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);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(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);
        }
    }
}

加载SpringApplicationRunListeners

利用 Spring 工厂加载机制,读取 SpringApplicationRunListener 对象集合,并且封装到组合类SpringApplicationRunListeners

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
        return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

运行SpringApplicationRunListeners

监听以下运行状态

public interface SpringApplicationRunListener {
    void starting();

    void environmentPrepared(ConfigurableEnvironment environment);

    void contextPrepared(ConfigurableApplicationContext context);

    void contextLoaded(ConfigurableApplicationContext context);

    void started(ConfigurableApplicationContext context);

    void running(ConfigurableApplicationContext context);

    void failed(ConfigurableApplicationContext context, Throwable exception);
}

监听Springboot、Spring事件

Spring Boot 通过 SpringApplicationRunListener 的实现类 EventPublishingRunListener 利用 Spring Framework 事件 API ,广播 Spring Boot 事件(initialMulticaster)

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
    private final SpringApplication application;
    private final String[] args;
    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        Iterator var3 = application.getListeners().iterator();

        while(var3.hasNext()) {
            ApplicationListener<?> listener = (ApplicationListener)var3.next();
            this.initialMulticaster.addApplicationListener(listener);
        }

    }

    public int getOrder() {
        return 0;
    }

    public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
    }

    public void contextPrepared(ConfigurableApplicationContext context) {
    }

    public void contextLoaded(ConfigurableApplicationContext context) {
        ApplicationListener listener;
        for(Iterator var2 = this.application.getListeners().iterator(); var2.hasNext(); context.addApplicationListener(listener)) {
            listener = (ApplicationListener)var2.next();
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware)listener).setApplicationContext(context);
            }
        }

        this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
    }

    public void started(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
    }

    public void running(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
    }

    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
        if (context != null && context.isActive()) {
            context.publishEvent(event);
        } else {
            if (context instanceof AbstractApplicationContext) {
                Iterator var4 = ((AbstractApplicationContext)context).getApplicationListeners().iterator();

                while(var4.hasNext()) {
                    ApplicationListener<?> listener = (ApplicationListener)var4.next();
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }

            this.initialMulticaster.setErrorHandler(new EventPublishingRunListener.LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }

    }

    private static class LoggingErrorHandler implements ErrorHandler {
        private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

        private LoggingErrorHandler() {
        }

        public void handleError(Throwable throwable) {
            logger.warn("Error calling ApplicationEventListener", throwable);
        }
    }
}

Spring Framework 事件/监听器编程模型

  • Spring 应用事件
    • 普通应用事件:ApplicationEvent
    • 应用上下文事件:ApplicationContextEvent
  • Spring 应用监听器
    • 接口编程模型:ApplicationListener
    • 注解编程模型:@EventListener
  • Spring 应用事广播器
    • 接口:ApplicationEventMulticaster
    • 实现类:SimpleApplicationEventMulticaster
      • 模式:同步或异步

代码

      // 创建上下文
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

      // 注册应用事件监听器
      context.addApplicationListener(event -> {
          System.out.println("监听到事件: " + event);
      });

      // 启动上下文
      context.refresh();
      // 发送事件PayloadApplicationEvent
      context.publishEvent("first");
      context.publishEvent("second");

      // 关闭上下文
      context.close();

效果

自定义运行监听器

MyRunListener

public class MyRunListener implements SpringApplicationRunListener {

    public MyRunListener(SpringApplication application, String[] args){}

    @Override
    public void starting() {
    System.out.println("1.MyRunListener starting");
    }

    @Override
    public void environmentPrepared(ConfigurableEnvironment environment) {
        System.out.println("2.MyRunListener environmentPrepared");
    }

    @Override
    public void contextPrepared(ConfigurableApplicationContext context) {
        System.out.println("3.MyRunListener contextPrepared");
    }

    @Override
    public void contextLoaded(ConfigurableApplicationContext context) {
        System.out.println("4.MyRunListener contextLoaded");
    }

    @Override
    public void started(ConfigurableApplicationContext context) {
        System.out.println("5.MyRunListener started");
    }

    @Override
    public void running(ConfigurableApplicationContext context) {
        System.out.println("6.MyRunListener running");
    }

    @Override
    public void failed(ConfigurableApplicationContext context, Throwable exception) {
        System.out.println("7.MyRunListener failed");
    }
}

META-INF/spring.factorie

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
com.springbootlearning.test.run.MyRunListener

引导类

@SpringBootApplication
public class TestApplication {

  public static void main(String[] args) {
      SpringApplication.run(TestApplication.class,args);
  }
}

效果

1.MyRunListener starting
2.MyRunListener environmentPrepared

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.1.RELEASE)

MyFirstApplicationContextInitializer
MySecondApplicationContextInitializer
3.MyRunListener contextPrepared
2019-12-08 23:02:15.139  INFO 3110 --- [           main] c.s.test.TestApplication                 : Starting TestApplication on zzh.local with PID 3110 (/Users/zhaozhihao/Documents/test/target/classes started by zhaozhihao in /Users/zhaozhihao/Documents/test)
2019-12-08 23:02:15.141  INFO 3110 --- [           main] c.s.test.TestApplication                 : No active profile set, falling back to default profiles: default
4.MyRunListener contextLoaded
2019-12-08 23:02:15.188  INFO 3110 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5be6e01c: startup date [Sun Dec 08 23:02:15 CST 2019]; root of context hierarchy
2019-12-08 23:02:15.736  INFO 3110 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
MyFirstApplicationListener
MySecondApplicationListener
2019-12-08 23:02:15.746  INFO 3110 --- [           main] c.s.test.TestApplication                 : Started TestApplication in 0.898 seconds (JVM running for 1.446)
5.MyRunListener started
6.MyRunListener running
2019-12-08 23:02:15.748  INFO 3110 --- [       Thread-4] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5be6e01c: startup date [Sun Dec 08 23:02:15 CST 2019]; root of context hierarchy
2019-12-08 23:02:15.749  INFO 3110 --- [       Thread-4] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

Spring MVC

图和步骤转载自原文连接

步骤

  • 发起请求到前端控制器(DispatcherServlet)
  • 前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
  • 处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
  • 前端控制器调用处理器适配器去执行Handler
  • 处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
  • Handler执行完成给适配器返回ModelAndView
  • 处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
  • 前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
  • 视图解析器向前端控制器返回View
  • 前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
  • 前端控制器向用户响应结果

基于配置

web.xml

    <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app-context.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

app-context.xml

    <context:component-scan base-package="com.test"/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

断点分析

  • HandlerMapping
  • HandlerAdapter
  • Controller
  • ViewResolver

基于注解

上述项目注解化改造

Servlet 3.0+ & Spring Framework 3.1 +就可以实现注解装配,Servlet SPI 的ServletContainerInitializer 被 Spring Framework 封装为SpringServletContainerInitializer

  • Spring SPI
    • 基础接口:WebApplicationInitializer
    • 编程驱动:AbstractDispatcherServletInitializer
    • 注解驱动:AbstractAnnotationConfigDispatcherServletInitializer

扫描

@ComponentScan(basePackages = "com.test")
public class DispatcherServletConfiguration {
}

WebMvcConfigurer

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/jsp/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

AbstractAnnotationConfigDispatcherServletInitializer

public class DefaultAnnotationConfigDispatcherServletInitializer extends
        AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() { // web.xml
        return new Class[0];
    }

    @Override
    protected Class<?>[] getServletConfigClasses() { // DispatcherServlet
        return new Class[]{DispatcherServletConfiguration.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

Controller

@Controller
public class HelloWorldController {
    @RequestMapping("/test")
    public String index() {
        return "index";
    }
}

其他相关MVC注解

注解 描述
@ModelAttribute 注册模型属性
@RequestHeader 读取请求头
@CookieValue 读取Cookie
@Valid 校验参数
@Validated 校验参数
@ExceptionHandler 异常处理
@ControllerAdvice 切面通知

Spring Boot 自动装配

自动装配DispatcherServlet

DispatcherServletAutoConfiguration(spring.factories)

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {
...
}

由源码可以分析出,装配DispatcherServletAutoConfiguration需要满足一些条件

  • @ConditionalOnWebApplication:需要SERVLET容器
  • @ConditionalOnClass:API需要DispatcherServlet
  • @AutoConfigureAfter(相对顺序):需要在ServletWebServerFactoryAutoConfiguration后加载
  • @AutoConfigureOrder(绝对顺序):Ordered.HIGHEST_PRECEDENCE,最小整数即最高优先级

自动装配WebMvc

WebMvcAutoConfiguration(spring.factories)

@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
		ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
...
	public static class WebMvcAutoConfigurationAdapter
			implements WebMvcConfigurer, ResourceLoaderAware {
...
		public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties,
				WebMvcProperties mvcProperties, ListableBeanFactory beanFactory,
				@Lazy HttpMessageConverters messageConverters,
				ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider) {
			this.resourceProperties = resourceProperties;
			this.mvcProperties = mvcProperties;
			this.beanFactory = beanFactory;
			this.messageConverters = messageConverters;
			this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider
					.getIfAvailable();
		}
...
	@Configuration
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {
	}
...
}

由源码可以分析出,装配WebMvcAutoConfiguration中有一个条件即@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),也就是说WebMvcConfigurationSupport在的时候就不会装配,而@EnableWebMvc注解的DelegatingWebMvcConfiguration就继承了WebMvcConfigurationSupport,所以当@EnableWebMvc存在的时候,我们可以自由定义,其实在WebMvcAutoConfiguration中也装配了继承DelegatingWebMvcConfiguration的EnableWebMvcConfiguration

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
...
}

Spring Boot 实现以及Filter&Interceptor&Aspect

类型 描述
Filter 过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次,随着web的销毁而销毁,filter初始化在dispathServlet前面,依赖注入的逻辑不要在filter中使用
Interceptor 拦截器基于Java的反射机制,属于面向切面编程(AOP)的一种运用
Aspect 日志,事务,请求参数安全验证等

引导类

@SpringBootApplication(scanBasePackages = "com.test")
public class SpringBootWebMvcBootstrap {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootWebMvcBootstrap.class, args);
    }
}

Controller

@RestController
public class HelloWorldController {
    @RequestMapping("")
    public String index() {
        return "index";
    }
}

Filter

@WebFilter(filterName = "MyFirstFilter", urlPatterns = "/*")
@Component
@Order(1)
public class MyFirstFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("MyFirstFilter init");
  }
  @Override
  public void doFilter(
      ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
    System.out.println("MyFirstFilter begin");
    filterChain.doFilter(servletRequest, servletResponse);
    System.out.println("MyFirstFilter end");
  }
  @Override
  public void destroy() {
    System.out.println("MyFirstFilter destroy");
  }
}
@WebFilter(filterName = "MySecondFilter", urlPatterns = "/*")
@Component
@Order(2)
public class MySecondFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    System.out.println("MySecondFilter init");
  }
  @Override
  public void doFilter(
      ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
    System.out.println("MySecondFilter begin");
    filterChain.doFilter(servletRequest, servletResponse);
    System.out.println("MySecondFilter end");
  }
  @Override
  public void destroy() {
    System.out.println("MySecondFilter destroy");
  }
}

Interceptor

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(
        new HandlerInterceptor() {
          @Override
          public boolean preHandle(
              HttpServletRequest request, HttpServletResponse response, Object handler)
              throws Exception {
            System.out.println("MyFirstInterceptor:preHandle");
            return true;
          }
          //postHandler方法的执行,当controller内部有异常,posthandler方法是不会执行的
          @Override
          public void postHandle(
              HttpServletRequest request,
              HttpServletResponse response,
              Object handler,
              @Nullable ModelAndView modelAndView)
              throws Exception {
            System.out.println("MyFirstInterceptor:postHandle");
          }
          @Override
          public void afterCompletion(
              HttpServletRequest request,
              HttpServletResponse response,
              Object handler,
              @Nullable Exception ex)
              throws Exception {
            System.out.println("MyFirstInterceptor:afterCompletion");
          }
        }).order(1).addPathPatterns("/");

      registry.addInterceptor(
              new HandlerInterceptor() {
                  @Override
                  public boolean preHandle(
                          HttpServletRequest request, HttpServletResponse response, Object handler)
                          throws Exception {
                      System.out.println("MySecondInterceptor:preHandle");
                      return true;
                  }
                  //postHandler方法的执行,当controller内部有异常,posthandler方法是不会执行的
                  @Override
                  public void postHandle(
                          HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler,
                          @Nullable ModelAndView modelAndView)
                          throws Exception {
                      System.out.println("MySecondInterceptor:postHandle");
                  }
                  @Override
                  public void afterCompletion(
                          HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler,
                          @Nullable Exception ex)
                          throws Exception {
                      System.out.println("MySecondInterceptor:afterCompletion");
                  }
              }).order(2).addPathPatterns("/");
  }
}

Aspect

@Aspect
@Component
@Order(1)
public class MyFirstAspect {
  @Pointcut("execution(* com.test.controller.*.*(..))")
  public void pointcut() {}

  @Before("pointcut()")
  public void begin() {
    System.out.println("MyFirstAspect:Before");
  }

  @After("pointcut()")
  public void commit() {
    System.out.println("MyFirstAspect:After");
  }

  @AfterReturning("pointcut()")
  public void afterReturning(JoinPoint joinPoint) {
    System.out.println("MyFirstAspect:AfterReturning");
  }

  @AfterThrowing("pointcut()")
  public void afterThrowing() {
    System.out.println("MyFirstAspect:AfterThrowing");
  }

  @Around("pointcut()")
  public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    try {
      System.out.println("MyFirstAspect:Around begin");
      return joinPoint.proceed();
    } catch (Throwable e) {
      e.printStackTrace();
      throw e;
    } finally {
      System.out.println("MyFirstAspect:Around end");
    }
  }
}
@Aspect
@Component
@Order(2)
public class MySecondAspect {
    @Pointcut("execution(* com.test.controller.*.*(..))")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void begin() {
    System.out.println("MySecondAspect:Before");
    }

    @After("pointcut()")
    public void commit() {
        System.out.println("MySecondAspect:After");
    }

    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint) {
        System.out.println("MySecondAspect:AfterReturning");
    }

    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("MySecondAspect:AfterThrowing");
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("MySecondAspect:Around begin");
            return joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        } finally {
            System.out.println("MySecondAspect:Around end");
        }
    }
}

本质也是Aspect

@ControllerAdvice(assignableTypes = HelloWorldController.class)
@Order(3)
public class MyFirstControllerAdvice {
    @ModelAttribute("acceptLanguage")
    public String acceptLanguage(@RequestHeader("Accept-Language") String acceptLanguage){
        System.out.println("切面参数:acceptLanguage");
        return acceptLanguage;
    }
}

效果

# Filter初始化只执行一次
MyFirstFilter init
MySecondFilter init
MyFirstFilter begin
MySecondFilter begin
MyFirstInterceptor:preHandle
MySecondInterceptor:preHandle
MyFirstAspect:Around begin
MyFirstAspect:Before
MySecondAspect:Around begin
MySecondAspect:Before
MyFirstControllerAdvice
MySecondAspect:Around end
MySecondAspect:After
MySecondAspect:AfterReturning
MyFirstAspect:Around end
MyFirstAspect:After
MyFirstAspect:AfterReturning
MyFirstAspect:Around begin
MyFirstAspect:Before
MySecondAspect:Around begin
MySecondAspect:Before
MySecondAspect:Around end
MySecondAspect:After
MySecondAspect:AfterReturning
MyFirstAspect:Around end
MyFirstAspect:After
MyFirstAspect:AfterReturning
MySecondInterceptor:postHandle
MyFirstInterceptor:postHandle
MySecondInterceptor:afterCompletion
MyFirstInterceptor:afterCompletion
MySecondFilter end
MyFirstFilter end

整体图示

Aspect图示

  • @Order(1):越小越先执行
  • around->before->around->after->afterReturning
  • 橙色:@Order(1),绿色:@Order(2)