第四章:Spring Bean基础

定义Bean:什么是BeanDefinition?

在这里插入图片描述
AnnotationConfigApplicationContext继承了GenicApplicationContext,它的refreshBeanFactory()只是在无参构造函数中new DefaultListableBeanFactory(),并没有注册bean定义相关的步骤,是不是此类ApplicationContext不需要注册bean定义?

AnnotationConfigApplicationContext 不需要加载外部配置化的 BeanDefinition,比如 ClassPathXmlApplicationContext 继承了 AbstractRefreshableConfigApplicationContext 在 refreshBeanFactory() 方法会调用 loadBeanDefinitions() 方法,在 AbstractXmlApplicationContext 的实现作用下,加载 XML 配置元素。

BeanDefinition元信息:除了Bean名称和类名,还有哪些Bean元信息值得关注?在这里插入图片描述

在这里插入图片描述

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 注册对象没有任何作用。
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值