最新最全面的Spring详解(一)——Spring概述与IOC容器(下)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 最新最全面的Spring详解(一)——Spring概述与IOC容器(下)

7️⃣更多Bean的特性


Spring框架提供了许多接口,您可以使用这些接口自定义bean的性质。 本节将它们归类如下:


生命周期回调

ApplicationContextAware和BeanNameAware

其他rAware 接口

🍀(1)生命周期回调


初始化回调


org.springframework.beans.factory.InitializingBean.InitializingBean的接口允许bean在容器设置了bean上的所有必要属性之后执行【初始化工作】。【InitializingBean】接口指定了一个方法:

void afterPropertiesSet() throws Exception;

建议您不要使用【InitializingBean】接口,因为这将你的代码与Spring的代码耦合在一起。 我们更推荐使用【@PostConstruct】注解或指定POJO初始化方法。


在基于xml的配置元数据的情况下,您可以使用【init-method】属性指定具有void无参数签名的方法的名称。 在Java配置中,您可以使用【@Bean】的【initMethod】属性。可以看看下面的例子:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
    public void init() {
        // do some initialization work
    }
}

前面的示例几乎与下面的示例(包含两个例子)具有完全相同的效果:


<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        // do some initialization work
    }
}

然而,前面两个示例中的第一个并没有将代码与Spring耦合起来。


🍀(2)销毁回调


实现org.springframework.beans.factory.DisposableBean接口可以让bean在管理它的容器被销毁时获得回调。 ’ DisposableBean '接口指定了一个方法:

void destroy() throws Exception;

同样,我们并不建议您使用【DisposableBean】回调接口,因为我们没有必要将自己的代码与Spring耦合在一起。 另外,我们建议使用【@PreDestroy】注解或指定beanDifination支持的销毁方法。 对于基于xml的配置元数据,您可以在<bean/>上使用’ destroy-method '属性。 在Java配置中,您可以使用“@Bean”的【destroyMethod】属性。如下所示:

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
    public void cleanup() {
        // do some destruction work (like releasing pooled connections)
    }
}

前面的定义与下面的定义几乎完全相同:


<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
    @Override
    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}

🍀(3)默认初始化和销毁方法


当我们不使用spring特有的InitializingBean和disapablebean回调接口进行初始化和销毁时,我们通常会编写名为【init()】、 【initialize()】、 【dispose()】等的方法。 理想情况下,这种生命周期回调方法的名称在项目中应该是标准化的(项目经理规定了都必须这么写),以便所有开发人员使用相同的方法名称,并确保一致性。


您可以配置统一的bean的初始化和销毁方法。 这意味着,作为应用程序开发人员,您可以仅仅声明一个名为【init()】的初始化方法即可,而不必为每个beanDifination配置一个【init method = “init"】属性。


假设你的初始化回调方法名为“init()”,你的destroy回调方法名为“destroy()”。 你的类就像下面这个例子中的类:

public class DefaultBlogService implements BlogService {
    private BlogDao blogDao;
    public void setBlogDao(BlogDao blogDao) {
        this.blogDao = blogDao;
    }
    // this is (unsurprisingly) the initialization callback method
    public void init() {
        if (this.blogDao == null) {
            throw new IllegalStateException("The [blogDao] property must be set.");
        }
    }
}

然后,您可以在bean中使用该类,类似如下:

<beans default-init-method="init">
    <bean id="blogService" class="com.something.DefaultBlogService">
        <property name="blogDao" ref="blogDao" />
    </bean>
</beans>

顶层<beans/>元素属性上的【default-init-method】属性导致Spring IoC容器将bean类上的一个名为【init】的方法识别为初始化方法回调。 在创建和组装bean时,如果bean类有这样的方法,就会在适当的时候调用它。


如果现有的bean类已经有按约定命名的回调方法,那么您可以通过使用<bean/>本身的【init-method】和【destroy-method】属性指定对应方法来覆盖默认值。


