Spring介绍
Spring是一个开源轻量框架
Spring为简化企业级开发而生,使用Spring开发可以将Bean对象,Dao组件对象,Service组件对象等交给Spring容器来管理,这样使得很多复杂的代码在Spring中开发却变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。
Spring是一个IOC(DI)和AOP容器框架。它的特点如下:
非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
控制反转:IOC——Inversion of Control,指的是将对象的创建权交给Spring去创建。使用Spring之前,对象的创建都是由我们自己在代码中new创建。而使用Spring之后。对象的创建都是由给了Spring框架。
依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
面向切面编程:Aspect Oriented Programming——AOP,扩展功能不是修改源代码实现
容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库,spring在javaee三层结构中,每一层都提供不同的解决技术
- web层:springMVC
- service层:spring的ioc
-dao层:spring的jdbcTemplate
Bean的深入学习
概括的描述一下Bean背后的操作
- 解析applicationgContext.xml,将xml中定义的bean(如userService和userDao)解析成Spring内部的BeanDefinition,
- 以beanName(如userService)为key,BeanDefinition(如userService相应的BeanDefinition)为value存储到DefaultListableBeanFactory中的beanDefinitionMap(其实就是一个ConcurrentHashMap)中
- 同时将beanName存入beanDefinitionNames(List类型)中,然后遍历beanDefinitionNames中的beanName,进行bean的实例化并填充属性
- 在实例化的过程中,如果有依赖没有被实例化将先实例化其依赖,然后实例化本身,实例化完成后将实例存入单例bean的缓存中,当调用getBean方法时,到单例bean的缓存中查找,如果找到并经过转换后返回这个实例(如UserDao的实例),之后就可以直接使用了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMatpCsB-1597317929046)(C:\Users\11864\AppData\Roaming\Typora\typora-user-images\image-20200813121411091.png)]
什么是BeanDefinition
在Java中,一切皆对象。在JDK中使用java.lang.Class
来描述类这个对象。
在Spring中,存在bean这样一个概念,那Spring又是怎么抽象bean
这个概念,用什么类来描述bean这个对象呢?Spring使用BeanDefinition来描述bean。
BeanDefinition的源码
可以看出BeanDefinition是一个接口,继承了AttributeAccessor和BeanMetadataElement。
一个BeanDefinition描述了一个bean的实例,包括属性值,构造方法参数值和继承自它的类的更多信息。BeanDefinition仅仅是一个最简单的接口,主要功能是允许BeanFactoryPostProcessor 例如PropertyPlaceHolderConfigure 能够检索并修改属性值和别的bean的元数据(译注)。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
---------------------------属性---------------------------------
String SCOPE_SINGLETON = "singleton"; #bean的作用域:单例
String SCOPE_PROTOTYPE = "prototype"; #bean的作用域:多例
int ROLE_APPLICATION = 0;
//表示BeanDefinition是应用程序主要部分的角色提示。 通常对应于用户定义的bean。
int ROLE_SUPPORT = 1;//实际上就是说,我这个Bean是用户的,是从配置文件中过来的。
int ROLE_INFRASTRUCTURE = 2;//就是我这Bean是Spring自己的,和你用户没有一毛钱关系。
---------------------当前Bean父类名称get&set方法-------------------
String getParentName(); //如果父类存在,则返回当前Bean的父类的名称
void setParentName(String var1);//如果父类存在,设置这个bean定义的父定义的名称。
--------------当前Bean的className get&set方法---------------------
String getBeanClassName();//获得此bean的类名称
void setBeanClassName(String var1);//指定此bean定义的bean类名称。 类名称可以在bean factory后期处理中修改,通常用它的解析变体替换原来的类名称。
------------------------定义创建该Bean对象的工厂类--------------------
String getFactoryBeanName();//返回工厂bean的名字,如果有的话。
void setFactoryBeanName(String var1);//指定要使用的工厂bean(如果有的话)这是调用指定的工厂方法的bean的名称
--------------------------创建该Bean对象的工厂方法--------------------
String getFactoryMethodName();//如果存在,返回工厂方法名
void setFactoryMethodName(String var1);//如果有的话,指定工厂方法。这个方法先将通过构造函数参数被调用,或者没参数,将调用该方法的无参数构造。方法将在指定的工厂bean(如果有的话)上被调用,或者作为本地bean类的静态方法被调用
-------------------------bean的作用域-------------------------------
String getScope();//返回此bean的当前目标作用域的名称,如果没有确定,返回null
void setScope(String var1);//覆盖此bean的目标范围,指定一个新的范围名称。
----------------------------懒加载---------------------------------
//通过配置文件这个判断是否有开启<bean lazy-init="true/false">
boolean isLazyInit();//返回这个bean是否应该被延迟初始化,即不是在启动时立即实例化。只适用于单例bean。
void setLazyInit(boolean var1);//设置是否懒加载
----------------------------依赖关系--------------------------------
//<bean depends-on="">
String[] getDependsOn();//返回这个bean依赖的bean名称
void setDependsOn(String... var1);//设置这个bean依赖被初始化的bean的名字,bean工厂将保证这些bean首先被初始化。
----------------------是否是自动转配设置-----------------------------
// <bean autowire-candidate="true/false">
boolean isAutowireCandidate();//是否为被自动装配
void setAutowireCandidate(boolean var1);//设置这个bean是否是获得自动装配到其他bean的候选人。如果名称匹配,通过名称的自动装配将注入
----------------------是否是自动转配设置-----------------------------
boolean isPrimary();//是否为主候选bean 使用注解
void setPrimary(boolean var1);//返回这个bean是否是主要的autowire候选者。
----------------------返回此bean的构造函数参数值。--------------------
ConstructorArgumentValues getConstructorArgumentValues();//返回此bean的构造函数参数值。
----------------------获取普通属性集合-------------------------------
MutablePropertyValues getPropertyValues();//获取普通属性集合
boolean isSingleton();//是否是单例的
boolean isPrototype();//是否是多例的
boolean isAbstract();//是否是抽象类
int getRole();//获取这个bean的应用
String getDescription();//返回对bean定义的可读描述
String getResourceDescription();//返回该bean定义来自的资源的描述
BeanDefinition getOriginatingBeanDefinition();//返回原始的BeanDefinition
}
可以看到上面的很多属性和方法都很熟悉,例如类名、scope、属性、构造函数参数列表、依赖的bean、是否是单例类、是否是懒加载等,其实就是将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建。BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。BeanDefinition继承了AttributeAccessor,说明它具有处理属性的能力;BeanDefinition继承了BeanMetadataElement,说明它可以持有Bean元数据元素,作用是可以持有XML文件的一个bean标签对应的Object。
AttributeAccessor
AttributeAccessor接口定义了最基本的对任意对象的元数据的修改或者获取,主要方法有:
public interface AttributeAccessor {
void setAttribute(String var1, Object var2);//将name的定义的属性设置为提供的value值。
Object getAttribute(String var1);//获取标识为name的属性
Object removeAttribute(String var1);//删除标识为name的属性
boolean hasAttribute(String var1);//判断名字为name的属性是否存在
String[] attributeNames(); //返回所有属性的名称
}
BeanMetadataElement
BeanMetadataElement接口提供了一个getResource()方法,用来传输一个可配置的源对象。
public interface BeanMetadataElement {
Object getSource();//返回此元数据元素的配置源对象(可能为null)
}
BeanFactory
BeanFactory的概述:
表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在 Spring 中,BeanFactory是 IoC 容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等。BeanFactory 提供的高级配置机制,使得管理任何性质的对象成为可能。ApplicationContext 是 BeanFactory 的扩展,功能得到了进一步增强,比如更易与 Spring AOP 集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的 context 实现(如针对 web 应用的WebApplicationContext)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-stJOwudF-1597317929048)(C:\Users\11864\AppData\Roaming\Typora\typora-user-images\image-20200813154822923.png)]
BeanFactory的源码
从接口中的方法名可以很容易的理解每个方法的意图,最常用或者最常见的就是getBean方法,用于获取Bean的实例。BeanFactory是用于访问Spring Bean容器的根接口,是一个单纯的Bean工厂,也就是常说的IOC容器的顶层定义,各种IOC容器是在其基础上为了满足不同需求而扩展的,包括经常使用的ApplicationContext。
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;//是否是单例
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;//是否是多例
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
DefaultListableBeanFactory重要属性
DefaultListableBeanFactory是整个Bean加载的核心部分,是Spring注册及加载Bean的默认实现。下面列出了DefaultListableBeanFactory源代码中两个重要的属性,其中,bean的定义被解析成BeanDefinition,同时解析得到beanName,将beanName和BeanDefinition存储到beanDefinitionMap中,同时会将beanName存储到beanDefinitionNames中。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256);//以beanName(如userService)为key,BeanDefinition(如userService相应的BeanDefinition)为value存储到DefaultListableBeanFactory中的beanDefinitionMap
private volatile List<String> beanDefinitionNames = new ArrayList(256);//同时将beanName存入beanDefinitionNames(List类型)中,然后遍历beanDefinitionNames中的beanName,进行bean的实例化并填充属性
DefaultListableBeanFactory间接继承DefaultSingletonBeanRegistry,DefaultSingletonBeanRegistry中有如下属性,
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
singletonObjects用于存储单例bean的实例,getBean方法就是从这个Map里取实例对象。
BeanDefinition装载前奏曲
进入ClassPathXmlApplicationContext源码内部,发现ClassPathXmlApplicationContext有多个构造方法,跟踪代码可以发现,最终使用的是下面这个方法
ClassPathXmlApplicationContext构造方法源码
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);//parent指父ApplicationContext。setConfigLocations方法就是设置框架要加载的资源文件的位置
this.setConfigLocations(configLocations);//configLocations指Spring的xml配置文件
if (refresh) {//refresh指是否需要刷新,这个refresh决定了是否进行bean解析、注册及实例化
this.refresh();
}
}
进入refresh方法
refresh方法源码
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//容器预先准备,记录容器时间和标记
this.prepareRefresh();
//创建bean工厂,里面实现了BeanDefinition的装载
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//配置bean工厂的上下文信息,如类装载器等
this.prepareBeanFactory(beanFactory);
try {
//在BeanDefinition被装载后,提供一个修改BeanFactory的入口
this.postProcessBeanFactory(beanFactory);
//在bean初始化之前,提供对BeanDefinition修改入口,PropertyPlaceholderConfigurer在这里被调用
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册各种BeanPostProcessors,用于在bean被初始化时进行拦截,进行额外初始化操作
this.registerBeanPostProcessors(beanFactory);
//初始化MessageSource
this.initMessageSource();
//初始化上下文事件广播
this.initApplicationEventMulticaster();
//模板方法
this.onRefresh();
//注册监听器
this.registerListeners();
//初始化所有未初始化的非懒加载的单例Bean
this.finishBeanFactoryInitialization(beanFactory);
//发布事件通知
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
这个方法里面就是IOC容器初始化的大致步骤了。
上面步骤的第二步完成了BeanDefinition的装载,进入obtainFreshBeanFactory方法,这个方法的具体实现也在AbstractApplicationContext类中,代码如下所示。
obtainFreshBeanFactory方法源码
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
这里面主要关注refreshBeanFactory方法,这个方法在AbstractApplicationContext类中并未实现,具体实现在子类AbstractRefreshableApplicationContext中,代码如下。
refreshBeanFactory实现类的源码
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {//判断beanfoctory是否存在,如何存在就销毁。
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();//创建一个新的beanfoctory
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);//设置是否允许重写BeanDefinition,是否允许循环引用
this.loadBeanDefinitions(beanFactory);//BeanDefinition载入的入口
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
这个方法使用了final修饰,也就是不能被重写了。首先检查BeanFactory是否已经存在,如果存在则销毁并关闭,然后新建一个BeanFactory,其实就是一个DefaultListableBeanFactory,这个DefaultListableBeanFactory就是前面说的那个。然后进行BeanFactory的属性设置,设置是否允许重写BeanDefinition、是否允许循环引用,接着loadBeanDefinitions方法就是BeanDefinition载入的入口了,这个方法在AbstractRefreshableApplicationContext本类中并未实现,具体在其子类中实现,根据用途不同有多个实现子类。
loadBeanDefinitions实现类的源码
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
关注其中的最后一行的loadBeanDefinitions方法,具体实现也在AbstractXmlApplicationContext类中,代码如下所示。
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);
}
}
这个方法里就是根据给定的xml配置文件进行后续的解析及装载。继续跟踪代码,进入reader.loadBeanDefinitions(configLocations),这个XmlBeanDefinitionReader用于从xml文件中读取Bean的定义。接下来的代码有很多跳转,大部分都是不重要的,直接追踪到重要代码,代码如下。代码实现在XmlBeanDefinitionReader下
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
这里只要关注doLoadBeanDefinitions方法,进入其实现,代码如下。
doLoadBeanDefinitions源码
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = this.doLoadDocument(inputSource, resource);
return this.registerBeanDefinitions(doc, resource);
} catch (BeanDefinitionStoreException var4) {
throw var4;
} catch (SAXParseException var5) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
} catch (SAXException var6) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
} catch (ParserConfigurationException var7) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
} catch (IOException var8) {
throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
} catch (Throwable var9) {
throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
}
}
看try块中的代码,可以看到,Spring将xml配置文件转成Document,这里使用了SAX对XML的解析,至于里面是如何解析可以后续针对性的研究。接着进入registerBeanDefinitions方法,后续又有很多代码的跳转,先不一一关注,直接进入重要代码,代码如下。代码在DefaultBeanDefinitionDocumentReader类中
parseBeanDefinitions源码
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
可以看到就是对Document中元素、节点的不断解析。这里的解析分成了两条路线,一个是默认标签的解析,如Spring自己定义的标签;一个是对自定义标签的解析,如自定义的标签。这里先关注默认标签的解析,进入parseDefaultElement方法,代码如下。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
代码中分别对import、alias、bean、beans标签进行解析,最终将解析得到的BeanDefinition存入DefaultListableBeanFactory中的beanDefinitionMap中。
doRegisterBeanDefinitions源码
- 通过BeanDefinitionParserDelegate进行解析,返回beanHolder
- BeanDefinitionParserDelegate装饰beanHolder
- 注册Bean
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
this.preProcessXml(root);
this.parseBeanDefinitions(root, this.delegate);
this.postProcessXml(root);
this.delegate = parent;
}