文章目录
- spring循环依赖?
- 请谈一下你对 spring 的理解?说一下 Spring 的核心是什么?请谈 一下你对 Spring IOC 和 和 AOP 的理解?
- 请说一下 Spring 的 的 Bean 作用域?
- 请谈一下Spring中bean对象的生命周期?
- Spring中的事务是如何实现的 ?
- 请谈一下Spring的两种事务形式?
- Spring容器启动流程是怎样的?
- Spring事务什么时候会失效?
- BeanFactory 和 ApplicationContext有什么区别?
- Spring 事务的传播行为了解吗?Spring 提供了几种事务的传播行为?
- Spring事务的隔离级别?
- 请谈一下 Spring 事务回滚机制 ?
- 说一下过滤器和 Spring 拦截器的区别 ?
- 拦截器的实现原理是什么?简单说一下拦截器用场景 ?
- 反射机制了解吗?知道 spring 中哪些用的反射吗 ?
- 使用@Autowired注解自动装配的过程是怎样的 ?
spring循环依赖?
当我们的类 A 中引用 B,B 引用 C,C 又引用 A 的时候,这个时候会产生循环依赖。Spring 所解决的循环依赖是有限
定条件的。首先 bean 必须是要单例模式。其次需要通过 get,set 的方式注入才行。使用构造器方式注入和多例模
式都不能解决循环依赖的问题。
根据 Spring 的初始化流程。Spring 先是用构造实例化 Bean 对象 ,此时 Spring 会将这个实例化结束的对象放到一
个 Map 中,并且 Spring 提供了获取这个未设置属性的实例化对象引用的方法。
首先理解为我们先实例化 A,A 此时并没有,于是从一级缓存区中取 A,取不到,然后取二级缓存区中取 A,也取不
到,最后通过对象的匿名工厂方法,创建了一个未装载完毕的实例 A。放入三级缓存区中。然后 B 对象依然与 A 走
了一套相同的流程。C 对象初始化的时候,发现所需要的 A 已经有了,虽然是不完全的,但是也可以用,于是 C 对
象的创建就完善了。放到一级缓存区中。C 对象完善后,B 对象将创建好的 C 对象初始化后,放到一级缓存区中。B
对象也装载完毕,然后 A 对象最后装载完毕。
请谈一下你对 spring 的理解?说一下 Spring 的核心是什么?请谈 一下你对 Spring IOC 和 和 AOP 的理解?
(1)Spring 框架是一个轻量级的 JavaSE/JavaEE 应用开发框架,是构建企业级应用程序的一站式解决方案。
(2)Spring 是模块化的,并被分为大约 20 个模块(core、beans、context、web 等),允许我们只使用需要的部分,而不需
要引入其他部分。
(3)Spring 的两大核心内容是 IOC 和 AOP(控制翻转和面向切面编程);
谈一下 IOC(Inversion Of Control)
IOC 的意思是控制反转,它是一种设计思想,是一个重要的面向对象编程的法则;
在 Java 开发中,Ioc 可以让我们把设计好的对象交给容器控制,而不是在对象内部直接控制;
对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系;
谈一下 AOP
(1)AOP 被称为面向切面编程,是一种编程范式,是对面向对象编程(OOP)的一种完善。
(2)OOP 最大问题就是无法解耦组件进行开发,而 AOP 就是为了克服这个问题而出现的。
(3)AOP 将整个系统分为"核心业务逻辑"和"非核心的服务";
AOP 的关注点是系统中的“非核心服务”【权限;事务;安全;异常;日志等】;
Spring 将非核心服务封装成一个 AOP 组件,然后通过配置信息形成"核心业务和 AOP 组件"之间的调用关系,
当执行核心业务时,AOP 组件会在合适的时机进行调用
请说一下 Spring 的 的 Bean 作用域?
- singleton:在 Spring IOC 容器中仅存在一个 Bean 的实例,Bean 以单例的方式存在;
- prototype:每次从容器中调用 Bean 时,都返回一个新的实例,也就是每次调用 getBean()方法时,相当于执行了 new 对
象的操作; - request:每次 http 请求都会创建一个新的 Bean,该作用域仅适合 WebApplicationContext 环境;
- session:同一个 http session 共享一个 Bean 实例,不同 session 使用不同的 Bean 实例,该作用域仅适用
WebApplicationContext 环境; - global session:这种作用域类似于标准的 HTTP Session 作用域,不过仅仅在基于 portlet 的 web 应用中才有意义。
请谈一下Spring中bean对象的生命周期?
Spring Bean 的生命周期主要分为四个阶段,也就是:Bean 的实例化、Bean 属性赋值、初始化和 Bean 的销毁
其中前三个阶段主要实现在 AbstractAutowireCapableBeanFactory 类中 doCreateBean()方法中;
而"Bean 的销毁"则是容器关闭时;
- Spring 启动,查找并加载需要被 Spring 管理的 bean,进行 Bean 的实例化
- Bean 实例化后对将 Bean 的引入和值注入到 Bean 的属性中
- 如果 Bean 实现了 BeanNameAware 接口的话,Spring 将 Bean 的 Id 传递给 setBeanName()方法
- 如果 Bean 实现了 BeanFactoryAware 接口的话,Spring 将调用 setBeanFactory()方法,将 BeanFactory 容器实例传
入 - 如果 Bean 实现了 ApplicationContextAware 接口的话,Spring 将调用 Bean 的 setApplicationContext()方法,将
bean 所在应用上下文引用传入进来。 - 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用他们的 postProcessBeforeInitialization()方法。
- 如果 Bean 实现了 InitializingBean 接口,Spring 将调用他们的 afterPropertiesSet()方法。类似的,如果 bean 使
用 init-method 声明了初始化方法,该方法也会被调用 - 如果 Bean 实现了 BeanPostProcessor 接口,Spring 就将调用他们的 postProcessAfterInitialization()方法。
- 此时,Bean 已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果 bean 实现了 DisposableBean 接口,Spring 将调用它的 destory()接口方法,同样,如果 bean 使用了 destory-
method 声明销毁方法,该方法也会被调用。
Spring中的事务是如何实现的 ?
- Spring事务底层是基于数据库事务和AOP机制的
- ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean
- 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解
- 如果加了,那么则利⽤事务管理器创建⼀个数据库连接
- 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮
常重要的⼀步 - 然后执⾏当前⽅法,⽅法中会执⾏sql
- 执⾏完当前⽅法后,如果没有出现异常就直接提交事务
- 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务
- Spring事务的隔离级别对应的就是数据库的隔离级别
- Spring事务的传播机制是Spring事务⾃⼰实现的,也是Spring事务中最复杂的
- Spring事务的传播机制是基于数据库连接来做的,⼀个数据库连接⼀个事务,如果传播机制配置为
需要新开⼀个事务,那么实际上就是先建⽴⼀个数据库连接,在此新数据库连接上执⾏sql
请谈一下Spring的两种事务形式?
1.Spring 提供了 “编程式事务” 和 “基于 AOP 方式的声明式事务”
2.Spring 编程式事务管理高层的抽象主要包括三个接口
PlatformTransactionManager:事务管理器
TransactionDefinition:事务定义信息(包括事务的隔离、传播机制等);
TransactionStatus:事务具体运行状态;
其中 Spring 为不同的持久化框架提供了不同事务管理器 PlatformTransactionManager 的接口实现;
比如
使用 Spring JDBC 或 Mybatis 进行持久化数据时的 DataSourceTransactionManager;
使用 Hibernate 进行持久化数据时的 HibernateTransactionManager;
使用 JPA 进行持久化数据时的 JpaTransactionManager;
同时,Spring 可以使用 TransactionTemplate 进行编程式的事务控制;
Spring 基于 AOP 的声明式事务又有三种方式
- 基于 TransactionProxyFactoryBean 的方式
- 基于基于 AspectJ 的方式
- 基于注解方式
Spring容器启动流程是怎样的?
- 在创建Spring容器,也就是启动Spring时:
- ⾸先会进⾏扫描,扫描得到所有的BeanDefinition对象,并存在⼀个Map中
Spring中什么时候@Transactional会失效
Spring容器启动流程是怎样的
25 - 然后筛选出⾮懒加载的单例BeanDefinition进⾏创建Bean,对于多例Bean不需要在启动过程中去进
⾏创建,对于多例Bean会在每次获取Bean时利⽤BeanDefinition去创建 - 利⽤BeanDefinition创建Bean就是Bean的创建⽣命周期,这期间包括了合并BeanDefinition、推断
构造⽅法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发⽣在初始化
后这⼀步骤中 - 单例Bean创建完了之后,Spring会发布⼀个容器启动事件
- Spring启动结束
- 在源码中会更复杂,⽐如源码中会提供⼀些模板⽅法,让⼦类来实现,⽐如源码中还涉及到⼀些
BeanFactoryPostProcessor和BeanPostProcessor的注册,Spring的扫描就是通过
BenaFactoryPostProcessor来实现的,依赖注⼊就是通过BeanPostProcessor来实现的 - 在Spring启动过程中还会去处理@Import等注解
Spring事务什么时候会失效?
Spring事务的原理是Aop,进行了切面的增强,那么失效的根本原因是这个AOP不起作用了!
常见的情况有如下几种
1.发生自调用,类里面使用this调用本类的方法(this通常省略),此时这个this对象不是
代理类,而是UserService对象本身!
解决方法很简单,让那个this变成UserService的代理类即可。
2.方法不是public的,@Transaction只能用于public的方法上,否则事务不会生效,如果用在public的方法上,可以开启AspectJ代理模式。
3.数据库不支持事务
4.没有被spring管理
5.异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
BeanFactory 和 ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中
ApplicationContext是BeanFactory的子接口。
依赖关系
BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean
的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了
更完整的框架功能:
继承MessageSource,因此支持国际化。
统一的资源文件访问方式。
提供在监听器中注册bean的事件。
同时加载多个配置文件。
载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web
层。
加载方式
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才
对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个
属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可
以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预
载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经
创建好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较
多时,程序启动较慢。
创建方式
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用
ContextLoader。
注册方式
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,
但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
Spring 事务的传播行为了解吗?Spring 提供了几种事务的传播行为?
- REQUIRED:表示如果当前存在一个事务,则加入该事务,否则将新建一个事务;
- REQUIRES_NEW:表示不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务;
- SUPPORTS:表示如果当前存在事务,就加入该事务;如果当前没有事务,那就不使用事务;
- NOT_SUPPORTED: 表示不使用事务;如果当前存在事务,就把当前事务暂停,以非事务方式执行;
- MANDATORY:表示必须在一个已有的事务中执行,如果当前没有事务,则抛出异常;
- NEVER:表示以非事务方式执行,如果当前存在事务,则抛出异常;
- NESTED:这个是嵌套事务;如果当前存在事务,则在嵌套事务内执行;如果当前不存在事务,则创建一个新的事务;
嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚;
Spring事务的隔离级别?
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:
- ISOLATION_DEFAULT:默认的
这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是 ISOLATION_READ_COMMITTED。 - ISOLATION_READ_UNCOMMITTED:未提交读
该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。
该级别不能防止脏读、不可重复读和幻读,因此很少使用该隔离级别。 - ISOLATION_READ_COMMITTED:已提交读
该隔离级别表示一个事务只能读取另一个事务已经提交的数据。
该级别可以防止脏读,这也是大多数情况下的推荐值。 - ISOLATION_REPEATABLE_READ:可重复读
该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。
该级别可以防止脏读、不可重复读。 - ISOLATION_SERIALIZABLE:序列化
所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰;
也就是说,该级别可以防止脏读、不可重复读以及幻读。
但是这将严重影响程序的性能。通常情况下也不会用到该级别。
请谈一下 Spring 事务回滚机制 ?
默认情况下,Spring 只有在抛出的异常是运行时异常(“非检查型”)时才回滚该事务;
也就是抛出的异常为 RuntimeException 的子类(Errors 也会导致事务回滚);
而抛出非运行时异常(检查型)则不会导致事务回滚;
但是,我们可以明确的配置抛出哪些异常时回滚事务,包括 checked 异常。也可以定义哪些异常抛出时不回滚事务。
说一下过滤器和 Spring 拦截器的区别 ?
拦截器和过滤器都是 AOP 编程思想的体现,都能实现权限检查、日志记录等。
拦截器是基于反射实现,更准确的说是通过 jdk 的动态代理实现; 过滤器是基于函数回调。
拦截器不依赖于 Servlet 容器,过滤器依赖于 Servlet 容器,它属于 Servlet 规范规定的。
拦截器只能对 Controller 请求起作用,过滤器则可以对几乎所有的请求起作用。
拦截器可以访问 controller 上下文的对象(如 service 对象、数据源等),过滤器则不可以访问.
拦截器可以深入的方法前后、异常抛出前后等,并且可以重复调用; 过滤器只在 Servlet 前后起作用,并且只在初始化
时被调用一次.
Java 中的拦截器是基于 Java 反射机制实现的,更准确的划分,应该是基于 JDK 实现的动态代理;
它依赖于具体的接口,在运行期间动态生成字节码。
使用场景:日志记录、权限检查、性能监控、通用行为
拦截器的实现原理是什么?简单说一下拦截器用场景 ?
Java 中的拦截器是基于 Java 反射机制实现的,更准确的划分,应该是基于 JDK 实现的动态代理;
它依赖于具体的接口,在运行期间动态生成字节码。
使用场景:
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算 PV(Page View)等。
2、权限检查:比如登录检查,进入处理器之前检查是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,
在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如 apache 可以自动记录);
4、通用行为:读取 cookie 得到用户信息并将用户对象放入请求,从而方便后续流程使用;
反射机制了解吗?知道 spring 中哪些用的反射吗 ?
- 反射是 JAVA 语言提供一套在运行期动态获得类中信息的 API。
- 通过反射,可以在运行期动态的获得类中的属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私
有的方法和属性)
通过反射,可以在运行期动态的创建类的对象。
通过反射,可以在运行期动态的执行类中的方法。
Spring 通过反射创建对象,并将对象放到 spring ioc 容器中。
Spring 的拦截器也是基于反射实现的。
使用@Autowired注解自动装配的过程是怎样的 ?
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进
行配置,<context:annotation-config />。
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当
容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该
对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。