🍀(4)总结


从Spring 2.5开始,你有三个选项来控制bean的生命周期行为:


InitializingBean和 DisposableBean 和 DisposableBean回调接口

自定义 init()和 destroy() 方法

@PostConstruct 和 @PreDestroy 您可以组合这些机制来控制给定的bean

为同一个bean配置的多个生命周期机制(具有不同的初始化方法),调用顺序如下:


(1)用“@PostConstruct”注解的方法

(2)afterPropertiesSet() 由 InitializingBean 回调接口

(3)自定义配置的init()方法


Destroy方法的调用顺序相同:


(1)用@PreDestroy注解的方法

(2)destroy()由 DisposableBean 回调接口定义

(3)自定义配置的 destroy() 方法


🍀(5)ApplicationContextAware 和 BeanNameAware


下面显示了“ApplicationContextAware”接口的定义:

public interface ApplicationContextAware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

因此,bean可以通过【ApplicationContextAware】接口,以编程方式【操作】创建它们的【ApplicationContext】。 其中一个用途是对其他bean进行编程检索, 有时这种能力是有用的。 但是,一般来说,您应该【避免使用它】,因为它将代码与Spring耦合在一起,而不遵循控制反转(Inversion of Control)风格,在这种风格中,协作者作为属性提供给bean。 ApplicationContext的其他方法提供了对文件资源的访问、发布应用程序事件和访问MessageSource。


当ApplicationContext创建一个实现了BeanNameAware接口的类时。 他提供了对其关联对象定义中定义的名称的引用。 下面的例子显示了BeanNameAware接口的定义:

public interface BeanNameAware {
    void setBeanName(String name) throws BeansException;
}

