参考
- Spring In Action
- Spring Boot In Action
- Spring Microservice in Action
- Spring Rest
- Spring官网
- 架构探险:从零开始写Java Web框架
- 关于 Spring AOP (AspectJ) 你该知晓的一切
- 关于Spring IOC (DI-依赖注入)你需要知道的一切
- Spring Boot实战与原理分析视频课程
- Spring源码阅读
- 字节码增强技术探索
- ViewResolver 视图解析器
Spring
概述
MVC
- Model:模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
- View:视图代表模型包含的数据的可视化。
- Controller:控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
DispatcherServlet接收客户请求DispatcherServlet查询HandlerMappingHandlerMapping通过请求路径(URL)确定处理的Handler,并返回给DispatcherServletDispatcherServlet执行前置拦截器Interceptor,如果通过执行第5步Hanlder请求Service,准备Model数据Handler执行后置拦截Interceptor,返回ModelAndViewDispatcherServlet将ModelAndView发给ViewResolver。ViewResolver的主要作用是对ModelAndView解析,把一个逻辑上的视图名称解析为一个真正的视图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
实现:
ImportSelector接口的方法返回值都会被纳入到Spring容器管理中- SpringFactoriesLoader类可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置,加载bean对象
- 可以设置排除配置(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.classes:DelegatingApplicationListener会解析 - 在方法上加
@EventListener注解,并将类加入容器管理:EventListenerMethodProcessor、EventListenerFactory解析处理
org.springframework.context.event包下有一系列的事件
重要类
ApplicationContextInitializer:接口是Spring容器执行refreshed之前的一个回调,使用步骤- 写一个实现此接口的类
- 注册
ApplicationContextInitializer(使用addInitializers或配置项context.initializer.classes或者spring.factories添加配置)
CommandLineRunner: Spring容器初始完后最后一个回调
Spring Boot运行流程
- 判断是否是Web环境
- 加载所有classpath下面的META-INF/spring-factories:
ApplicationContextInitializer - 加载所有classpath下面的META-INF/spring-factories:
ApplicationListener - 推断
main方法所在的类 - 开始执行
run方法 - 设置java.awt.headless系统变量
- 加载所有classpath下面的META-INF/spring.factories:
SpringApplicationRunListener - 执行所有
SpringApplicationRunListener的started方法 - 实例化
ApplicationArguments对象 - 创建
environment - 配置
environment,主要是把run方法的参数配置到environment - 执行所有
SpringApplicationRunListener的environmentPrepared方法 - 如果不是Web环境,但是是Web的
environment,则把这个Web的environment转换为标准的environment - 打印
Banner - 初始化
ApplicationContext,如果是Web环境,则实例化ApplicationConfigEmbeddedWebApplicationContext对象,否则实例化 - 如果
beanNameGenerator不为空,就把beanNameGenerator对象注入到context里面去 - 回调所有的
ApplictionContextInitializer方法 - 执行所有
SpringApplicationRunListener的contextPrepared方法 - 依次往spring容器中注入:
ApplicationArguments,Banner - 加载所有的源到
context里面去 - 执行所有
SpringApplicationRunListener的contextPrepared方法 - 执行
context的refresh方法,并调用context的register$shutdownHook方法 - 回调,获取容器中的
ApplicationRunner,CommandLineRunner接口,然后排序,依次调用 - 执行所有的
SpringApplicationRunListener的finished方法
静态资源
- src/main/webapp目录下,直接访问
- 默认静态资源路径是:classpath:[/META-INF/resources, /resources/, /static/,/public/]
- 可以通过spring.resources.staticLocation配置项修改默认静态资源路径
Servlet
SpringBoot中使用servletAPI:
方法一(Servlet3可以使用):
- 编写
servlet,然后加上对应的注解 - 启用
@ServletComponentScan注解
方法二(Servlet2.5以下可以):
- 编写
servlet,然后加上相应的注解 - 装配相应的bean到spring容器中:
servlet->ServletRegistrationBeanfilter->FilterRegistrationBeanlistener->ServletListenerRegistrationBean
拦截器使用步骤
- 写一个拦截器,实现
HandlerInterceptor接口 - 写一个类,继承
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(需要单独安装插件)