Spring问题小结

Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

特点:

  • 方便解耦,简化开发,通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码造成的程序耦合度高。
  • AOP编程的支持,通过Spring提供的AOP功能,方便进行面向切面编程。
  • 声明式事务的支持,在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
  • 方便程序的测试,可以用非容器依赖的编程方式进行几乎所有的测试工作。
  • 方便集成各种优秀框架,Spring提供了对各种优秀框架的直接支持。

摘抄一些问题,来方便自己复习

1.BeanFactory 和 FactoryBean 的区别.

BeanFactory:是个工厂类,用来管理 bean 的工厂。BeanFactory是所有Spring Bean的容器根接口,给Spring 的容器定义一套规范,也给IOC容器提供了一套完整的规范。BeanFactory最核心的功能是加载 bean,加载Bean用的是其中 getBean的 方法,但是我们通常不会直接使用该接口,而是使用其子接口。

FactoryBean:它不是一种普通的 bean,FactoryBean是个工厂 bean,该类是SpringIOC容器创建Bean的一种形式,融合了简单的工厂设计模式于装饰器模式。
那么为什么不直接使用Spring默认方式创建Bean,而是用FactoryBean?在某些情况下,对于实例Bean对象比较复杂的情况下,使用传统方式创建bean会比较复杂,这样就出现了FactoryBean接口,可以让用户通过实现该接口来自定义该Bean接口的实例化过程。即包装一层,将复杂的初始化过程包装,让调用者无需关系具体实现细节。FactoryBean 被广泛应用于 Java 相关的中间件中,如果你看过一些中间件的源码,一定会看到 FactoryBean 的身影。

总的来说,BeanFactory是负责生产和管理Bean的一个工厂接口,提供一个Spring Ioc容器规范,而FactoryBean则是一种Bean创建的一种方式,对Bean的一种扩展。对于复杂的Bean对象初始化创建使用其可封装对象的创建细节。

2.Spring bean 的生命周期.

Bean 的定义 -> Bean 的初始化 -> Bean 的使用 -> Bean 的销毁。

Bean 生命周期的整个执行过程描述如下。

1.Spring 启动,查找并加载需要被 Spring 管理的 Bean,并实例化 Bean。

2.利用依赖注入完成 Bean 中所有属性值的配置注入。

3.如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

4.如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

5.如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6.如果 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

7.如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。

8.如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9.如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10.如果在 <bean> 中指定了该 Bean 的作用域为 singleton,则将该 Bean 放入 Spring IoC 的缓存池中,触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用域为 prototype,则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

11.如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法销毁 Bean;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

3.Spring IoC 的容器构建流程.

4.Spring 的 AOP 有哪几种创建代理的方式?

JDK 动态代理和 Cglib 代理。

通常来说:如果被代理对象实现了接口,则使用 JDK 动态代理,否则使用 Cglib 代理。

另外,也可以通过指定 proxyTargetClass=true 来实现强制 Cglib 代理。

5.JDK 动态代理和 Cglib 代理的区别.

1、JDK是实现了被代理对象的接口,而 Cglib 是继承了被代理对象。

2、JDK 动态代理只能对实现了接口的类生成代理,Cglib 则没有这个限制。但是 Cglib 因为使用继承实现,所以 Cglib 无法代理被 final 修饰的方法或类。

3、在调用代理方法上,JDK 是通过反射机制调用,Cglib是通过FastClass 机制直接调用。FastClass 简单的理解,就是使用 index 作为入参,可以直接定位到要调用的方法直接进行调用。

4、从 JDK 1.7 开始,JDK 动态代理已经明显比 Cglib 更快了。

6.Spring 的事务传播行为有哪些?

1.REQUIRED:Spring 默认的事务传播级别,如果已经存在事务,那么就加入到事务中执行,如果当前不存在事务,则新建事务执行。

2.REQUIRES_NEW:每次都会新建一个事务,如果有事务,则将事务挂起,当新建事务执行完成以后,事务再恢复执行。

3.SUPPORTS:如果存在事务,则加入到事务执行,如果没有事务,则使用非事务的方式执行。

