Spring面试总结大全
2023-03-01 20:08:24 1 举报
AI智能生成
Spring面试总结大全
作者其他创作
大纲/内容
spring
spring的启动流程?
笼统的说法<br>启动Spring时: <br>1. ⾸先会进⾏扫描,扫描得到所有的BeanDefinition对象,并存在⼀个Map中 <br>2. 然后筛选出⾮懒加载的单例BeanDefinition进⾏创建Bean,对于多例Bean不需要在启动过程中去 进⾏创建,对于多例Bean会在每次获取Bean时利⽤BeanDefinition去创建 <br>3. 利⽤BeanDefinition创建Bean就是Bean的创建⽣命周期,这期间包括了合并BeanDefinition、推 断构造⽅法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发⽣在初始 化后这⼀步骤中 <br>4. 单例Bean创建完了之后,Spring会发布⼀个容器启动事件 <br>5. Spring启动结束
说一下你理解的 IOC 是什么?
首先 IOC 是一个「容器」,是用来装载对象的,它的核心思想就是「<b><font color="#f44336">控制反转</font></b>」<br><br>那么究竟「什么是控制反转」?<br><br>控制反转就是说,「<b><font color="#f44336">把对象的控制权交给了 spring,由 spring 容器进行管理</font></b>」,我们不进行任何操作<br><br>那么为「什么需要控制反转」?<br><br>我们想象一下,没有控制反转的时候,我们需要「自己去创建对象,配置对象」,还要「人工去处理对象与对象之间的各种复杂的依赖关系」,当一个工程的量起来之后,这种关系的维护是非常令人头痛的,所以就有了<b><font color="#f44336">控制反转</font></b>这个概念,<b><font color="#f44336">将对象的创建、配置等一系列操作交给 spring 去管理,我们在使用的时候只要去取就好了</font></b>
那么 DI 又是什么?
DI 就是<b><font color="#f44336">依赖注入</font></b>,其实和 IOC 大致相同,只不过是「同一个概念使用了不同的角度去阐述」<br><br>DI 所描述的「重点是在于依赖」,我们说了 「IOC 的核心功能就是在于在程序运行时动态的向某个对象提供其他的依赖对象」,而这个功能就是依靠 DI 去完成的,比如我们需要注入一个对象 A,而这个对象 A 依赖一个对象 B,那么我们就需要把这个对象 B 注入到对象 A 中,这就是依赖注入<br><br><b><font color="#f44336">依赖注入是指在程序运行期间,由外部容器动态地将依赖对象注入到组件中</font></b><br><br>spring 中有<b><font color="#f44336">三种注入方式</font></b><br><br><b><font color="#f44336">接口注入<br>构造器注入<br>set注入</font></b><br>
说说 AOP 是什么?
<b><font color="#f44336">AOP面向切面编程,简单的理解就是,在不修改原有代码的基础上对某些功能进行增强。<br>通常用于,事务,日志,权限等</font></b><br><br>AOP 意为:「面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术」。<br><br>AOP 是 「OOP(面向对象编程) 的延续」,是 Spring 框架中的一个重要内容,是函数式编程的一种衍生范型。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。<br><br>「AOP 实现主要分为两类:」<br>「静态 AOP 实现」, AOP 框架「在编译阶段」对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ<br>「动态 AOP 实现」, AOP 框架「在运行阶段」对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP<br>
spring 中 AOP 的实现是「通过动态代理实现的」,如果是实现了接口就会使用 <b><font color="#f44336">JDK 动态代理</font></b>,否则就使用<b><font color="#f44336"> CGLIB 代理</font></b>。<br>JDK动态代理:只能<b><font color="#f44336">代理接口</font></b>,不能代理类<br>CGLIB代理:是通过<b><font color="#f44336">继承的方式做的动态代理</font></b>,如果类被<b><font color="#f44336">final修饰</font></b>它就<b><font color="#f44336">无法</font></b>使用<b><font color="#f44336">CGLIB做动态代理</font></b>
「有 5 种通知类型:」<br><br><b><font color="#f44336">前置通知</font></b> Before advice:在连接点前面执行,前置通知不会影响连接点的执行,除非此处抛出异常<br><b><font color="#f44336">后置通知</font></b> After returning advice:在连接点正常执行完成后执行,如果连接点抛出异常,则不会执行<br><b><font color="#f44336">异常通知</font></b> After throwing advice:在连接点抛出异常后执行<br><b><font color="#f44336">最终通知</font></b> After (finally) advice:在连接点执行完成后执行,不管是正常执行完成,还是抛出异常,都会执行返回通知中的内容<br><b><font color="#f44336">环绕通知</font></b> Around advice:环绕通知围绕在连接点前后,能在方法调用前后自定义一些操作,还需要负责决定是继续处理 join point (调用 ProceedingJoinPoint 的 proceed 方法)还是中断执行<br>
Spring中Bean的生命周期
它有四个阶段:实例化 -》属性赋值 -》 初始化 -》销毁
spring事务失效场景<br>
1、service没有托管给spring<br>失效原因: spring事务生效的前提是,service必须是一个bean对象<br>解决方案: 将service注入spring<br><br>2、抛出受检异常<br>失效原因: spring默认只会回滚非检查异常和error异常<br>解决方案: 配置rollbackFor<br><br>3、业务自己捕获了异常<br>失效原因: spring事务只有捕捉到了业务抛出去的异常,才能进行后续的处理,如果业务自己捕获了异常,则事务无法感知<br>解决方案:<br> 1、将异常原样抛出;<br> 2、设置TransactionAspectSupport.currentTransactionStatus().setR ollbackOnly();<br><br>4、切面顺序导致<br>失效原因: spring事务切面的优先级顺序最低,但如果自定义的切面优先级和他一样,且自定义的切面没有正确处理异常,则会同业务自己捕获异常的那种场景一样<br>解决方案:<br> 1、在切面中将异常原样抛出;<br> 2、在切面中设置TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();<br><br>5、非public方法<br>失效原因: spring事务默认生效的方法权限都必须为public<br>解决方案:<br> 1、将方法改为public;<br> 2、修改TansactionAttributeSource,将publicMethodsOnly改为false【这个从源码跟踪得出结论】<br> 3、开启 AspectJ 代理模式【从spring文档得出结论】<br><br>6、父子容器<br>失效原因: 子容器扫描范围过大,将未加事务配置的serivce扫描进来<br>解决方案:<br> 1、父子容器个扫个的范围;<br> 2、不用父子容器,所有bean都交给同一容器管理<br><br>7、方法用final修饰<br>失效原因: 因为spring事务是用动态代理实现,因此如果方法使用了final修饰,则代理类无法对目标方法进行重写,植入事务功能<br>解决方案:<br> 1、方法不要用final修饰<br><br>8、方法用static修饰<br>失效原因: 原因和final一样<br>解决方案:<br> 1、方法不要用static修饰<br><br>9、调用本类方法<br>失效原因: 本类方法不经过代理,无法进行增强<br>解决方案:<br> 1、注入自己来调用;<br> 2、使用@EnableAspectJAutoProxy(exposeProxy = true) + AopContext.currentProxy()<br><br>10、场景十:多线程调用<br>失效原因: 因为spring的事务是通过数据库连接来实现,而数据库连接spring是放在threadLocal里面。同一个事务,只能用同一个数据库连接。而多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务<br><br>11、场景十一:错误的传播行为<br>失效原因: 使用的传播特性不支持事务<br><br>12、场景十二:使用了不支持事务的存储引擎<br>失效原因: 使用了不支持事务的存储引擎。比如mysql中的MyISAM<br><br>13、场景十三:数据源没有配置事务管理器<br>注: 因为springboot,他默认已经开启事务管理器。org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration。因此示例略过<br><br>14、被代理的类过早实例化<br>失效原因: 当代理类的实例化早于AbstractAutoProxyCreator后置处理器,就无法被AbstractAutoProxyCreator后置处理器增强<br>
你们项目中为什么使用Spring框架?
轻量:Spring 是轻量的,基本的版本大约2MB。<br>控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找<br>依赖的对象们。<br>面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。<br>容器:Spring 包含并管理应用中对象的生命周期和配置。<br>MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。<br>事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务<br>(JTA)。<br>异常处理:Spring 提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛<br>出的)转化为一致的unchecked 异常。
spring 中都用到了哪些设计模式?<br>
1、<b><font color="#f44336">工厂模式</font></b>:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;<br>2、<b><font color="#f44336">单例模式</font></b>:Bean默认为单例模式。<br>3、<b><font color="#f44336" style="">代理模式</font></b>:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;<br>4、<b><font color="#f44336">模板方法</font></b>:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。<br>5、<b><font color="#f44336">观察者模式</font></b>:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–ApplicationListener。
工厂模式
比如通过 BeanFactory 和 ApplicationContext 来生产 Bean 对象
适配器模式
Spring 中的 AOP 中 AdvisorAdapter 类,它有三个实现:<br>MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。Spring<br>会根据不同的 AOP 配置来使用对应的 Advice,与策略模式不同的是,一个方法可以同时拥有多个<br>Advice。Spring 存在很多以 Adapter 结尾的,大多数都是适配器模式。
模板方法模式
Spring 中 jdbcTemplate 等以 Template 结尾的对数据库操作的类,都会使用到模板方法设计模式,一些通用的功能
包装器模式
我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源
在你项目中使用到了哪些设计模式?使用在了哪里?为什么使用它?
spring怎么提交事务的?
1.判断事务是否已经完成,如果完成抛出异常<br>2.判断事务是否已经被标记成回滚,则执行回滚操作<br>3.嵌入事务标记回滚,如果嵌入事务抛出了异常执行了回滚,但是在调用方把嵌入事务的异常个捕获没有抛出,就会执行这一步。<br>4.提交事务
那 BeanFactory 和 FactoryBean 又有什么区别?<br>
这两个是「不同的产物」<br><br>「BeanFactory 是 IOC 容器」,是用来承载对象的<br><br>「FactoryBean 是一个接口」,为 Bean 提供了更加灵活的方式,通过代理一个Bean对象,对方法前后做一些操作。<br>
BeanFactory和ApplicationContext的区别?
BeanFactory在启动的时候不会去实例化Bean,从容器中拿Bean的时候才会去是实例化<br>ApplicationContext在启动的时候就把所有的Bean全部实例化了
@Repository、@Service、@Compent、@Controller它们有什么区别?
这四个注解的「本质都是一样的,都是将被该注解标识的对象放入 spring 容器当中,只是为了在使用上区分不同的应用分层」<br><br>@Repository:dao层<br>@Service:service层<br>@Controller:controller层<br>@Compent:其他不属于以上三层的统一使用该注解
动态代理和静态代理有什么区别?
「静态代理」<br><br>由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了<br>静态代理通常只代理一个类<br>静态代理事先知道要代理的是什么<br><br>「动态代理」<br><br>在程序运行时,运用反射机制动态创建而成<br>动态代理是代理一个接口下的多个实现类<br>动态代理不知道要代理什么东西,只有在运行时才知道<br>
三种实现指定初始化方法
使用@PostConstruct注解,该注解作用于void方法上<br>在配文件中配置init-method方法<br>将类实现InitializingBean接口,实现afterPropertiesSet方法
如何实现AOP,项⽬哪些地⽅⽤到了AOP?
利⽤动态代理技术来实现AOP,⽐如JDK动态代理或Cglib动态代理,利⽤动态代理技术,可以针对某个类 ⽣成代理对象,当调⽤代理对象的某个⽅法时,可以任意控制该⽅法的执⾏,⽐如可以先打印执⾏时间, 再执⾏该⽅法,并且该⽅法执⾏完成后,再次打印执⾏时间。 <br><br>项⽬中,⽐如事务、权限控制、⽅法执⾏时⻓⽇志都是通过AOP技术来实现的,凡是需要对某些⽅法做统 ⼀处理的都可以⽤AOP来实现,利⽤AOP可以做到业务⽆侵⼊。
Spring的对象默认是单例的还是多例的?单例bean存不存在线程安全问题?
在Spring中的对象默认是单例的,但是也可以配置为多例<br>单例bean对象对应的类存在可变的成员变量并且其中存在改变这个变量的线程时,多线程操作该bean对象是会出现线程安全问题<br>原因是:多线程操作如果改变成员变量,其他线程无法访问该bean对象,造成数据混乱<br>解决办法:在bean对象中避免定义可变成员变量;<br> 在bean对象中定义一个ThreadLocal成员变量,将要的可变成员变量保存在ThreadLocal中
Spring如果解决循环依赖?
通过三级缓存:<br>一级缓存,用于保存实例化,注入,初始化完成的bean实例<br>二级缓存,用于保存实例化完成的bean实例<br>三级缓存,用于保存bean创建工厂
依赖注入的方式有几种,各是什么?
构造器注入<br>setter注入<br>接口注入
@Autowired 和 @Resource 有什么区别?
「@Resource 是 Java 自己的注解」,@Resource 有两个属性是比较重要的,分是 name 和 type;Spring 将 @Resource 注解的 name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。<br><br>「@Autowired 是spring 的注解」,是 spring2.5 版本引入的,Autowired 只根据 type 进行注入,「不会去匹配 name」。如果涉及到 type 无法辨别注入对象时,那需要依赖 @Qualifier 或 @Primary 注解一起来修饰。
解释Spring支持的几种bean的作用域?
Spring容器中的bean可以分为5个范围:<br>(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维<br>护。<br>(2)prototype:为每一个bean请求提供一个实例。<br>(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回<br>收。<br>(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,<br>bean会随之失效。<br>(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet<br>容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那<br>么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
说一下spring的事务机制?
1. Spring事务底层是基于数据库事务和AOP机制的 <br>2. ⾸先对于使⽤了@Transactional注解的Bean,Spring会创建⼀个代理对象作为Bean <br>3. 当调⽤代理对象的⽅法时,会先判断该⽅法上是否加了@Transactional注解 <br>4. 如果加了,那么则利⽤事务管理器创建⼀个数据库连接 <br>5. 并且修改数据库连接的autocommit属性为false,禁⽌此连接的⾃动提交,这是实现Spring事务⾮常重 要的⼀步 <br>6. 然后执⾏当前⽅法,⽅法中会执⾏sql <br>7. 执⾏完当前⽅法后,如果没有出现异常就直接提交事务 <br>8. 如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务 <br>9. Spring事务的隔离级别对应的就是数据库的隔离级别 <br>10. Spring事务的传播机制是Spring事务⾃⼰实现的,也是Spring事务中最复杂的 <br>11. Spring事务的传播机制是基于数据库连接来做的,⼀个数据库连接⼀个事务,如果传播机制配置为需要 新开⼀个事务,那么实际上就是先建⽴⼀个数据库连接,在此新数据库连接上执⾏sql
spring 事务隔离级别有哪些?
DEFAULT:采用 DB 默认的事务隔离级别<br>READ_UNCOMMITTED:读未提交<br>READ_COMMITTED:读已提交<br>REPEATABLE_READ:可重复读<br>SERIALIZABLE:串行化
spirng的事务管理?
1、编程式事务:<br>beginTransaction()、commit()、rollback()等事务管理相关的方法<br>2、声明式事务<br>利用注解Transactional或者aop配置
Spring事务的传播行为
它有七种传播行为:<br>propagation_required:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务<br>propagation_supports:如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行<br>propagaion_mandatory:如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常<br>propagation_requires_new:无论当前存不存在事务,都创建新事务。<br>propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起<br>propagation_never:以非事务方式执行,如果当前存在事务,则抛出异常<br>propagation_nested:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则按required属性执行
spring事务是怎么回滚的?
判断是否存在事务,只有存在事务才执行回滚,即是否有@Transactional事务注解或相关事务切面<br>根据异常类型判断是否回滚。如果异常类型不符合,仍然会提交事务<br>根据@Transactional注解中rollbackFor、rollbackForClassName、noRollbackForClassName配置的值,找到最符合ex的异常类型,如果符合的异常类型不是NoRollbackRuleAttribute,则可以执行回滚。<br>如果@Transactional没有配置,则默认使用RuntimeException和Error异常。<br><br>回滚处理<br>如果存在安全点,则回滚事务至安全点,这个主要是处理嵌套事务,回滚安全点的操作还是交给了数据库处理.<br>当前事务是一个新事务时,那么直接回滚,使用的是DataSourceTransactionManager事务管理器,所以调用DataSourceTransactionManager#doRollback,直接调用数据库连接的回滚方法。<br>当前存在事务,但又不是一个新的事务,只把事务的状态标记为read-only,等到事务链执行完毕后,统一回滚,调用DataSourceTransactionManager#doSetRollbackOnly<br>清空记录的资源并将挂起的资源恢复<br>
spring 事务的传播机制有哪些?
1.「propagation_required」<br>当前方法「必须在一个具有事务的上下文中运行」,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)<br>
2.「propagation_supports」<br>当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行<br>
3.「propagation_mandatory」<br>表示当前方法「必须在一个事务中运行」,如果没有事务,将抛出异常<br>
4.「propagation_nested」<br>如果当前方法正有一个事务在运行中,则该方法应该「运行在一个嵌套事务」中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同propagation_required的一样<br>
5.「propagation_never」<br>当方法务不应该在一个事务中运行,如果「存在一个事务,则抛出异常」<br>
6.「propagation_requires_new」<br>当前方法「必须运行在它自己的事务中」。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。<br>
7.「propagation_not_supported」<br>方法不应该在一个事务中运行。「如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行」<br>
事务三要素是什么?
数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等。<br>事务管理器:像一个大管家,从整体上管理事务的处理过程,如打开、提交、回滚等。<br>事务应用和属性配置:像一个标识符,表明哪些方法要参与事务,如何参与事务,以及一些相关属<br>性如隔离级别、超时时间等。
spring事务不回滚场景<br>
1.错误的传播特性<br>
2.自己吞了异常<br>
3.手动抛了别的异常<br>
4.自定义了回滚异常<br>
5.嵌套事务回滚多了<br>
2.spring中使用了哪些设计模式?<br>
在spring中主要用到的设计模式有:工厂模式、单例模式、代理模式、模板模式、观察者模式、适配器模式。
1.工厂模式
IOC控制反转也叫依赖注入,它就是典型的工厂模式,通过sessionfactory去注入实例
解释:将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你 需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A类里面new这些bean了。<br>
总结:对象实例化与初始化进行解耦
2.单例模式
Spring中JavaBean默认为单例,因为spring上下文中会有很多个dao\service\action对象,如果用多例模式的话,就每次要用到的时候,都会重新创建一个新的对象,内存中就会有很多重复的对象,所以单例模式的特点就是能减少我们的内存空间,节约性能。<br>
还有常用Spring中 @Repository、@Component、@Configuration @Service注解作用下的类默认都是单例模式的,所以,我目前认为在Spring下使用单例最优的方式是将类@Component注册为组件。使用场景主要有:数据库配置、Redis配置、权限配置、Filter过滤、webMvcConfig、swagger及自定义的时间转换器、类型转换器、对接第三方硬件时,调用硬件的dll、so文件等。<br><br>单独使用@Component注解,只能控制到类上,使用@Configuration+@Bean可以控制到方法级别粒度,但是尽量避免@Component+@Bean组合使用,因为@Component+@Bean并不是单例,在调用过程中可能会出现多个Bean实例,导致蜜汁错误。<br>
并不是所有的注解默认都是单例模式,@RestController就是多例
3.代理模式
Spring中的AOP就是典型的代理模式,首先我们先聊聊AOP(面向切面编程)的的一个设计原理:<br>AOP可以说是对OOP的补充和完善 OOP引入了封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,oop允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能,日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致大量代码重复,而不利于各个模块的重用。<br>实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码<br>简单点解释,比方说你想在你的biz层所有类中都加上一个打印‘你好’的功能,这时就可以用AOP思想来做,你先写个类写个类方法,方法经实现打印‘你好’,然后IOC这个类ref="biz.* "让每个类都注入即可实现。<br>
4.模板模式
定义:模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤<br>目的:1.使用模版方法模式的目的是避免编写重复代码,以便开发人员可以专注于核心业务逻辑的实现<br> 2.解决接口与接口实现类之间继承矛盾问题<br>
5.观察者模式
定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新<br>例如:<br>报社的业务就是出版报纸。<br>向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户、你就会一直收到新报纸。<br>当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。<br>只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。<br>报社:被观察者<br>订户:观察者<br>一个报社对应多个订户<br>
在Spring中有一个ApplicationListener,采用观察者模式来处理的,ApplicationEventMulticaster作为主题,里面有添加,删除,通知等。<br>spring有一些内置的事件,当完成某种操作时会发出某些事件动作,他的处理方式也就上面的这种模式,当然这里面还有很多,可以了解下spring的启动过程。<br>
在java.util 包下 除了常用的 集合 和map之外还有一个Observable类,他的实现方式其实就是观察者模式。里面也有添加、删除、通知等方法。
6.适配器模式
定义:将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类能在一起工作,做法是将类自己的接口包裹在一个已存在的类中<br>例子:以手机充电为例,电压220V,手机支持5.5V,充电器相当于适配器
AOP和MVC中,都有用到适配器模式。<br>spring aop框架对BeforeAdvice、AfterAdvice、ThrowsAdvice三种通知类型的支持实际上是借助适配器模式来实现的,这样的好处是使得框架允许用户向框架中加入自己想要支持的任何一种通知类型,上述三种通知类型是spring aop框架定义的,它们是aop联盟定义的Advice的子类型。<br>Spring中的AOP中AdvisorAdapter类,它有三个实现:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。<br>
说说你对Spring MVC的理解
MVC:MVC是一种设计模式
M-Model 模型(完成业务逻辑:有javaBean构成,service+dao+entity)<br>V-View 视图(做界面的展示 jsp,html……)<br>C-Controller 控制器(接收请求—>调用模型—>根据结果派发页面)
工作原理:
1、 用户发送请求至前端控制器DispatcherServlet。 <br>2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。<br>3、 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器<br>拦截器(如果有则生成)一并返回给DispatcherServlet。 <br>4、 DispatcherServlet调用HandlerAdapter处理器适配器。<br>5、 HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。 <br>6、 Controller执行完成返回ModelAndView。 <br>7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。 <br>8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。<br>9、 ViewReslover解析后返回具体View。<br>10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。<br>11、 DispatcherServlet响应用户。
前端控制器(DispatcherServlet):接收请求,响应结果,相当于电脑的CPU。<br>处理器映射器(HandlerMapping):根据URL去查找处理器。<br>处理器(Handler):需要程序员去写代码处理逻辑的。<br>处理器适配器(HandlerAdapter):会把处理器包装成适配器,这样就可以支持多种类型的处理<br>器,类比笔记本的适配器(适配器模式的应用)。<br>视图解析器(ViewResovler):进行视图解析,多返回的字符串,进行处理,可以解析成对应的页<br>面。
SpringMVC常用的注解有哪些?
@RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中<br>的所有响应请求的方法都是以该地址作为父路径。<br>@RequestBody:注解实现接收http请求的json数据,将json转换为java对象。<br>@ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。
什么是spring?<br>
Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对<br>构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程<br>模型促进良好的编程习惯。
spring 中有哪些核心模块?<br>
1.「Spring Core」:Spring核心,它是框架最基础的部分,提供IOC和依赖注入DI特性<br>
2.「Spring Context」:Spring上下文容器,它是 BeanFactory 功能加强的一个子接口<br>
3.「Spring Web」:它提供Web应用开发的支持<br>
4.「Spring MVC」:它针对Web应用中MVC思想的实现<br>
5.「Spring DAO」:提供对JDBC抽象层,简化了JDBC编码,同时,编码更具有健壮性<br>
6.「Spring ORM」:它支持用于流行的ORM框架的整合,比如:Spring + Hibernate、Spring + iBatis、Spring + JDO的整合等<br>
7.「Spring AOP」:即面向切面编程,它提供了与AOP联盟兼容的编程实现<br>
spring 中的 IOC 容器有哪些?有什么区别?<br>
spring 主要提供了「两种 IOC 容器」,一种是 「BeanFactory」,还有一种是 「ApplicationContext」<br><br>它们的区别就在于,BeanFactory 「只提供了最基本的实例化对象和拿对象的功能」,而 ApplicationContext 是继承了 BeanFactory 所派生出来的产物,是其子类,它的作用更加的强大,比如支持注解注入、国际化等功能<br>
ApplicationContext 继承了 BeanFactory,BeanFactory 是 Spring 中比较原始的<br>Factory,它不支持 AOP、Web 等 Spring 插件。而 ApplicationContext 不仅包含了 BeanFactory<br>的所有功能,还支持 Spring 的各种插件,还以一种面向框架的方式工作以及对上下文进行分层和实<br>现继承。<br>BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身;而 ApplicationContext 面向使用<br>Spring 的开发者,相比 BeanFactory 提供了更多面向实际应用的功能,几乎所有场合都可以直接使<br>用 ApplicationContext,而不是底层的 BeanFactory。
实现AOP的步骤?
JDK 动态代理和 CGLIB 代理有什么区别?
JDK 动态代理时业务类「必须要实现某个接口」,它是「基于反射的机制实现的」,生成一个实现同样接口的一个代理类,然后通过重写方法的方式,实现对代码的增强。<br><br>CGLIB 动态代理是使用字节码处理框架 ASM,其原理是通过字节码技术为一个类「创建子类,然后重写父类的方法」,实现对代码的增强。
Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 是运行时增强,是通过「动态代理实现」的<br><br>AspectJ AOP 是编译时增强,需要特殊的编译器才可以完成,是通过「修改代码来实现」的,支持「三种织入方式」<br><br>「编译时织入」:就是在编译字节码的时候织入相关代理类<br>「编译后织入」:编译完初始类后发现需要 AOP 增强,然后织入相关代码<br>「类加载时织入」:指在加载器加载类的时候织入
主要区别 Spring AOP AspecjtJ AOP<br>增强方式 运行时增强 编译时增强<br>实现方式 动态代理 修改代码<br>编译器 javac 特殊的编译器 ajc<br>效率 较低(运行时反射损耗性能) 较高<br>织入方式 运行时 编译时、编译后、类加载时
spring 中 Bean 的生命周期是怎样的?<br>
(1)实例化Bean:<br>对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注<br>入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容<br>器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。<br>(2)设置对象属性(依赖注入):<br>实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以 及 通过BeanWrapper提供的设置属性的接口完成依赖注入。<br>(3)处理Aware接口:<br>接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:<br>①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String<br>beanId)方法,此处传递的就是Spring配置文件中Bean的id值;<br>②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传<br>递的是Spring工厂自身。<br>③如果这个Bean已经实现了ApplicationContextAware接口,会调用<br>setApplicationContext(ApplicationContext)方法,传入Spring上下文;<br>(4)BeanPostProcessor:<br>如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会<br>调用postProcessBeforeInitialization(Object obj, String s)方法。<br>(5)InitializingBean 与 init-method:<br>如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。<br>(6)如果这个Bean实现了BeanPostProcessor接口,将会调用<br>postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调<br>用的,所以可以被应用于内存或缓存技术;<br>以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。<br>(7)DisposableBean: 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现<br>的destroy()方法;<br>(8)destroy-method:<br>最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方<br>法。<br>
spring 是怎么解决循环依赖的?<br>
循环依赖就是说两个对象相互依赖,形成了一个环形的调用链路<br><br>spring 使用三级缓存去解决循环依赖的,其「核心逻辑就是把实例化和初始化的步骤分开,然后放入缓存中」,供另一个对象调用<br><br>「第一级缓存」:用来保存实例化、初始化都完成的对象<br>「第二级缓存」:用来保存实例化完成,但是未初始化完成的对象<br>「第三级缓存」:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象<br>
当 A、B 两个类发生循环引用时 大致流程<br><br>1.A 完成实例化后,去「创建一个对象工厂,并放入三级缓存」当中<br>如果 A 被 AOP 代理,那么通过这个工厂获取到的就是 A 代理后的对象<br>如果 A 没有被 AOP 代理,那么这个工厂获取到的就是 A 实例化的对象<br>2.A 进行属性注入时,去「创建 B」<br>3.B 进行属性注入,需要 A ,则「从三级缓存中去取 A 工厂代理对象」并注入,然后删除三级缓存中的 A 工厂,将 A 对象放入二级缓存<br>4.B 完成后续属性注入,直到初始化结束,将 B 放入一级缓存<br>5.「A 从一级缓存中取到 B 并且注入 B」, 直到完成后续操作,将 A 从二级缓存删除并且放入一级缓存,循环依赖结束<br>
spring 解决循环依赖有两个前提条件:<br><br>1.「不全是构造器方式」的循环依赖(否则无法分离初始化和实例化的操作)<br>2.「必须是单例」(否则无法保证是同一对象)<br>
为什么要使用三级缓存,二级缓存不能解决吗?<br>
不可以,主要是为了⽣成代理对象。<br>因为三级缓存中放的是⽣成具体对象的匿名内部类,他可以⽣成代理对象,也可以是普通的实例对象。<br>使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。<br>假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的Bean对象, BeanPostProcessor 去⽣成<br>代理对象之后,覆盖掉⼆级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不⼀致了。<br>
Spring中后置处理器的作⽤?
Spring中的后置处理器分为BeanFactory后置处理器和Bean后置处理器,它们是Spring底层源码架构设计 中⾮常重要的⼀种机制,同时开发者也可以利⽤这两种后置处理器来进⾏扩展。<br><br>BeanFactory后置处理器 表示针对BeanFactory的处理器,Spring启动过程中,会先创建出BeanFactory实例,然后利⽤ BeanFactory处理器来加⼯BeanFactory,⽐如Spring的扫描就是基于BeanFactory后置处理器来实现的;<br><br>Bean后置处理器也类似,Spring在创建⼀个Bean的过程中,⾸先会实例化得到⼀个对象,然后再 利⽤Bean后置处理器来对该实例对象进⾏加⼯,⽐如我们常说的依赖注⼊就是基于⼀个Bean后置处理器 来实现的,通过该Bean后置处理器来给实例对象中加了@Autowired注解的属性⾃动赋值,还⽐如我们常 说的AOP,也是利⽤⼀个Bean后置处理器来实现的,基于原实例对象,判断是否需要进⾏AOP,如果需 要,那么就基于原实例对象进⾏动态代理,⽣成⼀个代理对象。
为什么要用SpringBoot?
一、独立运行<br>Spring Boot而且内嵌了各种servlet容器,Tomcat、Jetty等,现在不再需要打成war包部署到容器<br>中,Spring Boot只要打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。<br>二、简化配置<br>spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。 <br>三、自动配置<br>Spring Boot能根据当前类路径下的类、jar包来自动配置bean,如添加一个spring-boot-starterweb启动器就能拥有web的功能,无需其他配置。<br>四、无代码生成和XML配置<br>Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作,这一切都是借助<br>于条件注解完成的,这也是Spring4.x的核心功能之一。<br>五、应用监控<br>Spring Boot提供一系列端点可以监控服务及应用,做健康检测。
说说常⽤的SpringBoot注解,及其实现
1. @SpringBootApplication注解:这个注解标识了⼀个SpringBoot⼯程,它实际上是另外三个注解的组 合,这三个注解是: <br>--a. @SpringBootConfiguration:这个注解实际就是⼀个@Configuration,表示启动类也是⼀个配 置类 <br>--b. @EnableAutoConfiguration:向Spring容器中导⼊了⼀个Selector,⽤来加载ClassPath下 SpringFactories中所定义的⾃动配置类,将这些⾃动加载为配置Bean <br>--c. @ComponentScan:标识扫描路径,因为默认是没有配置实际扫描路径,所以SpringBoot扫描的 路径是启动类所在的当前⽬录 <br>2. @Bean注解:⽤来定义Bean,类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean注解 的⽅法进⾏解析,将⽅法的名字做为beanName,并通过执⾏⽅法得到bean对象 <br>3. @Controller、@Service、@ResponseBody、@Autowired都可以说
springBoot 自动装配原理?<br>
1.容器在启动的时候会调用 EnableAutoConfigurationImportSelector.class 的 selectImports方法「获取一个全面的常用 BeanConfiguration 列表」<br><br>2.之后会读取 spring-boot-autoconfigure.jar 下面的spring.factories,「获取到所有的 Spring 相关的 Bean 的全限定名 ClassName」<br><br>3.之后继续「调用 filter 来一一筛选」,过滤掉一些我们不需要不符合条件的 Bean<br><br>4.最后把符合条件的 BeanConfiguration 注入默认的 EnableConfigurationPropertie 类里面的属性值,并且「注入到 IOC 环境当中」<br>
最后,说说Spring Boot 启动流程吧?
1. 准备环境,根据不同的环境创建不同的Environment<br>2. 准备、加载上下⽂,为不同的环境选择不同的Spring Context,然后加载资源,配置Bean<br>3. 初始化,这个阶段刷新Spring Context,启动应⽤<br>4. 最后结束流程<br>
0 条评论
下一页