专业的编程技术博客社区

网站首页 > 博客文章 正文

每日一练进击大厂「DAY10」Spring2

baijin 2024-08-12 13:38:47 博客文章 11 ℃ 0 评论

文章目录

  • 一、Spring注入Bean的方式
  • 二、Bean的生命周期
  • 三、BeanFactory和ApplicationContext
  • 四、Spring中的Bean是线程安全的吗
  • 五、Spring是如何解决循环依赖的
  • 六、Spring启动的加载过程
  • 七、Spring容器中的Bean什么时候被实例化
  • 八、BeanFactory和FactoryBean的区别
  • 总结

一、Spring注入Bean的方式

1、通过@Configuration+@Bean的方式注入

@Configuration
public class MyConfig {
    @Bean
    public Student getStudent(){
        return new Student();
    }
}

2、@Import快速给容器导入bean

@Import(value = {PersonA.class})

3、通过实现ImportBeanDefinitionRegistrar接口可以往容器中注入BeanDefinition,从而注入bean,重写registerBeanDefinitions方法。

@Configuration
@Import(value = {MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param importingClassMetadata  当前类的注解信息
     * @param registry 完成BeanDefinition的注册
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean company = registry.containsBeanDefinition("com.xinyu.major.Company");
        boolean member = registry.containsBeanDefinition("com.xinyu.major.Member");

        if(company && member){
            BeanDefinition beanDefinition = new RootBeanDefinition(User.class);
            registry.registerBeanDefinition("user",beanDefinition);
        }
    }
}

4、通过实现ImportSelector接口,往Spring容器中批量注入Bean。

@Configuration
@Import(value = {MyImportSelector.class})
public class MyConfig {}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.xinyu.major.Company",
                            "com.xinyu.major.Member"};
    }
}

5、通过实现FactoryBean接口往Spring容器中注入自定义Bean。

@Configuration
public class MyConfig {

    @Bean
    public MyFactoryBean getMyFactoryBean() {
    return new MyFactoryBean();
    }
}

public class MyFactoryBean implements FactoryBean<Monkey> {
    @Nullable
    @Override
    public Monkey getObject() throws Exception {
        return new Monkey();
    }

    @Nullable
    @Override
    public Class<?> getObjectType() {
        return Monkey.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}


调用
ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class);
Object monkey = app.getBean("getMyFactoryBean");
System.out.println(monkey);

Object monkey1 = app.getBean("&getMyFactoryBean");
System.out.println(monkey1);

Class<?> aClass = app.getBean("&getMyFactoryBean").getClass();
System.out.println(aClass);

6、@Componet+@ComponentScan,@ComponentScan会默认扫描该类所在包下的所有配置类,添加过Spring相关注解的类,@Controller、@Service、@Component等。

@Configuration
@ComponentScan(value = "com.xinyu.major")
public class MyConfig {

}

二、Bean的生命周期

Bean的生命周期主要是创建bean的过程,一个bean的生命周期主要是4个步骤,==实例化、属性注入、初始化、销毁。==但是对于一些复杂的bean的创建,spring会在bean的生命周期中开发很多的接口,可以让你加载bean的时候对bean做一些改变,因此Spring的bean的生命周期总共有以下几步:

  1. 实现了BeanFactoryPostProcessor接口的bean,在加载其他的bean的时候,会调用这个bean的postProcessBeanFactory方法,可以在这个步骤对bean中的属性去赋值。
  2. 实现了InstantiationAwareBeanPostProcessor接口的bean,会在实例化bean之前调用postProcessBeforeInstantiation方法。
  3. 然后再对bean进行实例化。
  4. 对bean进行属性注入。
  5. 对bean进行初始化,在初始化中包含了以下几个步骤实现了BeanFactoryAware接口会先调用setBeanFactory方法。实现了BeanNameAware接口,会先调用setBeanName方法。实现了BeanPostProcessor接口,会先调用 postProcessBeforeInitialization方法。实现了InitializingBean接口,会调用afterPropertiesSet方法。然后在进行aop后置处理,通过实现BeanPostProcessor接口,在postProcessAfterInitialization方法中进行动态代理。
  6. 销毁

三、BeanFactory和ApplicationContext

