BeanFactory的作用?
BeanFactory是Spring中核心的顶层接口,是Bean的工厂,主要职责就是生产Bean,实现了简单工厂的设计模式,根据传入一个唯一的标识来获得Bean对象,它有非常多的实现类。
BeanFactory也是容器,因为它管理Bean的生命周期。
BeanDefinition的作用
主要是用来存储Bean的定义信息,决定Bean的生产方式。
比如类名,对象名,作用域,懒加载。
这里是利用了反射的机制,是拿到了类名之后用class.forname拿到类,然后通过newinstance来创建对象。
ApplicationContext和BeanFactory的关系?
容器和工厂的关系类似于4S店和汽车店的关系。
Application不生产bean,只是通知BeanFactory来生产bean。ApplicationConext实现了BeanFactory。
但是ApplicationCOntext可以自动地把bean注册进来,加载环境变量,实现事件监听,支持多语言。
SpringIOC容器的加载过程
从new ApplicationContext开始加载。
- 概念态
- 定义态
- 纯净态
- 成熟态
实例化一个ApplicationContext的对象;
调用bean工厂后置处理器完成扫描;
循环解析扫描出来的类信息;
实例化一个BeanDefinition对象来存储解析出来的信息;
把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来, 以便后面实例化bean;
再次调用其他bean工厂后置处理器;
然后,会检查是否lazy,是否抽象等等,再通过反射进行实例化得到纯净的bean,然后进行属性注入,创建AOP等初始化,在put到单例池。
什么是Bean,JavaBean和SpringBean和对象的区别?
由SpringIOC容器管理的对象就是bean,由IOC容器实例化组装和管理的对象。
配置bean有几种方式?
- xml配置文件直接配置
- @Component(Controller,Service,Repostory) 前提得配置扫描,是直接通过反射得到的。
- JavaConfig:@Bean是标注在方法上面的,必须返回一个对象作为Bean,一般是加载Configuration注解下面的。
- @Import
Bean的作用域
单例、非单例(单例会减少垃圾回收并且提升性能,快速获取因为每次都是从IOC的缓存里拿,很快。)
Request:每次发起请求都创建一个Bean
Session:每个对话都创建一个对象
application:一个全局应用共享对象
Spring的Bean是线程安全的吗?如何处理单程并发问题?
并不是,单例Bean在类中声明了成员变量并且有读写操作,这个时候就会出现线程不安全的情况。
但是,只需要把成员变量放在方法中,就是线程安全的了。
解决方法:
- 设置为多例
- 将成员变量放在ThreadLocal中,也就是让成员变量与本地的线程做关联,这样就是线程安全的了。
- 使用同步锁 比如加入synchronized就行了。
实例化Bean有几种方式
- 构造器方式(反射机制)
- 实例工厂方式(@bean)
- FactoryBean
解释不同方式的自动装配,spring 自动装配 bean 有哪些方式?
autowired:
- byName 根据bean的名字进行装配,如果bean的名字和属性相同,就会自动相配
- ByType:根据参数的数据类型进行自动装配
- constructor:利用构造函数进行装配
- no:不自动装配,手动通过ref属性或者Autowired手动指定需要自动注入的属性。
Bean的生命周期回调方法
有两个重要的bean 生命周期方法,第一个是init , 它是在容器加载bean时候被调用。第二个方法是 destroy 它是在容器卸载类的时候被调用。
bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct和@PreDestroy)。
Bean的生命周期
- 实例化:通过反射去推断构造函数进行实例化
- 属性赋值:解析自动装配,进行依赖注入(循环依赖)
- 初始化:调用Aware回调方法,调用初始化生命周期回调方法(如果实现了它)如果bean出现了aop,那就会创建动态代理。
- 销毁:Spring容器关闭的时候进行调用。
如何解决循环依赖?
三级缓存。
一级缓存:存储完整的Bean
二级缓存:避免动态代理重复创建
三级缓存:缓存函数接口,可以通过lambda表达式吧方法传进去,但是不会立刻调用,可能会aop创建。
如何在bean创建完之后进行扩展?
beanFactoryPostProcessor
Spring如何避免在并发下获取不完整的Bean?
设置双重检查锁, 在创建bean的过程中加锁,直到放到一级缓存中。但是不是放到一级缓存中,而是把锁加载到二级缓存中。而另一个线程会在能执行后再次寻找一级缓存中的bean。
之所以不放在一级缓存里,是因为性能——因为锁住一级缓存,其他线程就拿不出来其他的bean,会影响其他线程的性能。
JavaConfig是如何代替Spring.xml方式的?
- 以前Xml
a. Spring容器 ClassPathXmlApplicationContext(“xml”)
b. Spring.xml
c. bean的配置:< bean scope lazy>
d. 扫描包: < component-scan>
e. 引入外部属性配置文件 < property-placeHodeler resource=“xxx.properties”>
f. < property name=“password” value=“${mysql.password}”></ property>
g. 指定其他配置文件:<import resource=“” - javaconfig
a. Spring容器:AnnotationConfigApplicationContext(javaconfig.class)
b. 配置类 @Configuration
c. @Bean @Scope @Lazy
d. 扫描包: @ComponentScan
e. 引入外部属性配置文件 @PropertySource(“classpath:db.properties”)
f. @Value(“${mysql.password}”)
g. @Import @Import({配置类}) 使用比较灵活
如何在没有找到自动注入的bean/找到多个bean的时候不报错?
@Autowired(required = false) 不报错
@Primary 多个bean
@Autowired 注解有什么作用
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。
atuowired和resource有什么区别?
autowired是spring提供的,默认类型。
resource是jdk提供的,默认名字。
使用@Autowired注解自动装配的过程是怎样的?
@Autowired 通过Bean的后置处理器进行解析的
在创建一个spring容器的时候,在构造函数中进行注册。
- 在实例化后预解析(解析@Autowired标注的属性、方法 比如:把属性的类型、名称、属性所在的类… 元数据缓存起)
- 在属性注入真正的解析(拿到上一步缓存的元数据 去ioc容器帮进行查找,并且返回注入)
- 首先根据预解析的元数据拿到 类型去容器中进行查找
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
Configuration的作用及解析与案例
- 用来代替xml配置文件的,可以用@bean来注解bean
- 没有configuration也可以配置bean,但是加了可以为配置类创建一个cglib动态代理,保证配置类中@bean方法调用Bean的单例,比如bean可能调用了两次beanB,但是动态代理是去容器中去找,而不是直接去创建bean。
原理:
3. 创建Spring上下文的时候会注册一个解析配置的处理器ConfigurationClassPostProcessor(实现BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)
4. 在调用invokeBeanFactoryPostProcessor,就会去调用
ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry进行解析配置 (解析配置类说白就是去解析各种注解(@Bean @Configuration@Import @Component … 就是注册BeanDefinition)
5. ConfigurationClassPostProcessor.postProcessBeanFactory去创建cglib动态代理
将一个第三方的类配置为Bean有哪些方式?
- @Bean可以
- 通过@Import(你想加载的类.class),但是不能干预实例化过程(比如数据库链接)
- 通过Spring的扩展接口:BeanDefinitionRegistryPostProcessor
为什么@ComponentScan 不设置basePackage也会扫描?
因为Spring在解析@ComponentScan的时候 拿到basePackage 如果没有设置会将你的类所在的包的地址作为扫描包的地址