Spring
Spring体系架构(基于4.x)
1、Core Container(核心容器)
该模块主要包含Core、Beans、Context和SpEL模块。其中Core和Beans是整个框架最基础的部分,提供IOC和依赖注入特性。这里最重要的概念就是BeanFactory,提供了以Factory模式的实现来消除对程序性
单例模式。
- Core:模块主要包含Spring框架最基本的核心工具类,Core是其他组件的基础核心。
- Beans:模块主要包含访问配置文件、创建/管理Bean以及IOC/DI相关的类。
- Context:继承了Beans的特性,主要为Spring提供大量的扩展,如国际化、事件机制、资源加载等待。ApplicationContext接口是Context模块的关键。
- SpEL:模块提供了一个强大的语言表达式。
———————————————————————————————————————————————————————
2、AOP and Instrumentation
提供符合AOP Alliance标准的面向切面编程的实现,可以让你定义如方法拦截器和切点,从而降低程序之间的耦合性。
- AspectJ模块:提供了与AspectJ的集成 。
- Instrumentation模块:提供用于某些应用程序服务器的类工具支持和类加载器实现 。
———————————————————————————————————————————————————————
3、Messaging
该模块具有来自Spring Integration项目的关键抽象,如Message,MessageChannel,MessageHandler等。它们构成基于消息的应用程序的基础。该模块还包括一组注释,用于将消息映射到方法,类似于基于Spring MVC注释的编程模型。
———————————————————————————————————————————————————————
4、Data Access/Integration
数据访问/集成层由JDBC,ORM,OXM,JMS和事务模块组成。
- JDBC模块:提供了JDBC抽象层,从而无需进行繁琐的JDBC编码和数据库特定错误代码(不同数据代码可能不同)的解析。
- 事务模块:支持对实现特殊接口的类以及所有POJO(普通Java对象)进行编程和声明式事务管理。
- ORM模块:该模块为当前流行的ORM(包括JPA,JDO和Hibernate)提供了集成层。使用ORM模块,可以将所有这些O/R映射框架与Spring提供的所有功能结合使用,如前面提到的事务管理功能。
- OXM模块:提供了一个抽象层,该抽象层支持Object/ XML映射实现,例如JAXB,Castor,XMLBeans,JiBX和XStream。
- JMS模块(Java Messaging Service):包含用于生成和使用消息的功能。从Spring Framework 4.1开始,提供了与Spring-Messaging模块集成。
———————————————————————————————————————————————————————
5、Web
Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供上下文支持。该模块包含Web、WebMVC、Web Socket和Web-Porlet模块。
- Web模块:提供了基本的面向Web的集成功能,如文件上传功能以及使用Servlet监听器和面向Web的应用程序上下文对IoC容器的初始化。
- WebMVC模块(也称为Web-Servlet模块):包含基于Spring的Model-View-Controller(MVC)支持和针对Web应用程序的Rest Web服务实现。
- Web-Portlet 模块(也称为Web-Portlet模块):提供Portlet环境中的MVC实现。
———————————————————————————————————————————————————————
6、Test
该模块支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
IoC
IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语言中也有应用,并非 Spring 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。
推荐阅读:https://www.zhihu.com/question/23277575/answer/169698662
Spring IoC的初始化过程:
1.从xml进行加载,解析为为BeanDefinition
2.注册到BeanFactory
bean的生命周期
在核心方法refresh中体现了bean的生命周期
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 1.准备刷新上下文环境 声明早期的监听器和事件,不需要手动调用publishEvent
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 2.告诉父类子类初始化Bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 3.对bean工厂进行填充属性 注册解析接口方式的监听器的BeanPostProcessor
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 4.留个子类去实现该接口
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 调用bean工厂的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 注册bean的后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化国际资源处理器
initMessageSource();
// Initialize event multicaster for this context.
// 创建事件多播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 模板方法,在容器刷新的时候可以自定义逻辑,不同的Spring容器做不同的事情。
onRefresh();
// Check for listener beans and register them.
// 注册监听器,广播early application events
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有剩余的(非懒加载)单例
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
对应中文版本:
1.Bean 容器找到配置文件中 Spring Bean 的定义
2.Bean 容器利用 Java Reflection API 创建一个Bean的实例
3.注入值
4.一堆Aware接口
5.BeanPostProcessor前置处理,执行postProcessBeforeInitialization()
方法
6.如果Bean实现了InitializingBean
接口,执行afterPropertiesSet()
方法。
7.init-method方法
8.BeanPostProcessor后置处理,执行postProcessAfterInitialization()
方法
9.当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean
接口,执行 destroy()
方法。
10.当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法
IoC源码阅读
循环依赖
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:
解决方法:
使用缓存,Spring中有三级缓存,如下面这个例子。
/***
* Spring --循环依赖实例DEMO :
* 帮助您更有效的理解Spring循环依赖源码
*/
public class MainStart {
private static Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/**
* 读取bean定义,当然在spring中肯定是根据配置 动态扫描注册
*/
public static void loadBeanDefinitions() {
RootBeanDefinition aBeanDefinition=new RootBeanDefinition(InstanceA.class);
RootBeanDefinition bBeanDefinition=new RootBeanDefinition(InstanceB.class);
beanDefinitionMap.put("instanceA",aBeanDefinition);
beanDefinitionMap.put("instanceB",bBeanDefinition);
}
public static void main(String[] args) throws Exception {
// 加载了BeanDefinition
loadBeanDefinitions();
// 注册Bean的后置处理器
// 循环创建Bean
for (String key : beanDefinitionMap.keySet()){
// 先创建A
getBean(key);
}
InstanceA instanceA = (InstanceA) getBean("instanceA");
instanceA.say();
}
// 一级缓存
public static Map<String,Object> singletonObjects=new ConcurrentHashMap<>();
// 二级缓存: 为了将 成熟Bean和纯净Bean分离,避免读取到不完整得Bean
public static Map<String,Object> earlySingletonObjects=new ConcurrentHashMap<>();
// 三级缓存
public static Map<String,ObjectFactory> singletonFactories=new ConcurrentHashMap<>();
// 循环依赖标识
public static Set<String> singletonsCurrennlyInCreation=new HashSet<>();
// 假设A 使用了Aop @PointCut("execution(* *..InstanceA.*(..))") 要给A创建动态代理
// 获取Bean
public static Object getBean(String beanName) throws Exception {
Object singleton = getSingleton(beanName);
if(singleton!=null){
return singleton;
}
// 正在创建
if(!singletonsCurrennlyInCreation.contains(beanName)){
singletonsCurrennlyInCreation.add(beanName);
}
// createBean
// 实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
Object instanceBean = beanClass.newInstance(); // 通过无参构造函数
// 创建动态代理 (耦合 、BeanPostProcessor) Spring还是希望正常的Bean 还是再初始化后创建
// 只在循环依赖的情况下在实例化后创建proxy 判断当前是不是循环依赖
singletonFactories.put(beanName, () -> new JdkProxyBeanPostProcessor().getEarlyBeanReference(earlySingletonObjects.get(beanName),beanName));
// 添加到二级缓存
// earlySingletonObjects.put(beanName,instanceBean);
// 属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
// 说明属性上面有Autowired
if(annotation!=null){
declaredField.setAccessible(true);
// byname bytype byconstrator
// instanceB
String name = declaredField.getName();
Object fileObject= getBean(name); //拿到B得Bean
declaredField.set(instanceBean,fileObject);
}
}
// 初始化 init-mthod
// 放在这里创建已经完了 B里面的A 不是proxy
// 正常情况下会再 初始化之后创建proxy
// 由于递归完后A 还是原实例,, 所以要从二级缓存中拿到proxy 。
if(earlySingletonObjects.containsKey(beanName)){
instanceBean=earlySingletonObjects.get(beanName);
}
// 添加到一级缓存 A
singletonObjects.put(beanName,instanceBean);
// remove 二级缓存和三级缓存
return instanceBean;
}
public static Object getSingleton(String beanName){
// 先从一级缓存中拿
Object bean = singletonObjects.get(beanName);
// 说明是循环依赖
if(bean==null && singletonsCurrennlyInCreation.contains(beanName)){
bean=earlySingletonObjects.get(beanName);
// 如果二级缓存没有就从三级缓存中拿
if(bean==null) {
// 从三级缓存中拿
ObjectFactory factory = singletonFactories.get(beanName);
if (factory != null) {
bean=factory.getObject(); // 拿到动态代理
//存到二级缓存
earlySingletonObjects.put(beanName, bean);
}
}
}
return bean;
}
}
为什么需要二级缓存?
一级缓存无法保证多线程下的一级缓存Bean的完整性
- 一级缓存和二级缓存相比:
二级缓存只要是为了分离成熟Bean和纯净Bean(未注入属性)的存放, 防止多线程中在Bean还未创建完成时读取到的Bean时不完整的。所以也是为了保证我们getBean是完整最终的Bean,不会出现不完整的情况。
- 一二三级缓存下二级缓存的意义:
二级缓存为了存储 三级缓存的创建出来的早期Bean, 为了避免三级缓存重复执行。
为什么需要三级缓存?
只用到二级缓存就可以解决循环依赖的问题了,但耦合度大,且多线程下读取性能不高,三级缓存存函数接口, 函数接口实现 创建动态代理调用BeanPostProcessor 。 为了避免重复创建, 调用把返回的动态代理对象或者原实例存储在二级缓存。
Spring是怎样避免读取到不完整Bean的?
使用缓存将完整的(一级缓存)和不完整的Bean(二级缓存)隔离,同时使用了synchronized方法进行同步(多线程下也安全)。
Spring事件
Spring事件体系包括三个组件:事件,事件监听器,事件广播器,使用观察者模式
事件可在最后容器刷新(finishRefresh()) 发布刷新事件(Spring cloud也是从这里启动的),可以在所有Bean创建完后做扩展。
事件
Spring内置事件
内置事件中由系统内部进行发布,只需注入监听器
Event | 说明 |
---|---|
ContextRefreshedEvent | 当容器被实例化或refreshed时发布.如调用refresh()方法, 此处的实例化是指所有的bean都已被加载,后置处理器都被激活,所有单例bean都已被实例化, 所有的容器对象都已准备好可使用. 如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而GenericApplicationContext则不支持) |
ContextStartedEvent | 当容器启动时发布,即调用start()方法, 已启用意味着所有的Lifecycle bean都已显式接收到了start信号 |
ContextStoppedEvent | 当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭的容器可以通过start()方法重启 |
ContextClosedEvent | 当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启或refresh |
RequestHandledEvent | 这只在使用spring的DispatcherServlet时有效,当一个请求被处理完成时发布 |
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:
所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:
AOP
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理,如下图所示:
当然你也可以使用 AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。Spring 提供了 AspectJ 的支持,但只用到的AspectJ的切点解析和匹配。
使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。
Advisor封装了Advise(around、before、after)和pointcut,精度可以到达方法。
AOP的使用:https://blog.csdn.net/qq_33369905/article/details/105828920
BeanFactory 和ApplicationContext区别
BeanFactory是Spring中的顶层接口,ApplicationContext是下层的接口。
public interface BeanFactory {...}
1、国际化
BeanFactory是不支持国际化功能的,因为BeanFactory没有扩展Spring中MessageResource接口。相反,由于ApplicationContext扩展了MessageResource接口,因而具有消息处理的能力(i18N)
2、强大的事件机制(Event)
基本上牵涉到事件(Event)方面的设计,就离不开观察者模式,
ApplicationContext的事件机制主要通过ApplicationEvent和ApplicationListener这两个接口来提供的,和java swing中的事件机制一样。即当ApplicationContext中发布一个事件的时,所有扩展了ApplicationListener的Bean都将会接受到这个事件,并进行相应的处理。
3、底层资源的访问
ApplicationContext扩展了ResourceLoader(资源加载器)接口,从而可以用来加载多个Resource,而BeanFactory是没有扩展ResourceLoader
4、对Web应用的支持
与BeanFactory通常以编程的方式被创建不同的是,ApplicationContext能以声明的方式创建,如使用ContextLoader。当然你也可以使用ApplicationContext的实现之一来以编程的方式创建ApplicationContext实例 。
5、延迟加载
1).BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。
2).BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
可以看到,ApplicationContext继承了BeanFactory,BeanFactory是Spring中比较原始的Factory,它不支持AOP、Web等Spring插件,而ApplicationContext不仅包含了BeanFactory的所有功能,还支持Spring的各种插件,还以一种面向框架的方式工作以及对上下文进行分层和实现继承。
BeanFactory是Spring框架的基础设施,面向Spring本身;而ApplicationContext面向使用Spring的开发者,相比BeanFactory提供了更多面向实际应用的功能,几乎所有场合都可以直接使用ApplicationContext而不是底层的BeanFactory
SpringBoot
filter,interceptor,controllerAdvice,aspect,controller执行顺序
1、filter,这是java的过滤器,和框架无关的,是所有过滤组件中最外层的,从粒度来说是最大的。
配置方式,有直接实现Filter+@component,@Bean+@configuration(第三方的filter)
2、interceptor,spring框架的拦截器
配置方式,@configuration+继承WebMvcConfigurationSupport类添加过滤器。
3、aspect,可以自定义要切入的类甚至再细的方法,粒度最小。加个注解用效果更佳。
4、controllerAdvice,是controller的增强,和ExceptionHandler一起用来做全局异常。
总结:
filter:和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了。
interceptor:可以控制请求的控制器和方法,但控制不了请求方法里的参数。
aspect : 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。