spring-core-3-23 | Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?

Spring IoC 依赖注入

根据Bean 名称注入

根据Bean 类型注入

单个Bean 对象

集合Bean 对象

注入容器內建Bean 对象

注入非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"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">

    <!--23 | Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?-->
    <!-- 通过导入复用 dependency-lookup-context.xml -->
    <import resource="dependency-lookup-context.xml"/>

    <bean id="userRepository" class="org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository"
          autowire="byType">
        <!-- Auto-Wiring, 自动注入, 这里用的是byType, 根据类型, 但是这种方式是不支持自定义类的加载顺序的 -->
        <!-- 手动配置, 那么这里就有问题, 如果user还有更多的上升文, 要不断手动配置, 我们更希望是自动注入的 -->
        <!--关于util标签见官方文档, 搜索 :util  :
            https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/spring-framework-reference/core.html#spring-core-->
        <!--        <property name="users">-->
        <!--            <util:list>-->
        <!--                <ref bean="superUser" />-->
        <!--                <ref bean="user" />-->
        <!--            </util:list>-->
        <!--        </property>-->

    </bean>
</beans>
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.geekbang.thinking.in.spring.ioc.overview.repository;

import org.geekbang.thinking.in.spring.ioc.overview.domain.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;

import java.util.Collection;

/**
 * 23 | Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?
 * 用户信息仓库
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
public class UserRepository {

    /**
     * 23.1 23.2
     * 注入自定义 Bean 对象
     * 自定义 Bean
      */
    private Collection<User> users;

    /**
     * 23.3 內建 Bean 对象(不是业务方构建的, 而是spring内部自己建的)
     * 23.4 內建非 Bean 对象(內建依赖)
     */
    private BeanFactory beanFactory;

    /**
     * 23.5 內建 Bean 对象(不是业务方构建的, 而是spring内部自己建的)
     * 这里解释了一个问题, 依赖查找和依赖注入, bean的来源不是同一个
     */
    private ObjectFactory<ApplicationContext> objectFactory;

    public Collection<User> getUsers() {
        return users;
    }

    public void setUsers(Collection<User> users) {
        this.users = users;
    }


    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    public ObjectFactory<ApplicationContext> getObjectFactory() {
        return objectFactory;
    }

    public void setObjectFactory(ObjectFactory<ApplicationContext> objectFactory) {
        this.objectFactory = objectFactory;
    }
}

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.geekbang.thinking.in.spring.ioc.overview.dependency.injection;

import org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 注意 视频 23 讲的并不清楚, 一定要结合 视频24一块看
 * 23 | Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?
 * 依赖注入示例
 * • 根据Bean 名称注入
 * • 根据Bean 类型注入
 * •    单个Bean 对象
 * •    集合Bean 对象
 * • 注入容器內建Bean 对象
 * • 注入非Bean 对象 (容器內建依赖)
 * • 注入类型
 * •    实时注入
 * •    延迟注入
 *
 *
 * 24 | Spring IoC依赖来源:依赖注入和查找的对象来自于哪里?
 * 自定义Bean
 *      如 xml中定义的Bean, 是用户自定义的
 *      可以通过beanFactory.getBean()的方式能够依赖查找出来
 * 容器內建Bean 对象
 *      容器内部的Bean对象, 是spring给实例化的
 *      可以通过beanFactory.getBean()的方式能够依赖查找出来
 * 容器內建依赖(內建非 Bean 对象)
 *      非spring的bean, 如 beanFactory, 是spring启动必须的组件, 基础环境
 *      不能通过beanFactory.getBean()的方式依赖查找出来
 * <p>
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
public class DependencyInjectionDemo {

