
文章目录
- Spring常见面试题总结
- 1.spring的模块有哪些?
- 2.说下你对spring的理解?
- 3.对spring ioc的理解和使用?
- 4.Bean是如何创建的?
- 5.spring注入bean的方式有哪些
- 6.Bean的生命周期?
- 7.Spring常用的设计模式有哪些?
- 8.BeanFactory和ApplicationContext的区别
- 9.spring是如何解决循环依赖的?
- 10.Spring Aop的理解和使用?
- 11.spring中的AOP是在DI之前还是之后
- 12.对于spring中的bean的懒加载如何实现
- 13.Spring bean是线程安全的吗?(资源的共享)
- 14.Spring Bean各作用域之间的区别?
- 15.spring中声明式事务注解中的参数
- 16.事务的特点
- 17.spring的事务失效场景(高频面试题)
- 18.对spring事务的理解?
- 19.springmvc的流程?
- 20.Spring事务和Mysql事务有什么区别?
- 21.加了Spring的事务注解后,用try catch捕获异常有什么影响
- 22.spring aop通知执行顺序
1.spring的模块有哪些?
Spring Core:基础,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。
Spring AOP:提供面向切面的编程实现。
Spring JDBC:Java数据库连接。
Spring JMS:Java消息服务。
Spring ORM:用于支持Hibernate等ORM工具。
Spring Web:为创建Web应用程序提供支持。
Spring Test:提供了对JUnit和TestNG测试的支持。
spring tx:事务
1.Spring框架帮我们管理对象及其依赖关系
2.是基于POJO的轻量级和最小侵入性编程
3.通过依赖注入进行控制反转
4.通过依赖注入和面向接口来松耦合
以前没有spring的时候,我们需要得到一个对象,都是自己主动去new一个对象,然后通过set方法给对象注入属性,但是这种动作其实是一个重复的动作,所以spring提供ioc的容器解决方案,在容器启动的时候就把许多需要实例化和属性注入的bean都提前做好并放入到一个map中存储起来。这就是控制反转,原来的控制全在用户,现在的控制权完全交给了容器,在bean实例化后,通过反射对属性进行依赖注入
有两种使用方式,一种是xml的方式,一种是注解的方式。
xml的加载方式,首先在spring的xml中通过bean标签配置我们需要注入的bean,当扫描到所有的bean后,首先把bean包装成BeanDefinition,放入到list中,然后循环这个list去创建bean,创建bean的步骤为实例化->属性注入->初始化->aop,然后最终放入到spring的一级缓存中保存起来。
注解方式是通过在类上面加上spring的注解去扫描,比如controller,service等,后续步骤和xml基本一样。
第一步:找到哪些bean需要去实例化 ;xml方式去找,一种注解的方式去找,封装成一个BeanDefinition放入到list中
第二步:循环list,创建一个个bean对象
1.实例化
2.属性注入
3.初始化
4.aop
第三步:放入到一级缓存中
1.通过@Configuration+@Bean的方式注入
2.通过实现ImportBeanDefinitionRegistrar接口可以往容器中注入BeanDefinition,从而注入bean
3.通过实现ImportSelector接口可以往spring容器中批量注入Bean
4.通过实现FactoryBean接口可以往Spring容器中自定义Bean
5.@Componet + @ComponentScan
spring的bean的生命周期主要是创建bean的过程,一个bean的生命周期主要是4个步骤,实例化,属性注入,初始化,销毁,但是对于一些复杂的bean的创建,spring会在bean的生命周期中开放很多的接口,可以让你加载bean的时候对bean做一些改变,因此spring的bean的生命周期总共有以下几步:
首先在spring中有一些特殊的bean会介入到其他bean的声明周期当中去,所以一个普通的bean的生命周期为:
实现了BeanFactoryPostProcessor接口的bean,在加载其他的bean的时候,也会调用这个bean的postProcessBeanFactory方法,可以在这个步骤去对bean中的属性去赋值。设置年龄初始化18等等。
实现了InstantiationAwareBeanPostProcessor接口的bean,会在实例化bean之前调用postProcessBeforeInstantiation方法
然后在对bean进行实例化
对bean进行属性注入
对bean进行初始化,在初始化中,包含了以下几个步骤:
1)实现了BeanFactoryAware接口,会先调用setBeanFactory方法
2)实现了BeanNameAware接口,会先调用setBeanName方法
3)实现了BeanPostProcessor接口,会先调用postProcessBeforeInitialization方法
3)实现了InitializingBean接口,会调用afterPropertiesSet方法
4)然后在进行aop后置处理,通过实现BeanPostProcessor接口,在postProcessAfterInitialization方法中进行动态代理
销毁
当然还有一些其他的步骤,在此就不一一列举了,bean的生命周期总共有18步。
1.工厂设计模式:Spring使用工厂模式可以通过BeanFactory或ApplicationContext创建bean对象。
2.单例设计模式:Spring中bean的默认作用域就是singleton。spring的一级缓存就是使用的容器式单例
3.代理设计模式:Spring AOP就是基于动态代理的。如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用JDK Proxy去进行代理了,这时候Spring AOP会使用Cglib,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理。
4.模板方法设计模式:Spring中jdbcTemplate、hibernateTemplate等以Template结尾的对数据库 *** 作的类,它们就使用到模板模式。
5.观察者设计模式:Spring事件驱动模型就是观察者模式很经典的应用。
spring的事件流程:
1)定义一个事件: 实现一个继承自 ApplicationEvent,并且写相应的构造函数
2)定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法
3)使用事件发布者发布消息: 可以通过 ApplicationEventPublisher 的 publishEvent() 方法发布消息
6.适配器设计模式:适配器模式使得接口不兼容的那些类可以一起工作,其别名为包装器
在Spring MVC中,DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler,解析到对应的Handler(也就是我们常说的Controller控制器)后,开始由HandlerAdapter适配器处理
7.装饰者设计模式:装饰者设计模式可以动态地给对象增加些额外的属性或行为。相比于使用继承,装饰者模式更加灵活
Spring 中配置DataSource的时候,DataSource可能是不同的数据库和数据源。我们能否根据客户的需求在少修改原有类的代码下切换不同的数据源?这个时候据需要用到装饰者模式。
8.策略设计模式:Spring 框架的资源访问接口就是基于策略设计模式实现的
功能 BeanFactory ApplicationContext
对Bean进行创建 yes yes
集成生命周期管理 no yes
BeanPostProcessor自动注册 no yes
BeanFacoryPostProcessor自动注册 no yes
便捷的MessageSoure获取(国际化) no yes
内建ApplicationEvent发布机制 no yes
因为ApplicationContext包含BeanFactory的所有功能,所以通常建议在普通BeanFactory中使用。
BeanFactory :延迟注入,相比于ApplicationContext 来说会占用更少的内存,程序启动速度更快。
ApplicationContext :容器启动的时候,不管你用没用到,一次性创建所有 bean 。BeanFactory 仅提供了最基本的依赖注入支持,ApplicationContext 扩展了 BeanFactory ,除了有BeanFactory的功能还有额外更多功能,所以一般开发人员使ApplicationContext会更多
1.构造函数循环依赖(无法解决):不能实例化
2.setter方式的多例的循环依赖(无法解决)
如果是多例的,在容器初始化的时候,不会去创建,所以早期没有放入到三级缓存中暴露出来,所以无法解决循环依赖,会报错
3.setter方式的单例循环依赖(A依赖B,B依赖A)
A与B循环依赖,都要被代理的情况
1.A实例化->A放入三级缓存->依赖注入B->B实例化->B放入三级缓存->依赖注入a->去三级缓存拿a的代理->把代理的A放入二级->返回a的代理注入到B->b初始化->走后置通知获得代理b->B的代理放入一级缓存、->原生a依赖注入b->A初始化->后置通知,不走代理返回原生A->再偷天换日->放入一级缓存
Aop面向切面编程,在spring中,通过配置切面类和切点,切面类中主要有前置通知方法,后置通知方法,异常通知方法以及环绕通知方法,切面类中的方法的逻辑就是你要织入代码的逻辑,然后你需要判断哪些类需要进行切面拦截,所以就要配置切入点,可以使用xml方式,也可以使用注解的方式。
实现方式主要是通过动态代理给目标类生成代理对象,如果目标类实现了接口就使用jdk代理,如果没有实现接口使用cglib代理,在spring容器启动后,就会去扫描那些需要进行aop的类,这些类有哪些切面(切面是可以配置多个的),然后给目标类的每个方法都绑定一个相应的执行链,并放入到一个map中,当你调用时,会走代理类,然后通过索引加递归的方式去调用切面类前置通知方法,目标类方法,后置通知方法这样的顺序去调用。
使用:可以拦截目标类,在方法的前后织入日志打印逻辑,包括spring事务开启也是使用的aop,还有动态数据源切换等场景都是可以使用aop。如果配置多个切面,则要指定执行顺序,通过设置sort的大小来指定,sort值越小,越先执行,越后结束,sort值越大,越后执行,越先结束,就相当于一个同心圆。事务sort值默认是int类型的最大值。
对于单个对象创建,我们可以看到当前对象AOP是在DI之后
循环依赖的时候 ,AOP在DI之间
@lazy
多例的bean
这个问题主要是针对Spring中的bean的线程安全问题。
Spring中的Bean默认是单例的,单例的是被所有线程共享的,所以会存在安全问题,如果想要线程安全,可以设置为多例的Bean。
追问:那么为什么有些bean是单例的呢,却又是安全的?
因为一般来说线程不安全是说共享的资源不安全,但是有些bean其实没有资源的概念,比如dao接口,只有像实体类,我们一般设置为多例的,因为里面包含了数据资源。
1.singleton:这种bean范围是默认的,这种范围确保不管接受到多个请求,每个容器中有一个bean的实例,单利模式由bean factory自身来维护。
2.Protype:原先通过范围与单例范围相反,为每一个bean请求提供一个实例。
3.request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成之后,bean会失效并被垃圾回收器回收。
4.session:与请求范围类似,确保每个session中的bean的实例在session过期后bean会随之消失。
1.propagation传播机制
2.isolation隔离级别
3.timeout超时时间,如果共用一个事务,只有之前的事务设置的timeout有效
4.rollbackFor回滚异常
5.noRollbackFor不会滚异常
事务应该具有 4 个属性:原子性、一致性、隔离性、持久性
原子性(Automicity)
一个事务是一个不可分割的工作单位,事务中包括的诸 *** 作要么都做,要么都不做。
一致性(Consistency)
事务必须是使数据库从一个一致性状态变到另一个一致性状态。
一致性与原子性是密切相关的。
隔离性(Isolation)
一个事务的执行不能被其他事务干扰。
即一个事务内部的 *** 作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性(Durability)
持久性也称永久性(Permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他 *** 作或故障不应该对其有任何影响。
1.非public方法 事务的实现原理是代理增强,非public不能进行代理增强,不能进行JDK或者CGLIB代理,cglib是非private的就可以代理
2.调用本类的方法 (调用methodC方法)
3.抛出捕捉的非RuntimeException,如果想要捕捉所有异常(rollbackFor = {Exception.class})
或者指定@Transactional(rollbackFor = {RuntimeException.class,ClassNotFoundException.class},propagation = Propagation.REQUIRED) 多个异常类
4.我们知道事务就是依赖于数据库的,所以数据库不支持肯定也是失效的!!比如myIsam
spring的事务开启方式有两种,一个是声明式事务,一个是编程式事务,声明式事务是通过添加Transaction注解的方式开启事务,一般加在类或者方法上,事务控制粒度比较大,但使用上比较方便,编程式事务通过TransactionTemplate,控制事务的粒度小,但是代码侵入性比较强。使用spring aop实现。
spring事务提供了7种传播机制:
REQUIRED(必须的):是默认的传播机制,如果B方法中调用A,如果B中有事务,则A无论是否开启事务都会用B的事务,任何地方出现异常A和B都回滚
REQUIRES_NEW(需要新的):每次都会开启一个新的事务,外面事务回滚,里面事务不会回滚
NESTED:开启新事务,提交事务依赖于外层事务,如果外层事务回滚,则里面事务也回滚
SUPPORTS(1): 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行,完全依赖最外层事务
MANDATORY(强制性的):必须运行在事务里面
NOT_SUPPORTED:以非事务方式执行 *** 作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
spring事务还提供了四种隔离级别:
DEFAULT(-1):数据库默认的隔离级别
READ_UNCOMMITTED(1):读未提交 ru,会导致脏读
READ_COMMITTED(2):读已提交 rc 避免脏读,允许不可重复读和幻读
REPEATABLE_READ(4):可重复读 rr 避免脏读,不可重复读,允许幻读,innodb存储引擎解决了幻读
SERIALIZABLE:串行化
从上到下,隔离级别越来越高,并发性能就越来越差,spring事务的本质还是数据库的事务,如果数据库不支持事务,spring的事务也就没有了意义
1.用户发送请求至前端控制器DispatcherServlet;
2.DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler;
3.处理器映射器根据请求url找到具体的处理器Handler,生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet;
4.DispatcherServlet 调用 HandlerAdapter处理器适配器,请求执行Handler;
5.HandlerAdapter 经过适配调用 具体处理器进行处理业务逻辑;
6.Handler执行完成返回ModelAndView;
7.HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
8.DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
9.ViewResolver解析后返回具体View;
10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11.DispatcherServlet响应用户。
Spring事务本质是对数据库事务的支持,如果数据库不支持事务(例如MySQL的MyISAM引擎不支持事务),则Spring事务也不会生效。
21.加了Spring的事务注解后,用try catch捕获异常有什么影响如果没有设置捕获的异常,然后捕获的是RuntimeException异常,则会回滚,如果不是,不会回滚
可以自己设置回滚的异常,也可以设置不回滚的异常
正常:Around.class ->Before.class->Before.class->目标方法
->Around.class->After.class->AfterReturning.class
异常:Around.class ->Before.class->目标方法->Around.class->After.class
->AfterThrowing.class(存疑)
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)