ApplicationContext包含BeanFactory所有的功能,BeanFactory是ApplicationContext的父接口。

ApplicationContext还提供了以下功能:

  • MessageSource,提供国际化的消息访问。
  • 资源访问,如URL何文件。
  • 事件传播。
  • 载入多个上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

BeanFactory采用的是延迟加载形式来注入bean的,只有在使用到某个bean时才对该bean进行加载实例化。ApplicationContext是在容器启动时,一次性创建了所有bean。
相对于BeanFactory,ApplicationContext唯一的不足是占用内存空间,当应用程序配置了较多的bean时,程序启动较慢。
BeanFactory通常以编程的方式被创建,ApplicationContext还能声明的方式创建,如ContextLoader。
ApplicationContext和BeanFactory都支持BeanPostPorcessor、BeanFactoryPostProcessor的使用,但是两者的区别是BeanFactory需要手动注册,ApplicationContext是自动注册。

四、Spring中的Bean是线程安全的吗

Spring中的Bean默认是单例的,单例是被所有线程共享的,所以会存在安全问题。如果想要线程安全,可以设置为多例的bean,一般来说线程不安全是说共享的资源不安全,但是有些bean其实没有资源的概念,并没有可变的状态,比如dao接口,service接口,所以在某种程度上来说spring的单例bean是线程安全的。

五、Spring是如何解决循环依赖的

  • 构造函数循环依赖,是无法被解决的,因为不能被实例化。
  • setter方式的多例的循环依赖,也是无法被解决的,如果是多例的,在容器初始化的时候,不会去创建,所以早期没有放入到三级缓存中暴露出来,所以无法解决循环依赖,会报错。
  • setter方式的单例循环依赖(A依赖B,B依赖A),A实例化->放入三级缓存->依赖注入B->B实例化->B放入三级缓存->依赖注入A->去三级缓存拿A的代理->把代理A放入二级缓存->返回A的代理注入到B->B初始化->走后置通知获得代理B->B的代理放入一级缓存->原生A依赖注入B->A初始化->后置通知,不走代理返回原生A->将原生A放入一级缓存。

六、Spring启动的加载过程

  1. 读取配置文件。
  2. 扫描配置文件配置的路径,把路径中的类都封装成BeanDefinition。
  3. 把BeanDefinition封装成BeanDefinitionMap,key是类名,value是BeanDefinition。
  4. 通过for循环实例化对象,完成属性注入并放入到容器中通过getBean()得到BeanDefinition通过BeanDefinition通过反射实例化对象放入到factoryBeanObjectCache封装成BeanWrapper属性注入,把对象中加了@Autowired注解的成员变量赋值把对象保存到容器

七、Spring容器中的Bean什么时候被实例化

如果使用BeanFactory作为spring bean的工厂类,则所有的bean都是在第一次使用该bean的时候才被实例化。
如果使用ApplicationContext作为spring bean的工厂类,则分为以下几种情况:

  • 如果bean的scope是singleton的,并且lazy-init=false,则Application启动的时候就实例化了所有的bean,并且将实例化的bean放在一个map结构的缓存中,下次再使用该bean的时候直接从这个缓存中取。
  • 如果bean的scope是singleton,lazy-init=true,则所有的bean都是在第一次使用该bean的时候才被实例化。
  • 如果bean的scope是prototype,则所有的bean都是在第一次使用该bean的时候才被实例化。

八、BeanFactory和FactoryBean的区别

BeanFactory是一个Bean工厂,使用简单工厂模式,是所有spring Bean容器的顶级接口,它为spring 的容器定义了一套规范,并提供像getBean这样的方法从容器中获取肯定的Bean实例。BeanFactory在产生Bean的同时,还提供了解决Bean之间的依赖注入能力,也就是DI。

FactoryBean是一个工厂的Bean,使用工厂方法模式,就是一个接口,主要的功能是动态生成某一个类型的Bean实例,我们可以自定义一个Bean并且加载到IOC容器里面,它提供了一个重要的方法getObject(),这个方法里面就是用来实现动态构造Bean的过程,SpringCloud里面的openFeign组件,客户端的代理类就是使用了FactoryBean来实现的。


总结

把汗水变成珍珠,把梦想变成现实

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表