目录
BeanFactory ( IOC 容)概述
1、org.springframework.beans.factory.BeanFactory 是一个 Factory(工厂),也就是 IOC 容器/对象工厂,Spring 中所有的 bean 都是由 BeanFactory(也就是IOC容器)来进行管理的。
2、经常会使用 @Autowired、@Resource 注解来从 Spring 容器中获取实例,而这些实例恰恰都是 BeanFactory 管理。无论是以前用 xml 配置文件来配置 <bean>、还是后来使用 @Bean 注解,都是为了将对象交由 Spring 容器管理。
3、BeanFactory 作为 IOC 功能的顶级接口,提供了各种不同的实现,IOC 作为 Spring 的核心功能之一,所以这些 API 也是很常见的。
//这两行代码一定不陌生
BeanFactory bf = new ClassPathXmlApplicationContext("student.xml");
Student studentBean = (Student) bf.getBean("studentBean");//BeanFactory 在 spring-beans-x.x.x.RELEASE.jar 包下
4、ApplicationContext
是 BeanFactory
的扩展,功能得到了进一步增强,比如更易与 Spring AOP 集成、消息资源处理(国际化处理)、事件传递及各种不同应用层的 context 实现(如针对 web 应用的 WebApplicationContext
)。
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
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;
import org.springframework.beans.BeansException;
/**
* BeanFactoryAware 接口只要一个方法
*/
public interface BeanFactoryAware extends Aware {
/**初始化回调方法,spring 会自动将 BeanFactory 注入进行,我们接收之后即可使用 BeanFactory*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
//BeanFactoryAware 在 spring-beans-x.x.x.RELEASE.jar 包下
编码演示
1、下面使用 Jdk 8 + Spring Boot 2.1.6(Spring 5.1.8 )进行演示,代码结构如下,详细说明在代码注释中。
浏览器访问 PersonController 中的方法,PersonController 中不再使用 @Autowired、@Resource 注解注入 PersonService,而是使用 BeanFactoryHelper 获取 PersonService 实例。
2、为了结构完整,定义一个 service 接口和它的实现类:
package wmx.com.springbootredisstudy.services;
public interface PersonService {
void deleteAll();
}
package wmx.com.springbootredisstudy.services.iml;
import org.springframework.stereotype.Service;
import wmx.com.springbootredisstudy.services.PersonService;
import java.util.logging.Logger;
//@Service 注解没有指定名称时,默认为实现类 类名首字母小写的 personServiceImpl
@Service
public class PersonServiceImpl implements PersonService {
private Logger logger = Logger.getAnonymousLogger();
@Override
public void deleteAll() {
logger.info("删除所有用户成功");
}
}
3、自定义一个工厂类/工具类/助手类 实现 BeanFactoryAware 接口,有了它,则可以在项目中的任意位置传入 bean 名称或者类型来轻松获取容器中的实例:
package wmx.com.springbootredisstudy.factory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
/**
* 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 接口和它的实现类
}
4、控制层代码如下:
package wmx.com.springbootredisstudy.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import wmx.com.springbootredisstudy.factory.BeanFactoryHelper;
import wmx.com.springbootredisstudy.services.PersonService;
import java.util.logging.Logger;
@RestController
public class PersonController {
private Logger logger = Logger.getAnonymousLogger();
/**
* 删除所有用户:http://localhost:8080/person/deleteAll
*/
@GetMapping("person/deleteAll")
public String deleteAll() {
logger.info("用户准备删除所有用户...");
//PersonService personService = BeanFactoryHelper.getBean(PersonService.class);
//上面是根据 bean 的类型获取实例,下面是根据 bean 的名称获取实例
PersonService personService = (PersonService) BeanFactoryHelper.getBean("personServiceImpl");
personService.deleteAll();
return "delete success";
}
}
ApplicationContextAware
1、上面已经说过 ApplicationContext
接口是 BeanFactory
接口的扩展,功能得到了进一步增强,能使用的方法也更多。
2、BeanFactory
对应 BeanFactoryAware,ApplicationContext 对应 ApplicationContext
Aware ,使用起来完全同理。
package wmx.com.springbootredisstudy.factory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 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);
}
}