网站首页 > 基础教程 正文
一、简介。
在面试的时候经常会被问道IOC/DI的原理,也即是对源码的理解,有没有看过Spring IOC容器的源码,同时对spring源码的学习和研究,能够提升对spring实现思想的理解,强化Java编程功底,学习其优秀的架构思想。本问主要是对IOC容器源码的分析。
二、Spring组成。
Spring源码主要有beans、context、core、aop、web等模块组成。如下图所示:
通过一个小实例来简单介绍spring简单的使用。先通过xml的形式来使用bean的配置。配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
//bean的注册,注入
<bean id="myBean" class="com.spring.source.bean.MyBean"></bean>
</beans>
代码如下:
package com.spring.source.bean;
public class MyBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
获取bean的代码:
package com.spring.source.bean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class MyBeanTest {
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("application.xml"));
MyBean bean = (MyBean) beanFactory.getBean("myBean");
System.out.println(bean.getTestStr());
}
可以看到XmlBeanFactory类的继承关系。XmlBeanFactory继承DefaultListableBeanFactory实现。
对这些类进行介绍,理解它们的作用。
1、类DefaultListableBeanFactory是整个bean加载的核心类之一,是spring注册以及加载bean的默认实现。DefaultListableBeanFactory和XmlBeanFactory不同之处是XmlBeanFactory使用了自定义的XML读取器XmlBeanDefinitionReader。XmlBeanDefinitionReader是对BeanDefinitionReader的个性化实现。一个基于bean定义的元数据的成熟的bean工厂,可以通过bena的后置处理器进行扩展。
2、AliasRegistry:定义对alias的增删改等操作,对bean的别名的操作,是一个超级接口,bean的注册都实现此接口。
3、SimpleAliasRegistry:对AliasRegistry接口的简单实现。要基于BeanDefinitionRegistry实现。
4、BeanDefinitionRegistry:是用于bean定义、注册的接口。通常由内部使用AbstractBeanDefinition层次结构的bean工厂实现。此接口是Spring bean工厂包中唯一封装bean定义、注册的接口。
5、SingletonBeanRegistry:此接口定义了对单例模式bean的注册和获取。
6、DefaultSingletonBeanRegistry:对SingletonBeanRegistry的实现。允许注册单例实例,bean的所有调用者都应该共享这些实例,这些实例可以通过bean名称获得
7、BeanFactory:spring bean容器的根接口。这个接口由包含许多bean定义的对象实现,每个定义和注册都有唯一的一个名字标识。根据bean的定义,工厂返回单例模式的bean还是原型模式的bean。
8、AbstractAutowireCapableBeanFactory:这是一个抽象的bean工厂类,具有由RootBeanDefinition类指定的全部功能。提供bean创建(带有构造函数解析)、属性填充、连接(包括自动装配)和初始化。处理运行时bean引用、解析托管集合、调用初始化方法等。支持自动装配构造函数、按名称的属性和按类型的属性
9、ConfigurableListableBeanFactory:配置接口将由大多数可列bean工厂实现。除了ConfigurableBeanFactory,它还提供了分析和修改bean定义以及预实例化单例的工具。
10、AutowireCapableBeanFactory:能够自动装配的bean工厂将实现BeanFactory接口的扩展,前提是它们希望为现有bean实例公开此功能。
11、AbstractBeanFactory:实现BeanFactory接口的抽象的基类,提供ConfigurableBeanFactory SPI的全部功能。不假设有一个可列出的bean工厂:因此也可以用作bean工厂实现的基类,这些实现从一些后端资源(其中bean定义访问是一个昂贵的操作)获取bean定义。
12、ConfigurableBeanFactory :被大多数bean工厂实现的配置接口。提供了配置一个bean工厂的工具。除了BeanFactory接口中bean工厂客户端的方法
13、HierarchicalBeanFactory:由bean工厂实现的子接口可以是层次结构的一部分。
14、ListableBeanFactory:bean工厂将实现BeanFactory接口的扩展,这些bean工厂可以枚举它们的所有bean实例,而不是按照客户端的请求逐个尝试bean查找。预加载所有bean定义(例如基于xml的工厂)的BeanFactory实现可以实现此接口。
15、FactoryBeanRegistrySupport:支持单例注册中心的基类,这些注册中心需要处理FactoryBean实例,与DefaultSingletonBeanRegistry的单例管理集成。
主要介绍了XmlBeanFactory类的继承关系,接下来主要介绍根据xml配置文档bean的定义和注册实现过程。
三、bean的定义、注册过程。
首先根据下图先大致了解一下bean的定义和注册过程。如下图:
1、首先使用XmlBeanFactory加载、读取spring的xml配置文档。
//使用给定的输入流创建一个新的XmlBeanFactory,该输入流必须使用DOM进行解析
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
//最终创建AbstractAutowireCapableBeanFactory抽象类
super(parentBeanFactory);
//从指定的xml文件加载bean
this.reader.loadBeanDefinitions(resource);
}
2、XmlBeanDefinitionReader类中的方法,从制定的xml配置文件中加载bean
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//获取xml配置文件的输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//真正根据指定的xml文件加载bean
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
3、真正根据指定的xml文件加载bean
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//使用配置的DocumentLoader实际加载指定的文档。
Document doc = doLoadDocument(inputSource, resource);
// 注册给定DOM文档中包含的bean定义
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
4、使用配置的DocumentLoader实际加载指定的文档
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
5、注册给定DOM文档中包含的bean定义
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建DOM的读取器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获取备案的数量
int countBefore = getRegistry().getBeanDefinitionCount();
//此方法实现根据“spring-beans”XSD解析bean定义
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回注册的bean的数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
6、注册根节点为<beans>元素的bean
protected void doRegisterBeanDefinitions(Element root) {
//任何嵌套的元素都将导致此方法中的递归。为了正确地传播和保存缺省属性,请跟踪当前(父)委托,它可能为空。创建新的(子)委托,并使用对父委托的引用进行回退,然后最终将this.delegate重置为其原始(父)引用。这种行为模拟了一堆委托,实际上并不需要委托
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//前置处理
preProcessXml(root);
//解析bean定义
parseBeanDefinitions(root, this.delegate);
//后置处理
postProcessXml(root);
this.delegate = parent;
7、解析文档根级别的元素:“import”、“alias”、“bean”
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)) {
//解析默认的DOM元素
parseDefaultElement(ele, delegate);
}
else {
//解析自定义的DOM元素
delegate.parseCustomElement(ele);
}
}
}
}
else {
//解析自定义的DOM元素
delegate.parseCustomElement(root);
}
}
8、解析自定义的DOM元素
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
//DefaultBeanDefinitionDocumentReader使用的基本接口,用于处理Spring XML配置文件中的自定义名称空间
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
9、通过委托给为该元素注册的BeanDefinitionParser来解析提供的元素
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
10、AbstractBeanDefinitionParser类中的解析器,解析自定义的bean。
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
//包含名称和别名的bean定义的Holder。可以注册为内部bean的占位符。还可以用于内部bean定义的编程注册。如果您不关心BeanNameAware之类的东西,注册RootBeanDefinition或ChildBeanDefinition就足够了。
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
//
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
11、使用提供的BeanDefinitionRegistry注册表注册所提供的BeanDefinitionHolder bean。
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry);
}
12、上边是解析自定义标签bean的注册过程。现在分析根据默认的标签注册bean的过程。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import标签的解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//alias标签的解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//bean标签的解析
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
//嵌套beans标签的解析
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
13、处理给定的bean元素,解析bean定义并将其注册到注册表。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册定义的bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
14、向给定的bean工厂注册给定的bean定义
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
15、注册bean,加到缓存中。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
通过跟踪源代码的实现实现,分析了xml配置文件的中bean的加载和注册过程。
四、Bean的获取流程
根据Spring中通过BeanFactory获取bean实例对象的方法,画出了一下获取bean对象实例的流程图。
1、调用AbstractBeanFactory类中的getBean方法,获取Bean实例。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
2、调用AbstractBeanFactory类中的doGetBean方法,返回的bean可以是单例也可以是多例。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//提取对应的beanName
final String beanName = transformedBeanName(name);
Object bean;
// 检查缓存中或者实例工厂中是否有对应的实例
因为在创建单例bean的时候会存在依赖注入的情况,而在创建以来的时候为了避免循环依赖,spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光,也就是将ObjectFactory加入到缓存中,一旦下个bean创建时候需要依赖上个bean则直接使用ObjectFactory
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//返回对应的实例,有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
//只有在单例情况下才会尝试解决循环依赖,原型模式下,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是下面的情况
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//如果beanDefinitionMap中也就是在所有已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
//递归到BeanFactory中寻找
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
//如果不是仅仅做类型检查则是创建bean,进行记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
//将存储xml配置文件的GenericBeanDefinition转化为RootBeanDefinition,如果指定BeanName是子Bean的话同时合并父类的相关属性
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//如果存在依赖则需递归实例化依赖bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//缓存依赖调用
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// Create bean instance.
//实例化依赖的bean后便可以实例化mbd本身了,单例模式创建
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//原型模式创建
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
//指定的scope上实例化bean
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// Check if required type matches the type of the actual bean instance.
//检查需要的类型是否符合bean的实际类型
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) b
3、AbstractAutowireCapableBeanFactory类的createBean方法,创建bean实例,填充bean实例,使用后置处理器
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 确保此时bean类已实际解析,并克隆bean定义,以防无法在共享合并bean定义中存储动态解析的类.
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 准备覆盖的方法
try {
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
//给bean后置处理器一个机会来返回代理来代替真正的实例,这是实例化的前置处理
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
//如果上边方改变了bean就直接返回
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
//进行常规bean的创建
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
4、使用实例化前的后置处理器。
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
//对后置处理器中所有InstantiationAwareBeanPostProcessor类型的后置处理器进行postProcessBeforeInstantiation方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
//BeanPostProcessor的postProcessAfterInitialization方法的调用
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
spring是如何解循环依赖的问题呢?
构造器循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。
对于setter注入造成的依赖是通过spring容器提前暴漏刚完成构造器注入但未完成其他步骤的bean来完成的,而且只能解决单例作用域的bean循环依赖。
5、常规创建制定的bean。此时已经进行了bean的预创建处理。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// 初始化bean,转化为BeanWrapper
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//根据指定bean使用对应的策略创建新的bean实例。如:工厂方法、构造函数自动注入、简单初始化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//允许后置处理器改变合并后bean的定义
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//应用MergedBeanMergedBeanDefinitionPostProcessor
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
//早期缓存单例解决循环依赖,即使是由生命周期接口(如BeanFactoryAware)触发的
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//避免后期循环依赖,可以在bean初始化完前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//aop织入
}
//初始化bean实例
Object exposedObject = bean;
try {
//对bean进行填充,将各个属性值注入,其中,可能存在依赖其他bean的属性,则会递归初始依赖bean
populateBean(beanName, mbd, instanceWrapper);
//调用初始化方法,比如init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
if (earlySingletonExposure) {
//只有检测到有循环依赖的情况下才会不为空
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
//如果exposedObject没有在初始化方法中改变,也就是没有被增强
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
//因为bean创建后其所依赖的bean一定是已经创建的,actualDependentBeans不为空则表示当前bean创建后其依赖的bean却没有全部创建完,也即是说存在循环依赖
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// 根据scope注册bean
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObje
这个方法的大致思路如下:
a、如果是单例则需要首先清除缓存
b、实例化bean,将BeanDefinition转化为BeanWrapper。转化是一个复杂的过程,转化过程如下:
(1)、如果存在工厂方法则使用工厂方法进行初始化
(2)、一个类中有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数并进行初始化
(3)、如果既不存在工厂方法也不存在带有参数构造方法,则使用默认的构造方法进行bean的实例化。
c、MergedBeanDefinitionProcessor的应用,bean合并后的处理,Autowired注解正是通过此方法实现诸如类型的预解析。
d、依赖处理
e、属性填充,将所有属性填充至bean的实例中。
f、循环依赖检查
g、注册DisposableBean,如果配置了destory-method,这里需要注册以便在销毁的时候调用
h、完成bean创建并返回。
6、使用适当的初始话策略创建新的bean实例
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 解析class
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
//如果工厂方法不为空则使用工厂方法初始化策略
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
//一个类中有多个构造函数,每个构造函数都有不同的参数,所以调用前需要先根据参数锁定构造函数或工厂方法
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
//如果已经解析过则使用解析好的构造函数方法不需要再次锁定
if (resolved) {
if (autowireNecessary) {
//构造函数自动注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
//使用默认构造函数构造
return instantiateBean(beanName, mbd);
}
}
// 需要根据参数解析构造函数
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
//构造函数自动注入
return autowireConstructor(beanName, mbd, ctors, args);
}
// 默认构造的首选构造函数
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
//构造函数自动注入
return autowireConstructor(beanName, mbd, ctors, null);
}
// 使用默认的无参数构造方法
return instantiateBean(beanName, mb
7、ConstructorResolver类中的autowireConstructor方法,根据参数实例化bean
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
//explicitArgs通过getBean方法传入,如果getBean方法调用的时候指定方法参数那么直接使用
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
//如果在调用getBean方法时候没有指定则尝试从配置文件中解析
Object[] argsToResolve = null;
//尝试从缓存中获取
synchronized (mbd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// 从缓存中获取
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
//配置的构造函数参数
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
//如果缓存中存在
if (argsToResolve != null) {
//解析参数类型,
//缓存中的值可能是原始值也可能是最终值
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
//没有缓存
if (constructorToUse == null || argsToUse == null) {
// 使用具体的构造方法
Constructor<?>[] candidates = chosenCtors;
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
// 需要解析构造方法
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
//提取配置文件中配置的构造函数参数
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
//用于承载解析后的构造函数参数的值
resolvedValues = new ConstructorArgumentValues();
//能解析到的参数个数
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
//给定的构造函数排序,public构造函数优先参数数量降序、非public构造函数参数数量降序
AutowireUtils.sortConstructors(candidates);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {
// 如果已经找到选用的构造函数或者需要的参数个数小于当前的构造函数参数个数则终止,因为已经按照参数个数降序排序
break;
}
if (paramTypes.length < minNrOfArgs) {
//参数个数不相等
continue;
}
ArgumentsHolder argsHolder;
if (resolvedValues != null) {
//有参数则根据值构造对应参数类型的参数
try {
//注释上获取参数名称
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
//获取参数名称探索器
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
//获取构造函数函数的参数名称
paramNames = pnd.getParameterNames(candidate);
}
}
//根据名称和参数类型创建参数参数持有者
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
}
catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
}
// Swallow and try next constructor.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
}
else {
if (paramTypes.length != explicitArgs.length) {
continue;
}
//构造函数没有参数的情况
argsHolder = new ArgumentsHolder(explicitArgs);
}
//探测是否有不确定的构造函数存在,例如不同构造函数的参数为父子关系
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// 如果它代表着当前最接近的匹配则选择作为构造函数
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
if (constructorToUse == null) {
if (causes != null) {
UnsatisfiedDependencyException ex = causes.removeLast();
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
throw ex;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Could not resolve matching constructor " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
}
else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous constructor matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
ambiguousConstructors);
}
if (explicitArgs == null && argsHolderToUse != null) {
//将解析的构造函数加入缓存
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
//将构建的实例加入BeanWrapper中
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
8、使用默认的构造方法初始化bean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
9、SimpleInstantiationStrategy类的实例化策略
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
//如果有需要覆盖或者动态替换的方法则当然需要使用cglib进行动态代理,因为可以在创建代理的同时动态方法织入类中,但是如果没有需要动态改变方法,为了方便直接反射就可以了
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
constructorToUse = clazz.getDeclaredConstructor();
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
}
}
}
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 必须产生CGLIB子类
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
10、使用给定构造函数实例化类的便利方法。注意,如果给定一个不可访问的构造函数(即非公共的),该方法将尝试设置可访问的构造函数,并支持Kotlin类,该类具有可选参数和默认值
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
五、总结
从源码的角度分析了基于XML形式配置加载bean的流程和使用getBean。在bean加载到Spring IOC容器中用到的主要组件。同时getBean()方法使用bean的加载流程。
猜你喜欢
- 2024-11-19 1-0 Protobuf通信协议.proto文件编写
- 2024-11-19 阿里P8资深架构师耗时一年整理19年Java工程师成神之路
- 2024-11-19 Java修炼终极指南:83. 声明一个Java记录
- 2024-11-19 Effective Java 3rd(Java高效编程)技术要点
- 2024-11-19 Java 14 发布了,快来了解下吧
- 2024-11-19 Spring技巧:深入研究Java 14和SpringBoot
- 2024-11-19 IntelliJ IDEA 2023.2 最新变化
- 2024-11-19 数据结构与算法 #18 下跳棋,极富想象力的同向双指针模拟
- 2024-11-19 Spring Bean生命周期你除了会背八股文面试,真的会用了吗?
- 2024-11-19 面试经验之BATMJ大厂面试Java岗位
- 最近发表
- 标签列表
-
- jsp (69)
- gitpush (78)
- gitreset (66)
- python字典 (67)
- dockercp (63)
- gitclone命令 (63)
- dockersave (62)
- linux命令大全 (65)
- pythonif (86)
- location.href (69)
- dockerexec (65)
- tail-f (79)
- queryselectorall (63)
- location.search (79)
- bootstrap教程 (74)
- deletesql (62)
- linuxgzip (68)
- 字符串连接 (73)
- html标签 (69)
- c++初始化列表 (64)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)