Spring-Bean的深入学习

本文深入剖析Spring框架的核心机制,详细解读Bean的生命周期管理、依赖注入、AOP特性以及容器内部工作原理,帮助开发者掌握Spring的高级用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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背后的操作

  1. 解析applicationgContext.xml,将xml中定义的bean(如userService和userDao)解析成Spring内部的BeanDefinition,
  2. 以beanName(如userService)为key,BeanDefinition(如userService相应的BeanDefinition)为value存储到DefaultListableBeanFactory中的beanDefinitionMap(其实就是一个ConcurrentHashMap)中
  3. 同时将beanName存入beanDefinitionNames(List类型)中,然后遍历beanDefinitionNames中的beanName,进行bean的实例化并填充属性
  4. 在实例化的过程中,如果有依赖没有被实例化将先实例化其依赖,然后实例化本身,实例化完成后将实例存入单例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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值