    public static void main(String[] args) {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        // 配置文件是 dependency-injection-context.xml, 这个文件通过导入复用 dependency-lookup-context.xml
        // 因此存在了两个上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");

        // 23.1 根据Bean 名称注入,
        // 23.2 根据Bean 类型注入 单个Bean 对象 单个Bean 对象
        // 24.1 依赖来源一:自定义 Bean,
        // 主要在配置文件 dependency-injection-context.xml 决定是查找还是注入
        //UserRepository userRepository =
        // applicationContext.getBean("userRepository", UserRepository.class);

        //System.out.println(userRepository.getUsers());


        // 23.3 注入容器內建Bean 对象 (不是业务方构建的, 而是spring内部自己建的)
        // 24.2 依赖来源二:容器內建 Bean 对象
        // 用依赖查找就是能获取到的
        //Environment environment = applicationContext.getBean(Environment.class);
        //System.out.println("获取 Environment 类型的 Bean:" + environment);


        // 23.4 內建非 Bean 对象(內建依赖)
        // 24.3 依赖来源三:容器內建依赖
        //System.out.println(userRepository.getBeanFactory());
        // 这里的结果是不相等的, 证明这俩不是一个beanFactory
        //System.out.println(userRepository.getBeanFactory() == applicationContext);

        // 依赖查找(报错), 那么这个地方依赖的来源是怎么回事?
        // 这说明BeanFactory不是一个内建Bean对象, 而是内建的依赖
        //System.out.println(beanFactory.getBean(BeanFactory.class));


        // ObjectFactory是之前用于延迟加载的那个beanFactory
        // 这里用 依赖注入 获取的 ObjectFactory
        //ObjectFactory userFactory = userRepository.getObjectFactory();
        // 这里的结果是相等的? 这里的依赖来源是哪里?
        // 答案是applicationContext中的beanFactory, 和手工new 的 beanFactory, 在spring容器中并不是一个对象,
        // 依赖查找和依赖注入的依赖来源是不同的, 
        // 在视频 26 这个问题得到解答
        //System.out.println(userFactory.getObject() == applicationContext);

    }