回调在填充普通bean属性之后,但在初始化回调(如“InitializingBean.afterPropertiesSet()”或自定义初始化方法之前调用。


总结: 实现了aware相关的接口,ioc容器不在遵循ioc风格,意思就是不在遵循按需初始化并注入依赖,而是在统一的地方统一注入,这个在源码中有所体现,后边的内容会涉及。


🍀(6)Other Aware Interfaces


除了“ApplicationContextAware”和“BeanNameAware”,spring提供了一个广泛的“aware”回调接口,让bean指示容器,他们需要一定【基础设施】的依赖。 作为一般规则,名称指示了所需依赖项的类型。 下表总结了一些最重要的“Aware”接口:


命名 依赖注入
ApplicationContextAware 将ApplicationContext注入bean当中
ApplicationEventPublisherAware 将ApplicationEventPublisherAware注入bean当中
BeanClassLoaderAware 将类加载器用于装入bean类
BeanFactoryAware 将BeanFactory注入bean当中
BeanNameAware 将bean的名称注入bean中
ResourceLoaderAware 配置了用于访问资源的加载器
ServletConfigAware 当前的’ ServletConfig '容器运行。 仅在web感知的Spring ’ ApplicationContext '中有效。
ServletContextAware 当前运行容器的“ServletContext”。 仅在web感知的Spring ’ ApplicationContext '中有效。


再次注意,使用这些接口将您的代码与Spring API绑定在一起,而不是遵循控制反转风格。 因此,我们将它们推荐给需要对容器进行编程访问的基础架构bean。


🍀(7)Bean的继承


bean的定义可以包含大量配置信息,包括构造函数参数、属性值和特定于容器的信息,比如初始化方法、静态工厂方法名,等等。 子beanDifination可以从父beanDifination继承配置数据。 子beanDifination可以根据需要覆盖一些值或添加其他值。 使用父beanDifination和子beanDifination可以节省大量输入。 实际上,这是模板的一种形式。


当您使用基于xml的配置元数据时,您可以通过使用“parent”属性来指示子beanDifination,下面的例子展示了如何做到这一点:

<bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
        class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">  
    <property name="name" value="override"/>
    <!-- the age property value of 1 will be inherited from parent -->
</bean>

如果没有指定,子beanDifination将使用来自父beanDifination的bean类,但也可以覆盖它。 在后一种情况下,子bean类必须与父bean兼容(也就是说,它必须接受父bean的属性值)。


子beanDifination从父bean继承范围、构造函数参数值、属性值和方法覆盖,并可选择添加新值。 您指定的任何scope、初始化方法、销毁方法或“静态”工厂方法设置都会覆盖相应的父方法设置。


其余的设置总是取自子定义:依赖、自动装配模式、依赖项检查、单例和延迟初始化。


前面的示例通过使用【 abstract 】属性显式地将父beanDifination标记为抽象。 如果父beanDifination没有指定类,则需要显式地将父beanDifination标记为【抽象】,如下例所示:

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBeanWithoutClass" init-method="initialize">
    <property name="name" value="override"/>
    <!-- age will inherit the value of 1 from the parent bean definition-->
</bean>


父bean不能单独实例化,因为它是不完整的,而且它也显式地被标记为“抽象”。 当定义是【抽象的】时,它只能作为作为一个父beanDifination的【纯模板beanDifination使用】。 试图单独使用这样一个【抽象】的父bean,通过将其作为另一个bean的ref属性引用,或使用父bean ID执行显式的“getBean()”调用,将返回错误。 类似地,容器内部的’ preinstantiatesingleton() '方法会忽略定义为抽象的beanDifination。


8️⃣基于注解的容器配置


在配置Spring时,注解比XML更好吗?


引入基于注解的配置提出了这样一个问题:这种方法是否比XML“更好”, 简短的回答是“视情况而定”。 长期的答案是,每种方法都有其优点和缺点。通常,由开发人员决定哪种策略更适合他们。 由于注解在其声明中提供了【大量上下文】,从而导致配置更简短、更简洁。 然而,XML擅长【连接组件】,而无需修改它们的源代码或重新编译它们。 一些开发人员更喜欢接近源代码进行连接,而另一些开发人员则认为带注解的类不再是pojo,而且配置变得分散且更难控制。


使用注解配置,我们需要开启以下的配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
</beans>

🍀(1)使用 @Autowired

作用就是自动装配,有byType的语义。你可以将@Autowired注解应用到构造函数中,如下面的例子所示


public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    // ...
}

注意: 从Spring Framework 4.3开始,如果目标bean只定义了一个构造函数,就不再需要在这样的构造函数上添加【 @Autowired 】注解。 然而,如果有几个构造函数可用,并且没有主/默认构造函数,那么至少其中一个构造函数必须用【@Autowired 】注解,以便告诉容器使用哪个构造函数。


你也可以将@Autowired注解应用到传统的 setter方法,如下面的例子所示:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // ...
}

你还可以将注解应用到具有任意名称和多个参数的方法,如下面的示例所示:

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
    // ...
}


用的最多的但spring官方并不推荐的方法是,你也可以将 @Autowired 应用到字段上,甚至可以将它与构造函数混合使用,如下面的示例所示:

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    private MovieCatalog movieCatalog;
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    // ...
}

你也可以通过在一个字段或方法中添加【@Autowired】注解来指示Spring从【ApplicationContext】中提供所有特定类型的bean,该字段或方法需要该类型的数组,如下面的例子所示:

public class MovieRecommender {
    @Autowired
    private MovieCatalog[] movieCatalogs;
    // ...
}


这同样适用于类型化的集合,如下例所示:

public class MovieRecommender {
    private Set<MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
    // ...
}

即使是类型化的“Map”实例,只要期望的键类型是“String”,也可以自动连接。 映射值包含预期类型的所有bean,键包含相应的bean名,如下例所示:

public class MovieRecommender {
    private Map<String, MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
    // ...
}

注意: 默认情况下,当给定注入点没有可用的匹配候选bean时,自动装配将失败。 对于声明的数组、集合或映射,至少需要一个匹配元素。


