目录

BeanFactory & ApplicationContext IOC 容器概述

BeanFactoryAware 获取 BeanFactory

ApplicationContextAware 获取 ApplicationContext

直接注入获取 IOC 容器

@Bean 添加实例时参数注入容器

启动类 Main 方法中获取 IOC 容器

WebApplicationContextUtils 工具类获取IOC容器


BeanFactory & ApplicationContext IOC 容器概述

1、org.springframework.beans.factory.BeanFactory 是一个 Factory(工厂),也就是 IOC 容器/对象工厂,Spring 中所有的 bean 都是由 BeanFactory(也就是IOC容器)来进行管理的。

2、可以使用 @Autowired、@Resource 注解来从 Spring 容器中自动获取实例,也可以手动从容器中获取。无论是以前用 xml 配置文件来配置 <bean>、还是后来使用 @Bean、@Component ...注解,都是为了将对象交由 Spring 容器管理。

3、BeanFactory 作为 IOC 功能的顶级接口,提供了各种不同的实现,IOC 作为 Spring 的核心功能之一,所以这些 API 也是很常见的。

4、ApplicationContext 是 BeanFactory 的扩展功能得到了进一步增强,比如更易与 Spring AOP 集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的 context 实现(如针对 web 应用的 WebApplicationContext)。

5、只要获取了 BeanFactory 或 ApplicationContext,则可以随意获取容器中的任意实例(bean),推荐使用 ApplicationContext 。

//这两行代码一定不陌生
BeanFactory bf = new ClassPathXmlApplicationContext("student.xml");
Student studentBean = (Student) bf.getBean("studentBean");
//BeanFactory 在 spring-beans-x.x.x.RELEASE.jar 包下
BeanFactory 接口常用方法
Object getBean(String name)

返回指定名称的 bean 实例,默认为单例。

如果没有具有指定名称的bean,则引发 NoSuchBeanDefinitionException

如果无法获取bean,则抛出 BeansException

<T> T getBean(String name, Class<T> requiredType)

在 get(String name) 的基础上加上了requiredType类型限制,可以是接口或超类。

如果bean不是所需类型,则引发BeannotofRequiredTypeException

<T> T getBean(Class<T> requiredType)

返回唯一匹配给定对象类型的bean实例(如果有).

bean 必须匹配参数RequiredType类型,可以是接口或超类

如果找到给定类型的多个bean,则引发nouniqueBeanDefinitionException

boolean containsBean(String name)返回是否存在具有给定名称的bean.
boolean isSingleton(String name)返回此bean是否是单例。如果没有具有给定名称的bean,则引发nosuchbeanDefinitionException
Class<?> getType(String name)

返回bean的类型,如果不能确定,则返回 NULL。

如果没有具有给定名称的bean,则引发nosuchbeanDefinitionException

BeanFactoryAware 获取 BeanFactory

1、对于 BeanFactory 管理的 Bean,怎么获取它然后使用呢?使用 @Autowired、@Resource 注解来注入是最常见的操作,也是必须掌握的技能。

2、 使用 @Autowired、@Resource 注解的类自己首先必须是 Spring 组件 @Component 才能注入,而有时候想在项目中的任意位置都能获取容器中的实例进行使用,比如想在某个 XxxUtils 中使用某个 @Service 标识的实例,这时把 XxxUtils 标识为 @Component ,然后使用 @Resource 注入实例,这种方式看起来没有使用代码直接从 BeanFactory 容器中获取实例方便。

3、显然只要拿到了 BeanFactory 对象,就可以使用它的 getBean 方法从容器中获取 bean 实例,而获取 BeanFactory 实例的一个最简单的方式就是实现 BeanFactoryAware 接口。