    /**
     * 26 | Spring IoC容器:BeanFactory和ApplicationContext谁才是Spring IoC容器?
     *
     * @param userRepository
     * @param applicationContext
     */
    private static void whoIsIoCContainer(UserRepository userRepository,
                                          //BeanFactory beanFactory
                                          ApplicationContext applicationContext) {
        /*
        ApplicationContext其实就是BeanFactory, 为什么这么说?

        首先要参看spring官方文档对于beanFactory和applicationContext的解释:
        https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/spring-framework-reference/core.html
        1.1节中
        The org.springframework.beans and org.springframework.context packages are the basis
        for Spring Framework’s IoC container.
        org.springframework.beans 和 org.springframework.context是 spring Ioc容器的相关包
        The BeanFactory interface provides an advanced configuration mechanism capable of
        managing any type of object.
        BeanFactory接口提供了一些高级配置的一个机制, 能够来管理任何类型的对象.
        (这里用object, 而不是bean, 是非常准确的, 因为我们前面知道, IoC的依赖来源有bean也有依赖(非bean))
        ApplicationContext is a sub-interface of BeanFactory. It adds:
        ApplicationContext 是 BeanFactory的子接口, 同时还提供了下面的特性:

        Easier integration with Spring’s AOP features
        简化了和spring AOP特性的整合

        Message resource handling (for use in internationalization)
        消息资源的处理(用于国际化)

        Event publication
        事件的发布, 就是spring的事件

        Application-layer specific contexts such as the WebApplicationContext for
        use in web applications.
        用于应用级别的上下文, 比如WebApplicationContext 使用在web场景的应用下

        In short, the BeanFactory provides the configuration framework and basic functionality,
        总而言之, BeanFactory提供了一个配置的框架且是基本的功能
        and the ApplicationContext adds more enterprise-specific functionality.
        并且 ApplicationContext 增加了一些企业级应用的特性.
        到这里我们可以认为BeanFactory就是一个基本的IoC容器, 而ApplicationContext是它的一个超集
        The ApplicationContext is a complete superset of the BeanFactory and is used exclusively
        in this chapter in descriptions of Spring’s IoC container.
        For more information on using the BeanFactory instead of the ApplicationContext, see [beans-beanfactory].
         */


        // 这个表达式为什么不会成立
        System.out.println(userRepository.getBeanFactory() == applicationContext);

        /*
        ApplicationContext is BeanFactory, 如官方文档所说, ApplicationContext就是BeanFactory的一个超集
        我们从子类往上找, 按照这样的方向去找找名堂:
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
        ClassPathXmlApplicationContext => AbstractXmlApplicationContext => AbstractRefreshableConfigApplicationContext
        => AbstractRefreshableApplicationContext => AbstractApplicationContext
        在最后的抽象类 AbstractApplicationContext 中是这样的
        public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
        ConfigurableApplicationContext 是配置模式, 这是一种可写的模式

        在这个类中寻找方法 getBeanFactory()
        @Override
        public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
        可见也是覆盖了接口的方法, 那么这个接口就是 ConfigurableApplicationContext
        那么这里就有些奇怪, 看类头是这样的:
        public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
        ApplicationContext不就是BeanFactory的子接口么
        ConfigurableApplicationContext <- ApplicationContext <- BeanFactory

        这里有两个问题:
        为什么ConfigurableApplicationContext要特意提供一个方法getBeanFactory()? 直接获取不到父类对象么?
        为什么System.out.println(userRepository.getBeanFactory() == applicationContext); 是false呢?

        那么ConfigurableApplicationContext既然是可写模式, 就回提供set方法:
        可以找到
        void setParent(@Nullable ApplicationContext parent);
        但是没有 setBeanFactory??

        往回找到 ConfigurableApplicationContext 的实现类  AbstractApplicationContext 的 getBeanFactory()
        这里就给按层往子类找, 有可能是在中间的抽象类实现的,
        用 ctrl + F12 搜索方法, 会发现实现是放在中间的抽象类 AbstractRefreshableApplicationContext
        @Override
        public final ConfigurableListableBeanFactory getBeanFactory() {
            synchronized (this.beanFactoryMonitor) {
                if (this.beanFactory == null) {
                    throw new IllegalStateException("BeanFactory not initialized or already closed - " +
                            "call 'refresh' before accessing beans via the ApplicationContext");
                }
                return this.beanFactory;
            }
        }

        看一下类中beanFactory变量:
        @Nullable
        private DefaultListableBeanFactory beanFactory;
        会发现这个 beanFactory 是一个组合的, 就是说这里代码是将 beanFactory 的实现 DefaultListableBeanFactory
        来组合进来了, 并不是去完全抽象或继承的这个类.
        那么这也就解释了上面的两个问题
        userRepository.getBeanFactory()获取的也正是 DefaultListableBeanFactory

        这里先提供一个先入为主的概念:
        在上下文里面的实现是采用了一种组合的方式, 同时在接口上面又是 extends 关系
        这种方式有点像代理

        再去看getBean()的实现:
        在 AbstractApplicationContext 中:
        @Override
        public <T> T getBean(Class<T> requiredType) throws BeansException {
            assertBeanFactoryActive();
            return getBeanFactory().getBean(requiredType);
        }
        这里就是先getBeanFactory(), 相当于我们先用代理对象去查找BeanFactory的实现, 并不是自己要具备这样的能力,
        而是外面组装了一个组合对象来帮助我们做这个事情

        结论是
        真正的IOC的底层实现就是BeanFactory的实现类,
        但 ApplicationContext 在底层组合了一个 BeanFactory 实现,
        是委托DefaultListableBeanFactory来操作getBean等方法的。

        也因此 System.out.println(userRepository.getBeanFactory() == applicationContext); 是false
        尽管他们都复用了同样的接口 BeanFactory, 但他们不是一个对象

         */

    }

}

上一篇:ASP.NET服务器端执行耗时操作的工作记录


下一篇:springboot中@ReqquestBody注解的使用以及不生效的原因