默认行为是将带注解的方法和字段视为指示所需的依赖关系。 你可以像下面的例子一样改变这种行为,通过将一个不满足的注入点标记为非必需的(例如,通过将【 @Autowired】中的' required '属性设置为' false ')来让框架跳过它:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    // ...
}


🍀(2)使用 @Primary微调基于注解的自动装配


由于按类型自动装配可能会导致多个【候选者】,因此通常需要对选择过程进行更多的控制。 实现这一点的一种方法是使用Spring的【@Primary】注解。 【@Primary】表示当多个bean可以作为一个依赖项的候选bean时,应该优先考虑某个特定bean。 如果在候选bean中恰好存在一个主要的bean,那么它将成为自动连接的值。


考虑以下配置,将’ firstMovieCatalog ‘定义为主要的’ MovieCatalog ':


以下内容【@Bean】是下个章节的:

@Configuration
public class MovieConfiguration {
    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }
    @Bean
    public MovieCatalog secondMovieCatalog() { ... }
    // ...
}

通过上述配置,下面的“MovieRecommender”将自动与“firstMovieCatalog”连接:

public class MovieRecommender {
    @Autowired
    private MovieCatalog movieCatalog;
    // ...
}

当然在xml中我们可以如下配置、相应的beanDifination如下,效果是等价的:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>
    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>
    <bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>

🍀(3)使用@Qualifier微调基于注解的自动装配


当可以确定一个主要候选时,【@Primary】注解可以轻松完成这个工作。 当您需要对选择过程进行更多控制时,可以使用Spring的【@Qualifier】注解。 您可以将【限定符值】与特定的参数关联起来,从而缩小类型匹配的集合,以便为每个参数选择特定的bean。 在最简单的情况下,这可以是一个简单的描述性值,如下例所示:

public class MovieRecommender {
    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;
    // ...
}

您还可以在单个构造函数参数或方法参数上指定’ @Qualifier '注解,如下面的示例所示:

public class MovieRecommender {
    private MovieCatalog movieCatalog;
    private CustomerPreferenceDao customerPreferenceDao;
    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
    // ...
}

下面的示例显示了相应的beanDifination:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 
        <!-- inject any dependencies required by this bean -->
    </bean>
    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 
        <!-- inject any dependencies required by this bean -->
    </bean>
    <bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>

注意: 除了使用qualifier标签决定,其实 @Qualifier可以使用id,name等属性定义的任何标识符。


其实,如果您打算按【名称标识符】完成的注入,那么就可以不使用【@Autowired 】,即使它能够在类型匹配的候选对象中按bean名称进行选择(需要配合@Qualifier同时使用)。 有一个更好的选择是使用JSR-250的 【@Resource】注解,该注解在语义上定义为通过惟一的【名称标识】选择特定的目标组件,声明的类型与匹配过程无关。


🍀(4)使用 @Resource


Spring还通过在字段或bean属性设置方法上使用JSR-250的 【@Resource】注解(' javax.annotation.Resource ')来支持注入。 这是Java EE中的常见模式, Spring也支持这种模式用于Spring管理的对象。


@Resource 带有一个name属性。 默认情况下,Spring将该值解释为要注入的bean名。 换句话说,它遵循by-name语义,如下面的示例所示:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

如果没有显式指定名称,则默认名称为【字段名或setter方法的参数名】。 对于字段,它接受字段名。 对于setter方法,它采用bean属性名。 下面的例子将把名为【movieFinder】的bean注入到它的setter方法中:

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

因此,在下面的示例中,' customerPreferenceDao '字段首先查找名为"customerPreferenceDao"的bean,然后按照类型' customerPreferenceDao '的主类型匹配:

public class MovieRecommender {
    @Resource
    private CustomerPreferenceDao customerPreferenceDao;
    @Resource
    private ApplicationContext context; 
    public MovieRecommender() {
    }
    // ...
}

9️⃣容器的启动过程


核心方法: refresh()


