1、Spring的循环依赖及解决方式:
循环依赖:就是循环引用,两个或者两个以上的bean互相持有对方,最终形成闭环。
Spring中循环依赖的场景:1、构造器的循环依赖 2、field属性的循环依赖
如何检测:bean在创建的时候会给bean打标,如果递归调用回来发现正在创建中,则说明循环依赖了
具体:Spring容器会将所有正在创建的bean标识符放在一个“当前创建bean池”中,即singletonsCurrentlyInCreation。如果在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖,bean创建完毕后将从singletonsCurrentlyInCreation中清除掉
以单例对象的初始化为例,右图为具体实例化的图
1、createBeanInstance 实例化,其实就是调用对象的构造方法实例化对象
2、populateBean 填充属性,这一步主要是对bean的依赖属性进行填充
3、initializeBean 调用Spring xml中的init方法
循环依赖主要发生在第一二步,即构造器循环依赖和field循环依赖
如何解决:三级缓存
/** Cache of singleton objects: bean name --> bean instance */
// 单例对象的cache
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
// 单例对象工厂的cache
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name --> bean instance */
// 提前曝光的单例对象的cache
private final Map earlySingletonObjects = new HashMap(16);
我们在创建bean的时候,首先就会从cache中获取这个单例的bean,这个cache即为singletonObjects,具体方法为:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 1、从一级缓存中取bean
Object singletonObject = this.singletonObjects.get(beanName);
// 2、如果获取不到,并且对象正在创建中(即还没有初始化完成,比如A在populateBean的过程中依赖了B对象,得先去创建B对象,这时候A即处于创建中状态)
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 3、从二级缓存中获取,如果获取不到,并且允许singletonFactories通过getObject获取,就从三级缓存中获取
if (singletonObject == null && allowEarlyReference) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//4、如果从三级缓存中获取到了,则从singletonFactories中移除,并放到earlySingletonObjects,就是从三级缓存移动到了二级缓存
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
其中三级缓存在createBeanInstance之后被如下方法引用
protected void addSingletonFactory(String beanName, ObjectFactory> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
这里就是解决循环依赖的关键:当对象通过createBeanInstance创造出来,但是还没有进行初始化的第二步和第三步,但是已经可以通过对象引用定位到堆中的对象,这时候放到三级缓存中,这样如果A的某个field依赖了B的实例对象,同时B的field依赖了A的实例对象,A先初始化完成第一步,然后发现B还没有被create,所以走B的create,B初始化的时候发现自己依赖了A,于是尝试get(A),这时候可以通过三级缓存拿到A,B拿到A对象后顺利完成初始化123,完成后会放到一级缓存中,这样A也能拿到B的对象进行23初始化,然后放入一级缓存中,完成初始化
所以Spring不能解决:构造器循环依赖问题,因为加入三级缓存的前提是执行了构造器,所以构造器的循环依赖无法解决,而且只能解决单例模式下创建bean的循环引用问题,如果是prototype作用域Bean,Spring容器不进行缓存,因此无法提前暴露一个创建中的bean,就无法解决循环依赖。关于bean的作用域:
对于singleton作用域,Spring管理了bean的创建初始化和销毁,但是对于prototype作用域,Spring只负责创建,创建了bean实例后,后续就交给客户端代码管理,即销毁需要客户端来处理,Spring只负责new功能
2、BeanFactory和ApplicationContext区别:
1、BeanFactory是Spring最底层的接口,只提供了最简单的容器功能,即实例化对象和拿对象,而ApplicationContext(应用上下文),继承了BeanFactory接口,提供了更多功能,比如国际化,访问资源,载入多个上下文,消息发送,响应机制,AOP等 2、BeanFactory在启动的时候不会实例化bean,只有从容器中拿Bean的时候才会去实例化,而ApplicationContext在启动的时候就把所有的bean实例化了,不过可以为bean配置lazy-init=true来让bean延迟实例化
Spring容器提供的扩展方法:
0)BeanFactoryPostProcessor接口(在容器启动阶段)
1)各种的Aware接口,可以获取Spring框架的一些对象,比如ApplicationContextAware,可以获取ApplicationContext对象,并对这些对象做自定义初始化操作
2)BeanPostProcessor接口
3)InitializingBean接口(@PostConstruct, init-method)
4)DisposableBean接口(@PreDestroy, destory-method)
3、SpringMVC 工作原理:
流程说明:1、客户端(浏览器)发送请求,请求到DispatcherServlet
2、DispatcherServlet根据请求信息调用HandlerMapping,解析对应的Handler ,然后会返回一个包含interceptor,handler等的执行链
3、解析到对应的handler(也就是controller)以后,开始由HandlerAdapter适配器处理
4、HandlerAdapter根据handler来调用真正的处理器来处理请求,并处理相应的业务逻辑
5、处理完请求后,会返回一个ModelAndView对象,model是返回的数据对象,view是逻辑上的view
6、ViewResolver 会根据逻辑view来查找实际的view
7、DispaterServlet把返回的model传给view(视图渲染)
8、把view返回给请求者
4、Spring用到了哪些设计模式
1、工厂设计模式,Spring通过BeanFactory
、ApplicationContext
创建bean对象
2、代理设计模式, AOP功能的实现
3、单例设计模式,Spring 中的bean默认为单例
4、模版方法模式,Spring中jdbcTemplate
、hibernateTemplate等以template结尾的类,使用到了模版模式
5、包装器设计模式,连接多个数据库
6、观察者模式,Spring的事件驱动模型
7、适配器模式 Spring AOP中的增强或者通知,controller也用到了适配器模式
5、Spring事务
1、编程式事务,在代码中编写 2、声明式事务,配置文件中配置 (基于xml的和基于注解的声明式事务)
隔离级别:
TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务的传播行为:
事务传播行为:propagation
1、支持当前事务的情况
required:如果当前存在事务,则加入该事务,如果没有事务,则创建一个新的事务(默认配置是这个)
supports:如果当前存在事务,则加入事务,如果当前没有事务,则以非事务的方式继续运行
mandatory(强制):如果当前存在事务,则接入该事务,如果当前没有事务,则抛出异常
2、不支持当前事务的情况
requires_new:创建一个新的事务,如果当前存在事务,则把当前事务挂起
not_supported:以非事务方式运行,如果当前存在事务,则把当前事务挂起
propagation_never:以非事务方式运行,如果当前存在任务,则抛出异常
3、其他情况
nested:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务,则等价于required
适用情况:事务1方法里面有个方法定义了事务2,如果想要事务2方法异常时回滚,同时不影响事务1执行,则可以采用nested嵌套事务模式
rollbackfor=Exception.class 如果不配置rollbackfor,则只有在碰到RuntimeException或者error时才会回滚,,如果加上上述配置,则碰到异常时就会回滚
如果用的是aop代理,则@Transaction 只有应用到public方法才有效,并且会存在自调用问题,即只有被外部调用,目标方法才会由Spring生成的代理对象来管理,如果同一类中没有@Transaction的方法内部调用了有@Transaction的方法,则此事务会被忽略
解决办法就是由AspectJ取代aop代理
事务的原理:基于AOP,如果一个类或者public方法上标注了@Transactional,Spring容器会在启动的时候为其创建一个代理类,然后在调用此方法的时候,实际调用的是TransactionInterceptor的invoke方法,这个方法的作用就是在目标方法之前开启事务,在方法执行过程中如果遇到异常则回滚事务,调用完成后提交事务。
JDK和Cglib的区别:
Cglib |
JDK |
|
是否提供子类代理 |
是 |
否 |
是否提供接口代理 |
是(可强制) |
是 |
区别 |
必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法 |
实现InvocationHandler 使用Proxy.newProxyInstance产生代理对象 被代理的对象必须要实现接口 |