定义Bean:什么是BeanDefinition?
AnnotationConfigApplicationContext继承了GenicApplicationContext,它的refreshBeanFactory()只是在无参构造函数中new DefaultListableBeanFactory(),并没有注册bean定义相关的步骤,是不是此类ApplicationContext不需要注册bean定义?
AnnotationConfigApplicationContext 不需要加载外部配置化的 BeanDefinition,比如 ClassPathXmlApplicationContext 继承了 AbstractRefreshableConfigApplicationContext 在 refreshBeanFactory() 方法会调用 loadBeanDefinitions() 方法,在 AbstractXmlApplicationContext 的实现作用下,加载 XML 配置元素。
BeanDefinition元信息:除了Bean名称和类名,还有哪些Bean元信息值得关注?![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/51569913f62d6bd1064665f9d8e18218.png)
public class BeanDefinitionCreationDemo {
public static void main(String[] args) {
// 1.通过 BeanDefinitionBuilder 构建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
// 通过属性设置
beanDefinitionBuilder
.addPropertyValue("id", 1)
.addPropertyValue("name", "小马哥");
// 获取 BeanDefinition 实例
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// BeanDefinition 并非 Bean 终态,可以自定义修改
// 2. 通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置 Bean 类型
genericBeanDefinition.setBeanClass(User.class);
// 通过 MutablePropertyValues 批量操作属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
// propertyValues.addPropertyValue("id", 1);
// propertyValues.addPropertyValue("name", "小马哥");
propertyValues
.add("id", 1)
.add("name", "小马哥");
// 通过 set MutablePropertyValues 批量操作属性
genericBeanDefinition.setPropertyValues(propertyValues);
}
}
java bean与spring bean的区别又是什么呢?
广义地来看,Java Bean 是一个表现形式,而 Spring Bean 是狭义地托管在 Spring 容器中的 Java Bean。
BeanDefinition 是 Bean 实例化和初始化的依据,在未来的 Bean 生命周期中会给出两者之间的关系。简单地说,BeanDefinition 提供了 Bean 所属的类型,作用范围以及关联的属性等。
命名Spring Bean:id和name属性命名Bean,哪个更好?
早期的 Spring 都是通过 new DefaultBeanNameGenerator 来实现的,不过 Spring 5.2 开始可以使用常量,我说的是通常单例对象不通过构造器创建,而是通过常量或静态字段,假设默认构造器被调整为非 public ,过去的基于默认构造器实现的扩展均不能工作了。
Spring Bean的别名:为什么命名Bean还需要别名?
在早期的 Spring 有,说的是 ID 是全局唯一,而 name 是当前应用上下文唯一,而现在则没有这样的限制,id 和 name 相同了。别名实际上也是唯一的,不过别名的唯一性并不敏感.
注册Spring Bean:如何将BeanDefinition注册到IoC容器?
// 3. 通过 @Import 来进行导入
@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(AnnotationBeanDefinitionDemo.class);
// 通过 BeanDefinition 注册 API 实现
// 1.命名 Bean 的注册方式
registerUserBeanDefinition(applicationContext, "mercyblitz-user");
// 2. 非命名 Bean 的注册方法
registerUserBeanDefinition(applicationContext);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 按照类型依赖查找
System.out.println("Config 类型的所有 Beans" + applicationContext.getBeansOfType(Config.class));
System.out.println("User 类型的所有 Beans" + applicationContext.getBeansOfType(User.class));
// 显示地关闭 Spring 应用上下文
applicationContext.close();
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
beanDefinitionBuilder
.addPropertyValue("id", 1L)
.addPropertyValue("name", "小马哥");
// 判断如果 beanName 参数存在时
if (StringUtils.hasText(beanName)) {
// 注册 BeanDefinition
registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
} else {
// 非命名 Bean 注册方法
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
}
}
public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
registerUserBeanDefinition(registry, null);
}
// 2. 通过 @Component 方式
@Component // 定义当前类作为 Spring Bean(组件)
public static class Config { // 如果不增加 static 的话,那么 Config 类属于 AnnotationBeanDefinitionDemo 对象的一部分
// 1. 通过 @Bean 方式定义
/**
* 通过 Java 注解的方式,定义了一个 Bean
*/
@Bean(name = {"user", "xiaomage-user"})
public User user() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
}
}
通常在注解驱动场景下,非命名方式会比较多一点。XML 配置的方式,命名方式则相对多。
实例化Spring Bean:Bean实例化的姿势有多少种?
User
public class User {
private Long id;
private String name;
private City city;
private City[] workCities;
private List<City> lifeCities;
private Resource configFileLocation;
private Company company;
// get set
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", city=" + city +
", workCities=" + Arrays.toString(workCities) +
", lifeCities=" + lifeCities +
", configFileLocation=" + configFileLocation +
", company=" + company +
'}';
}
public static User createUser() {
User user = new User();
user.setId(1L);
user.setName("小马哥");
return user;
}
}
- 通过静态工厂方法
<!-- 静态方法实例化 Bean -->
<bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
factory-method="createUser"/>
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User user = beanFactory.getBean("user-by-static-method", User.class);
- 通过 Bean 工厂方法(调用实例工厂的实例方法)
<!-- 实例(Bean)方法实例化 Bean -->
<bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>```
<bean id="userFactory" class="org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory"/>
public class DefaultUserFactory implements UserFactory {
}
public interface UserFactory {
default User createUser() {
return User.createUser();
}
}
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
- 通过 FactoryBean(getObject方法)
<!-- FactoryBean实例化 Bean -->
<bean id="user-by-factory-bean" class="org.geekbang.thinking.in.spring.bean.factory.UserFactoryBean" />
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return User.createUser();
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
- 通过 ServiceLoaderFactoryBean
参考: https://www.jianshu.com/p/821a71d076c1
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
ServiceLoader<UserFactory> serviceLoader = beanFactory.getBean("userFactoryServiceLoader", ServiceLoader.class);
displayServiceLoader(serviceLoader);
private static void displayServiceLoader(ServiceLoader<UserFactory> serviceLoader) {
Iterator<UserFactory> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
UserFactory userFactory = iterator.next();
System.out.println(userFactory.createUser());
}
}
- 通过 AutowireCapableBeanFactory#createBean
// 启动 Spring 应用上下文
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/special-bean-instantiation-context.xml");
// 通过 ApplicationContext 获取 AutowireCapableBeanFactory
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
// 创建 UserFactory 对象,通过 AutowireCapableBeanFactory
UserFactory userFactory = beanFactory.createBean(DefaultUserFactory.class);
System.out.println(userFactory.createUser());
使用AutowireCapableBeanFactory时, xml文件中可以不声明任何bean,当调用createBean时实际使用的是CGLIB进行动态代理生成指定实现类的实例
AppliationContext启动后容器内实际上已经存在ServiceLoaderFactoryBean,
但是autowireCapableBeanFactory.createBean(DefaultUserFactory.class) 这里为什么没有调用ServiceLoaderFactoryBean.getObject方法?
我使用断点跟踪最后到了org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean
中的 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); 这里使用的是CGLIB代理
初始化Spring Bean:Bean初始化有哪些方式?
public class DefaultUserFactory implements UserFactory, InitializingBean{
// 1. 基于 @PostConstruct 注解
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
}
spring bean初始化顺序:PostContruct->afterPropertiesSet->自定义init方法
见名知意
postconstruct 构造后置
afterpropertiesset 属性填充后
initmethod bean初始化(可以认为是功能初始化)
延迟初始化Spring Bean:延迟初始化的Bean会影响依赖注入吗?
@Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
@Lazy(value = false)
public UserFactory userFactory() {
return new DefaultUserFactory();
}
@PostConstruct : UserFactory 初始化中...
InitializingBean#afterPropertiesSet() : UserFactory 初始化中...
自定义初始化方法 initUserFactory() : UserFactory 初始化中...
Spring 应用上下文已启动...
@Lazy 注册加上之后,它返回的对象实际上是一个代理 ,当依赖注入完成时,实际的 Bean 还没有完成初始化,只有首次调用方法或字段时,才开始~
代理对象是一个占位对象,初始化实际发生在实际调用时。
销毁Spring Bean: 销毁Bean的基本操作有哪些?
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {
// 1. 基于 @PostConstruct 注解
@PostConstruct
public void init() {
System.out.println("@PostConstruct : UserFactory 初始化中...");
}
public void initUserFactory() {
System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
}
@PreDestroy
public void preDestroy() {
System.out.println("@PreDestroy : UserFactory 销毁中...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean#destroy() : UserFactory 销毁中...");
}
public void doDestroy() {
System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中...");
}
@Override
public void finalize() throws Throwable {
System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
}
}
DisposableBean 实现原理
org.springframework.context.support.AbstractApplicationContext#close
org.springframework.context.support.AbstractApplicationContext#doClose
org.springframework.context.support.AbstractApplicationContext#destroyBeans
org.springframework.beans.factory.support.DefaultListableBeanFactory#destroySingletons
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#destroySingletons
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
在上面面的方法中找到disposableBeans的实现,调用destory方法
PreDestroy实现原理
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#CommonAnnotationBeanPostProcessor
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
在这个方法中会设置销毁的注解类型
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
currDestroyMethods.add(new LifecycleElement(method));
if (logger.isTraceEnabled()) {
logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
});
找到实现@PreDestroy注解的方法进行执行
回收Spring Bean:Spring IoC容器管理的Bean能够被垃圾回收吗?
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 注册 Configuration Class(配置类)
applicationContext.register(BeanInitializationDemo.class);
// 启动 Spring 应用上下文
applicationContext.refresh();
// 关闭 Spring 应用上下文
applicationContext.close();
Thread.sleep(5000L);
// 强制触发 GC
System.gc();
Thread.sleep(5000L);
finalize() 并不是不一定会回调,而是在使用System.gc()方法的时候,并不是调用这个方法就会立即触发虚拟机的垃圾回收,只是建议虚拟机进行垃圾回收,垃圾回收线程工作的具体时间是由虚拟机决定的,而finalize()方法,只有在垃圾回收的时候,才会调用。
Java Class 均存储在 ClassLoader 关联的空间,而这部分数据存放在 Perm 或 Metaspace 中,当动态代理,还是字节码提升还是会生成 Class 对象,这部分数据均在前面提到的空间类,均属于元信息。而普通 Java 对象包括数组均在 Heap 上。
面试题
外部单体对象和 BeanDefinition区别
外部单体对象注册和BeanDefinition注册的区别似乎只在于是new BeanDefinition还是new Object来区分是调BeanDefinitionRegistry#registryBeanDefinition还是调SingletonBeanRegistry#registrySingleton方法
比如 Spring 生命周期处理,对 registrySingleton 注册对象没有任何作用。