@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新,准备开店,检查环境,是不是适合开店,比如我选用哪个日志
        prepareRefresh();
        // 把门面租下来,获得一个bean工厂,loadBeanDefinitions(beanFactory)获取蛋糕的制作流程
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);
        // 忽略对应的自动装配
            //beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);
            // bean工厂已经基本好了,后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            // Initialize message source for this context.
            initMessageSource();
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // Check for listener beans and register them.
            registerListeners();
            // 初始化bean
            finishBeanFactoryInitialization(beanFactory);
            // Last step: publish corresponding event.
            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();
        }
    }
}
// 已经完成了创建和属性填充给你的工作
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
        invokeAwareMethods(beanName, bean);
        return null;
      }, getAccessControlContext());
    }
    else {
    // 1、调用实现的aware接口
           invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
            // 调用beanpostproccessor的BeforeInitialization方法
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
            // 调用初始化方法在这里
      invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
      throw new BeanCreationException(
          (mbd != null ? mbd.getResourceDescription() : null),
          beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
            // 调用beanpostproccessor的AfterInitialization
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
  }

🍀(1)初始化Spring容器


这个阶段相当于考察一下地理环境怎么样。


prepareRefresh(): 做一些准备阶段做的是:标记容器为active状态,以及检查当前的运行环境,比如使用log4j,还是jdklog等。


🍀(2)获得一个新的容器


这个阶段相当于租一个门面,同时准备好产品的制作流程。


ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();


如果有旧的容器,那么清空容器和容器中注册了的bean,创建新的容器DefaultListableBeanFactory。


protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
  }

🍀(3)bean工厂的准备阶段

相当于做一些基础装修,比如设备的采购。

prepareBeanFactory(beanFactory);

设置一些处理器:

tandardBeanExpressionResolver
ResourceEditorRegistrar

🍀(4)调用所有的BeanFactory后置处理器

这是留给我们进行扩展的,同事spring在也有很多的扩展实现。

执行:

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);


🍀(5)注册BeanPostProcessors

🍀(6)完成bean的创建


beanFactory.preInstantiateSingletons();


在创建bean的过程中,会执行如下流程:


  • (1)创建bean
  • (2)执行BeanPostProcessors
postProcessBeforeInitialization

(3)执行配置的初始化方法

(4)执行BeanPostProcessors


postProcessAfterInitialization
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
         invokeAwareMethods(beanName, bean);
         return null;
      }, getAccessControlContext());
   }
   else {
      invokeAwareMethods(beanName, bean);
   }
   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }
   try {
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

一些重要的BeanFactory后置处理器:


BeanFactoryPostProcessor:BeanFactory后置处理器

ConfigurationClassPostProcessor:解析配置类的BeanFactory后置处理器

一些重要的BeanFactory:


InstantiationAwareBeanPostProcessor:Bean实例化前后运行的后置处理器,还负责设置属性值populateBean()

AutowiredAnnotationBeanPostProcessor:对注解@Autowired的实现

CommonAnnotationBeanPostProcessor:对注解 @Resource的实现

InitDestroyAnnotationBeanPostProcessor:主要是实现了Bean的@PostConstruct和@PreDestroy方法。

AnnotationAwareAspectJAutoProxyCreator:AOP代理的后置处理器,AOP生成代理的地方就是在后置处理器postProcessAfterInitialization方法中实现的。

InfrastructureAdvisorAutoProxyCreator:自动代理创建器,仅考虑基础结构Advisor

Bean,而忽略任何应用程序定义的Advisor。Spring 的事务使用的是这个后置处理器。

相关文章
|
8天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
25 2
|
8天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
19 1
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
21天前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
23天前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
44 0
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
35 0
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
76 0
|
7月前
|
XML Java 数据格式
Spring IoC容器初始化过程(xml形式)
Spring IoC容器初始化过程(xml形式)
82 0
|
7月前
|
XML Java 数据格式
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
71 1