Spring框架
什么是Spring
Spring是一个支持快速开发JavaEE应用程序的框架。它提供了一系列底层容器和基础设施,可以和常用的开源框架无缝集成
什么是容器
容器是一种为某种特定组件的运行提供必要支持的软件环境,例如Tomcat就是一个Servlet容器,它可以为Servlet提供运行环境。Docker也是容器,它提供了必要的Linux系统环境,以便运行一个Linux进程。
Spring的核心就是提供了一个IOC容器,它可以管理所有的轻量级JavaBean组件,提供的底层服务包括组件的生命周期管理,配置管理,组装服务,Aop支持,包括建立在AOP基础之上的声明式事务管理
IOC的底层原理
三大核心接口
-
•
BeanDefinition: 定义了一个Bean相关配置的各种信息,比如当前Bean的构造器参数,属性以及一些其他的信息
-
•
BeanDefinitionReader: 定义了一些读取配置文件的方法,支持使用Resource和String的位置参数加载方法
-
•
BeanFactory: 访问Bean容器的顶层接口,我们经常用到的ApplicationContext接口也实现了BeanFactory
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdownApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); ac.getBean(); // BeanFactory.getBean(resource/string); //classpath: resource string:name id
IOC初始化三大步骤
IOC的整个初始化流程大概可以分为三大步
-
1.
定位: 寻找需要初始化的Bean
-
2.
加载: 将需要初始化的Bean进行解析封装
-
3.
注册: 将记载好的Bean放入IOC容器,也就是放到集合(Map)里
定位
Spring中配置支持以下六种来源:
-
1.
classpath
-
2.
annotation
-
3.
network
-
4.
filesystem
-
5.
servletContext
接下来我们以最常用的一种方式作为入口来分析一下定位的流程
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
ac.getBean("beanName");
ac.getBean(MyBean.class);
通过进入构造方法得知其最终会调用一个父类的 refresh() 方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh();
}
}
我们进入refresh
方法 ,可以看到 refresh方法会构建一个BeanFactory对象
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
进入 obtainFreshBeanFactory
方法 又会调用一个 refreshBeanFactory()
的方法
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
} catch (IOException var2) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
}
}
该方法最终会调用 AbstractRefreshableApplicationContext的 refreshBeanFactory方法,该方法最终会构建 DefaultListableBeanFactory
对象, 构建完成后,调用 loadBeanDefinitions
方法开始加载Bean对象,该方法会调用AbstractXmlApplicationContext
的 loadBeanDefinitions
方法, 在该方法中会构建 XmlBeanDefinitionReader
对象
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// xml bean对应读取工具 ---》解析XML
// beanDefinitionReader: 专门用来读取bean定义, xml
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
继续进入loadBeanDefinitions()
方法,该方法判断了两种情况,一种是根据resource类型来判断 一种是根据String类型来判断,这里其实跟我们传递的参数有关系,如果我们传入的是一个String的参数 例如 new ClassPathXmlApplicationContext("spring.xml"); // 传入的类型是Resource 或者是String
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
注意看到这里又要构建两个对象
Document doc = this.doLoadDocument(inputSource, resource);
int count = this.registerBeanDefinitions(doc, resource);
第一个就是把resource转换成document对象,然后调用另一个方法准备注册Bean
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) { //解析import节点
this.importBeanDefinitionResource(ele); // 最终会重新调用loadBeanDefnitions
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate); // 解析bean并且进行注册
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
小结
IOC的底层原理,IOC的源码实现流程
我理解的IOC的主流程主要是三个步骤,定位(找配置文件),加载(解析配置文件), 注册(将bean添加到beanDefinitionMap)
详细版
在创建容器对象的时候,例如构建ClassPathXmlApplicationContext对象的时候,会调用一个refresh()方法,该方法会初始化并构建BeanFactory,在构建factory的过程中,会创建一个 XmlBeanFactoryReader对象,该对象会将当前传递的文件参数构建成resource对象开始加载,加载的时候会使用 BeanDefinitionParserDelegate
类中定义好的 元数据 加载配置信息,构建BeanDefnition对象,且最终执行 registerBeanDefinition方法完成注册,注册时将对象添加到DefaultListableBeanFactory类中的 beanDefinitionMap中,该map的key是beanName 值是 BeanDefnition,且添加的时候会判断当前Bean是否已经存在,根据情况判断是否可以覆盖。
Aop的底层原理
Aop的底层原理是基于JDK的动态代理, 但是如果需要代理的类没有实现任何接口则无法使用jdk动态代理,这个时候Spring会自动切换成cglib模式,该模式基于字节码技术实现,其原理就是通过在运行过程中使用字节码生成一个子类去继承我们需要进行代理的类,有点类似于静态代理的实现,该字节码生成的类就是代理,代理原来的类去执行业务。