Spring IoC依赖查找:依赖注入还不够吗?依赖查找存在的价值几何?
提前准备
User类
public class User implements BeanNameAware {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name +
'}';
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<bean id="user" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User">
<property name="id" value="1"/>
<property name="name" value="小马哥"/>
</bean>
<!-- primary = true , 增加了一个 address 属性 -->
<bean id="superUser" class="org.geekbang.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user"
primary="true">
<property name="address" value="杭州"/>
</bean>
<bean id="objectFactory" class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName" value="user"/>
</bean>
</beans>
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Super {
}
启动类
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
// 按照类型查找
lookupByType(beanFactory);
// 按照类型查找结合对象
lookupCollectionByType(beanFactory);
// 通过注解查找对象
lookupByAnnotationType(beanFactory);
// lookupInRealTime(beanFactory);
// lookupInLazy(beanFactory);
}
- 根据Bean名称实时查找
private static void lookupInRealTime(BeanFactory beanFactory) {
User user = (User) beanFactory.getBean("user");
System.out.println("实时查找:" + user);
}
- 根据Bean名称延迟查找
private static void lookupInLazy(BeanFactory beanFactory) {
ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
User user = objectFactory.getObject();
System.out.println("延迟查找:" + user);
}
ObjectFactory 对象并不是直接返回了实际的 Bean,而是一个 Bean 的查找代理。当得到 ObjectFactory 对象时,相当于 Bean 没有被创建,只有当 getObject() 方法时,才会触发 Bean 实例化等生命周期.
FactoryBean 与BeanFactory区别?
FactoryBean 是一种特殊的 Bean,需要注册到 IoC 容器,通过容器 getBean 获取 FactoryBean#getObject() 方法的内容,而 BeanFactory#getBean 则是依赖查找,如果 Bean 没有初始化,那么将从底层查找或构建。
- 根据Bean类型查找单个对象
private static void lookupByType(BeanFactory beanFactory) {
User user = beanFactory.getBean(User.class);
System.out.println("实时查找:" + user);
}
注意:由于在dependency-lookup-context.xml配置有2个User对象实例,因此需要在其中一个bean上添加primary=“true”,确保查找到主要的bean
- 根据 Bean 类型查找集合 Bean 对象
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
- 根据 Java 注解查找
private static void lookupByAnnotationType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
}
}
Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?
提前准备
public class UserRepository {
private Collection<User> users; // 自定义 Bean
private BeanFactory beanFactory; // 內建非 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;
}
}
dependency-injection-context.xml
<?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">
<!-- 通过导入复用 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 -->
<!-- 手动配置 -->
<!-- <property name="users">-->
<!-- <util:list>-->
<!-- <ref bean="superUser" />-->
<!-- <ref bean="user" />-->
<!-- </util:list>-->
<!-- </property>-->
</bean>
</beans>
public class DependencyInjectionDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
// BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
// 依赖来源一:自定义 Bean
UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);
// System.out.println(userRepository.getUsers());
// 依赖来源二:依赖注入(內建依赖)
System.out.println(userRepository.getBeanFactory());
ObjectFactory userFactory = userRepository.getObjectFactory();
System.out.println(userFactory.getObject() == applicationContext);
// 依赖查找(错误)
// System.out.println(beanFactory.getBean(BeanFactory.class));
}
}
Spring IoC依赖来源:依赖注入和查找的对象来自于哪里?
总结spring依赖来源的三个方向
- 自定义Bean(自己用xml配置或注解配置的bean)
- 内部容器依赖的Bean(非自己定义的Bean,spring容器初始化的Bean)
3.内部容器所构建的依赖(非Bean,不可通过获取依赖查找Bean的方法来获取(getBean(XXX)))
依赖可以大致分为2类:
1.普通的bean
2.非bean
内建bean和内建依赖的区别?
内建的 Bean 是普通的 Spring Bean,包括 BeanDefinitions 和 Singleton Objects,而内建依赖则是通过 org.springframework.beans.factory.config.ConfigurableListableBeanFactory#registerResolvableDependency方法来注册,这并非是一个 Spring Bean,无法通过依赖查找获取~Spring IoC配置元信息:Spring IoC有哪些配置元信息?
BeanFactory和ApplicationContext谁才是Spring IoC容器?
ApplicationContext是BeanFactory的子接口,说明ApplicationContext is BeanFactory。并且ApplicationContext 是BeanFactory的包装类,也就是内部组合了BeanFactory的实现-DefaultListableBeanFactory。为什么包装了DefaultListableBeanFactory,因为它需要简化且丰富功能来为企业开发提供更高的便捷性,也就是说ApplicationContext 是DefaultListableBeanFactory的超集。
至于为什么UserRepository注入的BeanFactory 不等于ClassPathXmlApplicationContext得到的BeanFactory ,是因为AbstractApplicationContext#prepareBeanFactory中 指明了 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); 也就是说当byType是BeanFactory.class的时候,获得是的ApplicationContext中的DefaultListableBeanFactory对象。
那真正的IOC的底层实现就是BeanFactory的实现类,因为ApplicationContext是委托DefaultListableBeanFactory来操作getBean等方法的。
org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory方法创建了DefaultListableBeanFactory
org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory方法中beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
明确地指定了 BeanFactory 类型的对象是ApplicationContext#getBeanFactory() 方法的内容,而非它自生。
Spring应用上下文:ApplicationContext除了IoC容器角色,还提供哪些特性?
BeanFactory 是 Bean 容器,它不提供企业特性,比如 AOP、事务以及 事件等,这些都被 ApplicationContext 支持。
使用Spring IoC容器:选BeanFactory还是ApplicationContext?
public class BeanFactoryAsIoCContainerDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// XML 配置文件 ClassPath 路径
String location = "classpath:/META-INF/dependency-lookup-context.xml";
// 加载配置
int beanDefinitionsCount = reader.loadBeanDefinitions(location);
System.out.println("Bean 定义加载的数量:" + beanDefinitionsCount);
// 依赖查找集合对象
lookupCollectionByType(beanFactory);
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
}
@Configuration
public class AnnotationApplicationContextAsIoCContainerDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 将当前类 AnnotationApplicationContextAsIoCContainerDemo 作为配置类(Configuration Class)
applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
// 启动应用上下文
applicationContext.refresh();
// 依赖查找集合对象
lookupCollectionByType(applicationContext);
// 关闭应用上下文
applicationContext.close();
}
/**
* 通过 Java 注解的方式,定义了一个 Bean
*/
@Bean
public User user() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
private static void lookupCollectionByType(BeanFactory beanFactory) {
if (beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
System.out.println("查找到的所有的 User 集合对象:" + users);
}
}
}
Spring IoC容器生命周期:IoC容器启停过程中发生了什么?
面试题精选
Spring Bean有两种实现,普通Bean,和工厂Bean(FactoryBean)
实现工厂Bean的方法就是pojo继承FactoryBean,并实现他的方法,当容器通过getBean()获取bean时,返回的是实现的getObject()方法所返回的对象