Spring/SpringBoot核心概念

102 阅读6分钟

参考

Spring

概述

MVC

  • Model:模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
  • View:视图代表模型包含的数据的可视化。
  • Controller:控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

  1. DispatcherServlet接收客户请求
  2. DispatcherServlet查询HandlerMapping
  3. HandlerMapping通过请求路径(URL)确定处理的Handler,并返回给DispatcherServlet
  4. DispatcherServlet执行前置拦截器Interceptor,如果通过执行第5步
  5. Hanlder请求Service,准备Model数据
  6. Handler执行后置拦截Interceptor,返回ModelAndView
  7. DispatcherServletModelAndView发给ViewResolverViewResolver 的主要作用是对ModelAndView解析,把一个逻辑上的视图名称解析为一个真正的视图
  8. DispatcherServlet将Model发给View接口,View接口的作用是用于处理视图进行渲染。

IOC

注解注入

AOP

代理实现

继承实现

SpringBoot

特点

  • 基于Spring

实践

依赖注入问题

  • 通过构造函数注入
    • 构造函数只能有一个,如果有多个的话,就必须有一个无参的构造函数,此时,Spring会调用无参的构造函数
    • 构造函数的参数,都要在Spring容器中存在
  • 实现BeanPostProcessor接口(参考ApplicationContextAwareProcessor)
    • 可以扩展代理对象
    • 可以初始化调用方法(在初始化前后的后置处理方法中)

  • BeanFactoryPostProcessor接口:修改BeanFactory,在Bean实例化前执行
  • BeanDefinitionRegistryPostProcessor(继承了BeanFactoryPostProcessor接口):动态注册对象
  • BeanDefinitionRegistry:注册Bean到容器中

启动加载

实现

常用注解

  • @SpringBootApplication
  • @ComponentScan:配置扫描包
  • @Configuration
  • @ConfigurationProperties: 直接注入到类中
  • @PropertySource:设置配置文件
  • @PropertySources:设置多个配置文件路径
  • @Value
  • @SpringBootConfiguration
  • @Conditional:设置注入条件(条件类实现Condition接口的matches方法,返回true才装配),可以设置多个
  • @ConditionalOnBean:注解或Bean存在才装配
  • @ConditionalMissingBean:与上面相反,不存在才装配
  • @ConditionalOnClass:Class存在装配
  • @ConditionalOnMissingClass
  • @ConditionalOnExpression:表达式true装配
  • @ConditionalOnJava:JVM版本号匹配装配
  • @ConditionalOnWebApplication
  • @ConditionalOnNotWebApplication
  • @ConditionalOnResorce:资源存在装配
  • @ConditionalOnProperty:配置存在装配
  • @EnableConfigurationProperties:启用一个特性,可以把配置文件的属性注入到bean中
  • @EnableAsync:使能异步执行(@Async配合),依赖于注解@Import的实现
  • @Import:导入一个或多个类,会被Spring容器托管,或者配置类(配置类里面的bean都会被容器托管)

重要注解原理

@Confiuration

@Import

ImportSelector接口:方法的返回值必须是一个class(全称),并会被Spring容器托管

public interface ImportSelector {
	/**
	 * Select and return the names of which class(es) should be imported based on
	 * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
	 */
	String[] selectImports(AnnotationMetadata importingClassMetadata);
}

ImportBeanDefinitionRegistrar接口:接口方法入参有个一个BeanDefinitionRegistry,可以用来在容器中注入bean,可以通过实现此接口实现动态注入

public interface ImportBeanDefinitionRegistrar {
	/**
	 * Register bean definitions as necessary based on the given annotation metadata of
	 * the importing {@code @Configuration} class.
	 * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
	 * registered here, due to lifecycle constraints related to {@code @Configuration}
	 * class processing.
	 * @param importingClassMetadata annotation metadata of the importing class
	 * @param registry current bean definition registry
	 */
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

}

EnableAutoConfiguration

实现:

  1. ImportSelector接口的方法返回值都会被纳入到Spring容器管理中
  2. SpringFactoriesLoader类可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置,加载bean对象
  3. 可以设置排除配置(exclude)

@EnableAutoConfiguration ->EnableAutoConfigurationImportSelector: (selectImports -> getCandidateConfigurations) -> SpringFactoriesLoader.loadFactoryNames

public String[] selectImports(AnnotationMetadata metadata) {
	AnnotationAttributes attributes = getAttributes(metadata);
	List<String> configurations = getCandidateConfigurations(metadata,
			attributes);
	configurations = removeDuplicates(configurations);
	Set<String> exclusions = getExclusions(metadata, attributes);
	configurations.removeAll(exclusions);
	configurations = sort(configurations);
	recordWithConditionEvaluationReport(configurations, exclusions);
	return configurations.toArray(new String[configurations.size()]);
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    
    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
    ArrayList result = new ArrayList();

    while(urls.hasMoreElements()) {
        URL url = (URL)urls.nextElement();
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }

    return result;
}

