面试总结-Spring
一、Spring IOC的注入方式
- xml配置申明注册:setter、构造器、工厂方法注入;
- 注解方式申明注册:注解方式注
参考
1.构造器注入
构造器单个参数
<bean id="" class="">
<constructor-arg ref="beanId"></constructor-arg>
</bean>
构造器多个参数,下面写法是按照顺序注入到构造器中的
<bean id="" class="">
<constructor-arg ref="beanId"></constructor-arg>
<constructor-arg ref="beanId"></constructor-arg>
</bean>
构造器多个参数,当加上name属性,则与顺序无关
<bean id="" class="">
<constructor-arg name="" ref="beanId"></constructor-arg>
<constructor-arg name="" ref="beanId"></constructor-arg>
</bean>
2.set注入(三级缓存解决循环依赖)
spring会将name值全部转成大写,然后在前面拼接上"set",然后去对应类找到该方法,通过反射调用实现注入。
坑1:name属性值与类成员变量名无关系,但是和对应的set方法有关。
坑2:通过set注入,会通过调用默认空参构造器。
<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
<!-- 写法一 -->
<!-- <property name="UserDao" ref="userDao"></property> -->
<!-- 写法二 -->
<property name="userDao" ref="userDao"></property>
</bean>
<!-- 注册dao -->
<bean id="userDao" class="com.muyer.dao.UserDao"></bean>
3.工厂注入(FactoryBean)
public interface FactoryBean<T> {
//返回的对象实例
T getObject() throws Exception;
//Bean的类型
Class<?> getObjectType();
//true是单例,false是非单例 在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
boolean isSingleton();
}
4.注解注入
@Component/@Service/@Controller/@Repository/@Autowire
二、BeanFactory和ApplicationContext
- BeanFactory:最底层的接口,提供两个功能:实例化对象和拿对象
- ApplicationContext:继承BeanFactory,提供更多功能:国际化、AOP、访问资源文件、载入多个上下文等...
- 对比不同之处:
BeanFactory是延迟实例化Bean
ApplicationContext启动把所有Bean全都实例化,当然也可以通过lazy-init=true实现beaan的延迟实例化
三、BeanFactory和FactoryBean
- BeanFactory是IOC底层最的接口,提供了实例化对象和拿对象,DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext 等具体的容器都是实现了BeanFactory,再在其基础之上附加了其他的功能。
public interface BeanFactory {
//下面几个getBean的作用:
//注册的bean实例。
//根据bean的配置情况,如果是singleton模式将返回一个共享实例,否则将返回一个新建的实例,如果没有找到指定bean,该方法可能会抛出异常
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
//判断工厂中是否包含给定名称的bean定义,若有则返回true
boolean containsBean(String var1);
//判断给定名称的bean定义是否为单例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
//判断给定名称的bean定义是否为多例
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
//返回给定bean名称的所有别名
String[] getAliases(String var1);
}
- FactoryBean在IOC容器的基础上加上了简单工厂模式和装饰模式。可以getObject()方法中灵活配置。
我们可以在再FactoryBean#getObject()中组装我们需要的bean,当调用getObject的时候,即可返回一个bean
四、Spring IOC具体怎么实现的?
- IOC和DI:
IOC中文意思是控制反转,是一种思想:把对对象的控制权交给IOC容器,容器中对象通过DI(依赖注入)来实现 - IOC底层实现:
xml+dom4j+工厂+单例?????
五、循环依赖如何解决?循环依赖会报什么错?什么时候出现这个异常?是启动还是调用的时候?
- 概念
循环依赖就是循环引用,例如A依赖B,B依赖C,C依赖A形成了一个闭环。 - 种类
- 构造器循环依赖
- 属性注入循环依赖
- 现象
- 构造器循环依赖启动的时候就失败,报错:项目启动失败,发现了一个cycle
- 属性循环依赖(singleton),没有报错
- 属性循环依赖(prototype),报错:项目启动失败,发现了一个cycle
- 分析现象
- 单例的设值注入bean是如何解决循环依赖问题呢?如果A中注入了B,那么他们初始化的顺序是什么样子的?
本质就是三级缓存发挥作用,解决了循环;
简单来说就是:未等bean创建完就先将实例曝光出去,方便其他bean的引用。最先曝光到第三级缓存singletonFactories中。
- 为什么prototype类型的和构造器类型的Spring无法解决循环依赖呢?
prototype类型属性循环依赖:
对于“prototype”作用域Bean,Spring容器无法完成依赖注入,因为“prototype”作用域的Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的Bean。
构造器类型循环依赖:
构造器循环依赖Spring无法直接解决。但是可以[通过@Lazy解决构造器循环依赖](https://blog.csdn.net/qq271859852/article/details/105181422/)
通过在构造器参数中标识@Lazy注解,Spring 生成并返回了一个代理对象,因此注入并非真实对象而是其代理;
- 三级缓存
一级缓存:
/** 保存所有的singletonBean的实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存:
/** singletonBean的生产工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** 保存所有已经完成初始化的Bean的名字(name) */
private final Set<String> registeredSingletons = new LinkedHashSet<String>(64);
/** 标识指定name的Bean对象是否处于创建状态 这个状态非常重要 */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));
六、Spring Bean为什么默认是单例?
- 好处
- 可以从缓存中快速获取bean,其中Spring 单例bean的循环依赖就是通过三级缓存解决的
- 减少创建性能提升(池化技术)
- 减少jvm回收(单例bean作为GC Root对象不会被回收)
- 坏处
- 多线程并发安全问题,prototype就不会出现这种情况
七、Spring事务声明式注解如何实现、传播行为、失效场景?编程事务设计模式?自己定义个注解?
- 声明事务
- 实现
step1.配置事务管理器、连接池:
DataSourceTransactionManager
step2.开启事务注解:
<tx:annotation-driven transaction-manager="transactionManager"/>
step3.在业务类上加上@Transactional
-
传播行为
传播行为:事务在多个方法中如何传递的
传播的类型:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER
//如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.REQUIRED)
//如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行
@Transactional(propagation=Propagation.SUPPORTS)
//如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
@Transactional(propagation=Propagation.MANDATORY)
/不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
//以非事务的方式运行,如果当前存在事务,暂停当前的事务。
@Transactional(propagation=Propagation.NOT_SUPPORTED)
//必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
//如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。
Propagation.NESTED
- 失效场景
相应的bean没有被Spring管理
mysql的引擎是MyISAM失效,因为MyISAM不支持事务
注到了非public方法、或者非接口方法了
异常被捕获了
事务传播行为设置错误了,比如设置成never
-
编程事务
编程事务:显式调用 beginTransaction()、commit()、rollback() 等事务管理相关的方法
设计模式:用到了模板方法的设计模式 -
自己定义个注解
step1、定义注解:@interface+元注解(@Target/@Retention)
step2、标注
step3、反射读取,实现逻辑
注意:Spring定义注解,第三步怎么实现的?建议写个小demo,日志注解
七、销毁bean的方式
八、Spring中的设计模式?
单例模式,单例注册表,ConcurrentHashMap来管理这个注册表,核心方法getSingleton,类型DCL检查是否实例化
模板方法:xxxxTemplate,模板方法最大的好处,解决代码的复用,固定算法都封装到模板,自定义方法