package org.springframework.beans.factory;
// BeanFactoryAware 接口只要一个方法
public interface BeanFactoryAware extends Aware {
    /**初始化回调方法,spring 会自动将 BeanFactory 注入进行,我们接收之后即可使用 BeanFactory*/
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

4、自定义一个工厂类/工具类/助手类 实现 BeanFactoryAware 接口,有了它,则可以在项目中的任意位置传入 bean 名称或者类型来轻松获取容器中的实例(Jdk 8 + Spring Boot 2.1.6):

/**
 * 1、实现 BeanFactoryAware 接口,重写 setBeanFactory(BeanFactory beanFactory)
 * 2、本类必须是 Spring 组件,所以加上 @Component 注解
 * 3、spring 容器启动实例化本类的时候,会自动执行回调方法 setBeanFactory(BeanFactory beanFactory) 将 BeanFactory 注入
 * 4、获取了 BeanFactory 之后,则可以使用它的任意方法了,比如各种 getBean
 */
@Component
public class BeanFactoryHelper implements BeanFactoryAware {
    private static BeanFactory beanFactory;
    /**
     * 重写 BeanFactoryAware 接口的方法
     * @param beanFactory :参数赋值给本地属性之后即可使用 BeanFactory
     * @throws BeansException
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }
    /**
     * 根据名称获取容器中的对象实例
     * @param beanName :注入的实例必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
     * @return
     */
    public static Object getBean(String beanName) {
        return beanFactory.getBean(beanName);
    }
    /**
     * 根据 class 获取容器中的对象实例
     * @param requiredType :被注入的必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> requiredType) {
        return beanFactory.getBean(requiredType);
    }
    /**
     * 判断 spring 容器中是否包含指定名称的对象
     * @param beanName
     * @return
     */
    public static boolean containsBean(String beanName) {
        return beanFactory.containsBean(beanName);
    }
    //其它需求皆可参考 BeanFactory 接口和它的实现类
}

src/main/java/com/wmx/jdbc_template_app/helper/BeanFactoryHelper.java · 汪少棠/jdbc_template_app - Gitee.com

4、控制层代码演示如下:

    /**
     * BeanFactoryAware + BeanFactory 获取 BeanFactory
     * http://localhost:8080/application/context3
     * @return
     */
    @GetMapping("application/context3")
    @SuppressWarnings("Duplicates")
    public Map<String, Object> applicationContext3() {
        PhoneController phoneController = BeanFactoryHelper.getBean(PhoneController.class);
        List<Map<String, Object>> mapList = phoneController.userList();
        Map<String, Object> dataMap = new HashMap<>(4);
        dataMap.put("mapList", mapList);
        return dataMap;
    }

src/main/java/com/wmx/jdbc_template_app/controller/PhoneController2.java · 汪少棠/jdbc_template_app - Gitee.com

ApplicationContextAware 获取 ApplicationContext

1、上面已经说过 ApplicationContext 接口是 BeanFactory 接口的扩展,功能得到了进一步增强,能使用的方法也更多。

2、BeanFactory 对应 BeanFactoryAware,ApplicationContext 对应 ApplicationContextAware ,使用起来完全同理。

/**
 * 1、spring 容器启动的时候会自动创建 ApplicationContextHelper 实例
 * 2、然后调用 setApplicationContext 回调方法自动注入 ApplicationContext
 * 3、有了 ApplicationContext 就可以 getBean 了
 */
@Component
public class ApplicationContextHelper implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    /**
     * 根据名称获取容器中的对象实例
     * @param beanName :注入的实例必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
     * @return
     */
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }
    /**
     * 根据 class 获取容器中的对象实例
     * @param requiredType :被注入的必须已经存在容器中,否则抛异常:NoSuchBeanDefinitionException
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }
    /**
     * 判断 spring 容器中是否包含指定名称的对象
     * @param beanName
     * @return
     */
    public static boolean containsBean(String beanName) {
        return applicationContext.containsBean(beanName);
    }
}

src/main/java/com/wmx/jdbc_template_app/helper/ApplicationContextHelper.java · 汪少棠/jdbc_template_app - Gitee.com

   /**
     * ApplicationContextAware + ApplicationContext 获取 ApplicationContext
     * http://localhost:8080/application/context4
     * @return
     */
    @GetMapping("application/context4")
    @SuppressWarnings("Duplicates")
    public Map<String, Object> applicationContext4() {
        PhoneController phoneController = ApplicationContextHelper.getBean(PhoneController.class);
        List<Map<String, Object>> mapList = phoneController.userList();
        Map<String, Object> dataMap = new HashMap<>(4);
        dataMap.put("mapList4", mapList);
        return dataMap;
    }

直接注入获取 IOC 容器

    /**
     * Spring 默认已经提供了 IOC 容器(BeanFactory & ApplicationContext) 的实例,可以直接注入使用。
     * 注意:从容器里面获取自己是没有的,但是可以直接注入使用。
     */
    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private BeanFactory beanFactory;