4.MANDATORY:必须要存在事务,否则就会抛出异常。

5.NOT_SUPPORTED :如果存在事务,则挂起事务,执行当前逻辑,结束后恢复事务。

6.NEVER:不能存在事务,否则就会抛出异常。

7.NESTED:嵌套事务。如果存在事务,则嵌套事务执行,如果不存在事务,则新建事务。

7.Spring 的事务隔离级别?

Spring 的事务隔离级别底层其实是基于数据库的,Spring 并没有自己的一套隔离级别。

DEFAULT:使用数据库的默认隔离级别。

READ_UNCOMMITTED:读未提交,最低的隔离级别,会读取到其他事务还未提交的内容,存在脏读。

READ_COMMITTED:读已提交,读取到的内容都是已经提交的,可以解决脏读,但是存在不可重复读。

REPEATABLE_READ:可重复读,在一个事务中多次读取时看到相同的内容,可以解决不可重复读,但是存在幻读。

SERIALIZABLE:串行化,最高的隔离级别,对于同一行记录,写会加写锁,读会加读锁。在这种情况下,只有读读能并发执行,其他并行的读写、写读、写写操作都是冲突的,需要串行执行。可以防止脏读、不可重复度、幻读,没有并发事务问题。

8.Spring 事务的实现原理.

Spring 事务的底层实现主要使用的技术:AOP(动态代理) + ThreadLocal + try/catch。

动态代理:基本所有要进行逻辑增强的地方都会用到动态代理,AOP 底层也是通过动态代理实现。

ThreadLocal:主要用于线程间的资源隔离,以此实现不同线程可以使用不同的数据源、隔离级别等等。

try/catch:最终是执行 commit 还是 rollback,是根据业务逻辑处理是否抛出异常来决定。

9.Spring 怎么解决循环依赖的问题?

Spring 是通过提前暴露 bean 的引用来解决的,具体如下。

Spring 首先使用构造函数创建一个 “不完整” 的 bean 实例(之所以说不完整,是因为此时该 bean 实例还未初始化),并且提前曝光该 bean 实例的 ObjectFactory(提前曝光就是将 ObjectFactory 放到 singletonFactories 缓存).

通过 ObjectFactory 我们可以拿到该 bean 实例的引用,如果出现循环引用,我们可以通过缓存中的 ObjectFactory 来拿到 bean 实例,从而避免出现循环引用导致的死循环。

举个例子:A 依赖了 B,B 也依赖了 A,那么依赖注入过程如下。

检查 A 是否在缓存中,发现不存在,进行实例化

通过构造函数创建 bean A,并通过 ObjectFactory 提前曝光 bean A

A 走到属性填充阶段,发现依赖了 B,所以开始实例化 B。

首先检查 B 是否在缓存中,发现不存在,进行实例化

通过构造函数创建 bean B,并通过 ObjectFactory 曝光创建的 bean B

B 走到属性填充阶段,发现依赖了 A,所以开始实例化 A。

检查 A 是否在缓存中,发现存在,拿到 A 对应的 ObjectFactory 来获得 bean A,并返回。

B 继续接下来的流程,直至创建完毕,然后返回 A 的创建流程,A 同样继续接下来的流程,直至创建完毕。

这边通过缓存中的 ObjectFactory 拿到的 bean 实例虽然拿到的是 “不完整” 的 bean 实例,但是由于是单例,所以后续初始化完成后,该 bean 实例的引用地址并不会变,所以最终我们看到的还是完整 bean 实例。

10.使用 Mybatis 时,调用 DAO接口时是怎么调用到 SQL 的.

简单点说,当我们使用 Spring+MyBatis 时:

1、DAO接口会被加载到 Spring 容器中,通过动态代理来创建。

2、XML中的SQL会被解析并保存到本地缓存中,key是SQL 的 namespace + id,value 是SQL的封装。

3、当我们调用DAO接口时,会走到代理类中,通过接口的全路径名,从步骤2的缓存中找到对应的SQL,然后执行并返回结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值