spring-boot-autoconfigure包下有大量的自动配置(有一个默认的META-INF/spring.factories文件),比如:

// 存在Gson类条件
@Configuration
@ConditionalOnClass(Gson.class)
public class GsonAutoConfiguration {

    // 没有Gson bean情况下加载
	@Bean
	@ConditionalOnMissingBean
	public Gson gson() {
		return new Gson();
	}

}

RedisAutoConfiguration

事件监听

  • 自定义事件:一般是继承ApplicationEvent抽象类
  • 定义事件监听器:一般实现ApplicationListener接口
  • 发布事件:
    • ApplicationContext.publishEvent
    • 使用配置context.listener.classesDelegatingApplicationListener会解析
    • 在方法上加@EventListener注解,并将类加入容器管理:EventListenerMethodProcessorEventListenerFactory解析处理

org.springframework.context.event包下有一系列的事件

重要类

  • ApplicationContextInitializer:接口是Spring容器执行refreshed之前的一个回调,使用步骤
    • 写一个实现此接口的类
    • 注册ApplicationContextInitializer(使用addInitializers或配置项context.initializer.classes或者spring.factories添加配置)
  • CommandLineRunner: Spring容器初始完后最后一个回调

Spring Boot运行流程

  1. 判断是否是Web环境
  2. 加载所有classpath下面的META-INF/spring-factories:ApplicationContextInitializer
  3. 加载所有classpath下面的META-INF/spring-factories:ApplicationListener
  4. 推断main方法所在的类
  5. 开始执行run方法
  6. 设置java.awt.headless系统变量
  7. 加载所有classpath下面的META-INF/spring.factories:SpringApplicationRunListener
  8. 执行所有SpringApplicationRunListenerstarted方法
  9. 实例化ApplicationArguments对象
  10. 创建environment
  11. 配置environment,主要是把run方法的参数配置到environment
  12. 执行所有SpringApplicationRunListenerenvironmentPrepared方法
  13. 如果不是Web环境,但是是Web的environment,则把这个Web的environment转换为标准的environment
  14. 打印Banner
  15. 初始化ApplicationContext,如果是Web环境,则实例化ApplicationConfigEmbeddedWebApplicationContext对象,否则实例化
  16. 如果beanNameGenerator不为空,就把beanNameGenerator对象注入到context里面去
  17. 回调所有的ApplictionContextInitializer方法
  18. 执行所有SpringApplicationRunListenercontextPrepared方法
  19. 依次往spring容器中注入:ApplicationArgumentsBanner
  20. 加载所有的源到context里面去
  21. 执行所有SpringApplicationRunListenercontextPrepared方法
  22. 执行contextrefresh方法,并调用contextregister$shutdownHook方法
  23. 回调,获取容器中的ApplicationRunnerCommandLineRunner接口,然后排序,依次调用
  24. 执行所有的SpringApplicationRunListenerfinished方法

静态资源

  1. src/main/webapp目录下,直接访问
  2. 默认静态资源路径是:classpath:[/META-INF/resources, /resources/, /static/,/public/]
  3. 可以通过spring.resources.staticLocation配置项修改默认静态资源路径

Servlet

SpringBoot中使用servletAPI: 方法一(Servlet3可以使用):

  1. 编写servlet,然后加上对应的注解
  2. 启用@ServletComponentScan注解

方法二(Servlet2.5以下可以):

  1. 编写servlet,然后加上相应的注解
  2. 装配相应的bean到spring容器中:
    • servlet -> ServletRegistrationBean
    • filter -> FilterRegistrationBean
    • listener -> ServletListenerRegistrationBean

拦截器使用步骤

  1. 写一个拦截器,实现HandlerInterceptor接口
  2. 写一个类,继承WebMvcConfigurerAdapter抽象类,然后重写addInterceptors,并调用registry.addInterceptor把上一步拦截器加进入

日志

  • Springboot默认的日志级别是info
  • 可以通过logging.level.*=debug来设置,*可以是包名,也可以是某个类
  • 日志级别有TRACE,DEBUG,INFO,WARN,ERROR,FAITAL,OFF

监控和度量

Actuator

  • 自定义健康状态监测,实现HealthIndicator接口,并纳入Spring容器管理
  • CounterService:计数服务
  • GuageService:统计某个值
  • MetricWriter:不仅可以写到界面,也可以输出到jmx,redis等
  • JDK监控工具:jconsole,jmc,jvisualvm(需要单独安装插件)

测试

Spring自动配置