src/main/java/com/wmx/jdbc_template_app/controller/PhoneController2.java · 汪少棠/jdbc_template_app - Gitee.com

@Bean 添加实例时参数注入容器

/**
 * 应用配置类
 *
 * @author wangMaoXiong
 * @version 1.0
 * @date 2022/4/21 21:28
 */
@Configuration
public class AppConfig {
    /**
     * ========= 自定义 DataSourceTransactionManager 数据源事务管理器 =========
     * 1、如果程序中有多个数据源(javax.sql.DataSource),且没有设置首选 Bean (如@Primary标识),需要手动为 DataSourceTransactionManager 绑定需要的数据源
     * 2、采用配置类的形式,将其添加到容器中,这样程序中可以直接 @Autowired 或者 @Resource 注入使用。
     * 3、当然不在配置类中添加实例,直接在需要的地方 new DataSourceTransactionManager(DataSource dataSource) 也是可以的,同样放入目标数据源即可。
     * 4、使用 Qualifier 指定对应名称的数据源进行注入
     * <p>
     * <p>
     * ========= IOC容器(ApplicationContext)自动注入并使用 ApplicationContext =========
     * 1、@Bean 添加实例时,方法中的参数可以获取容器中的任意实例,比如数据源-DataSource,IOC容器自身-ApplicationContext
     * 2、@Bean 添加实例时,如果容器中同一类型存在多个实例,且没有标记优先级时,则需要使用 Qualifier 注解指定实例名称
     */
    @Bean
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource, ApplicationContext applicationContext) {
        // 程序一启动就会执行本方法,然后从容器中获取实例,查询数据库数据
        PhoneController phoneController1 = applicationContext.getBean(PhoneController.class);
        List<Map<String, Object>> mapList2 = phoneController1.userList();
        System.out.println(mapList2);

        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
        return transactionManager;
    }
}

src/main/java/com/wmx/jdbc_template_app/config/AppConfig.java · 汪少棠/jdbc_template_app - Gitee.com

启动类 Main 方法中获取 IOC 容器

1、启动 Spring Boot 项目时,main 方法中 SpringApplication.run() 方法的返回值 ConfigurableApplicationContext 就是 ApplicationContext 的子接口,所以可以把 run() 方法返回的ApplicationContext 对象保存下来,随时使用。

@SpringBootApplication
public class JdbcTemplateAppApplication {
    private static ApplicationContext applicationContext;
    public static void main(String[] args) {
        applicationContext = SpringApplication.run(JdbcTemplateAppApplication.class, args);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("========服务启动成功,IOC容器中实例如下:");
        int count = 1;
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println((count++) + ":实例名称:" + beanDefinitionName + ",类型:" + applicationContext.getBean(beanDefinitionName).getClass());
        }
    }
}

src/main/java/com/wmx/jdbc_template_app/JdbcTemplateAppApplication.java · 汪少棠/jdbc_template_app - Gitee.com

WebApplicationContextUtils 工具类获取IOC容器

1、Spring 提供了工具类- WebApplicationContextUtils,可用于获取 ApplicationContext 对象。

    /**
     * http://localhost:8080/application/context2
     * IOC 容器(BeanFactory & ApplicationContext) 实例通过内置 WebApplicationContextUtils 工具类获取
     * <p>
     * 获取 web 应用的根 WebApplicationContext(ApplicationContext的子接口)
     * * WebApplicationContext getRequiredWebApplicationContext(ServletContext sc)
     * * WebApplicationContext getWebApplicationContext(ServletContext sc)
     * * WebApplicationContext findWebApplicationContext(ServletContext sc)
     *
     * @return
     */
    @GetMapping("application/context2")
    public Map<String, Object> applicationContext2(HttpServletRequest request) {
        ApplicationContext webApplicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
        WebApplicationContextUtils.findWebApplicationContext()

        PhoneController phoneController2 = (PhoneController) webApplicationContext.getBean("phoneController");
        List<Map<String, Object>> mapList2 = phoneController2.userList();
        Map<String, Object> dataMap = new HashMap<>(4);
        dataMap.put("mapList2", mapList2);
        return dataMap;
    }

src/main/java/com/wmx/jdbc_template_app/controller/PhoneController2.java · 汪少棠/jdbc_template_app - Gitee.com

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