Java面试题6-spring框架相关

Spring中Bean的生命周期

在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;

    Spring上下文中的Bean也类似,如下

    1、实例化一个Bean--也就是我们常说的new;

    2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

    3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

    4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

    5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

    6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

    7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

    8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;

    注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。

    9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

    10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

image.png

spring如何解决循环依赖

所谓Spring的循环依赖,指的是这样一种场景:

当我们注入一个对象A时,需要注入对象A中标记了某些注解的属性,这些属性也就是对象A的依赖,把对象A中的依赖都初始化完成,对象A才算是创建成功。那么,如果对象A中有个属性是对象B,而且对象B中有个属性是对象A,那么对象A和对象B就算是循环依赖,如果不加处理,就会出现:创建对象A-->处理A的依赖B-->创建对象B-->处理B的对象A-->创建对象A-->处理A的依赖B-->创建对象B......这样无限的循环下去

虽说要初始化一个Bean,必须要注入Bean里的依赖,才算初始化成功,但并不要求此时依赖的依赖也都注入成功,只要依赖对象的构造方法执行完了,这个依赖对象就算存在了,注入就算成功了,至于依赖的依赖,以后再初始化也来得及。

因此,我们初始化一个Bean时,先调用Bean的构造方法,这个对象就在内存中存在了(对象里面的依赖还没有被注入),然后把这个对象保存下来,当循环依赖产生时,直接拿到之前保存的对象,于是循环依赖就被终止了,依赖注入也就顺利完成了。

举个例子:

假设对象A中有属性是对象B,对象B中也有属性是对象A,即A和B循环依赖。
创建对象A,调用A的构造,并把A保存下来。 然后准备注入对象A中的依赖,发现对象A依赖对象B,那么开始创建对象B。 调用B的构造,并把B保存下来。 然后准备注入B的构造,发现B依赖对象A,对象A之前已经创建了,直接获取A并把A注入B(注意此时的对象A还没有  完全注入成功,对象A中的对象B还没有注入),于是B创建成功。 把创建成功的B注入A,于是A也创建成功了。 于是循环依赖就被解决了。

注意,循环依赖对于初始化方式为构造器方式的没法解决

spring的依赖注入有哪几种方式

Spring主要使用的是依赖注入。一般而言,依赖注入可以分为3种方式。

  •构造器注入。

  •setter注入。

  •接口注入。

  

构造器注入

  构造器注入依赖于构造方法实现,而构造方法可以是有参数的或者是无参数的。在大部分的情况下,我们都是通过类的构造方法来创建类对象,Spring也可以采用反射的方式,通过使用构造方法来完成注入,这就是构造器注入的原理

public class Role {
 private Long id;  private String roleName;  private String note;    public Role(String roleName, String note) {  this.roleName = roleName;  this.note = note;  }   /******** setter and getter *******/ }

这个时候是没有办法利用无参数的构造方法去创建对象的,为了使Spring能够正确创建这个对象,可以像代码清单那样去做。

 

<bean id="role1" class="com.ssm.chapter9.pojo.Role">
 <constructor-arg index="0" value="总经理"/>  <constructor-arg index="1" value="公司管理者"/> </bean>

constructorarg元素用于定义类构造方法的参数,其中index用于定义参数的位置,而value则是设置值,通过这样的定义Spring便知道使用Role(String,String)这样的构造方法去创建对象了。这样注入还是比较简单的,但是缺点也很明显,由于这里的参数比较少,所以可读性还是不错的,但是如果参数很多,那么这种构造方法就比较复杂了,这个时候应该考虑setter注入

 

使用setter注入

setter注入是Spring中最主流的注入方式,它利用JavaBean规范所定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先可以把构造方法声明为无参数的,然后使用setter注入为其设置对应的值,其实也是通过Java反射技术得以现实的。这里假设先在代码清单中为Role类加入一个没有参数的构造方法,然后做代码清单的配置。

<bean id="role2" class="com.ssm.chapter9.pojo.Role">
 <property name="roleName" value="高级工程师"/>  <property name="note" value="重要人员"/> </bean

  这样Spring就会通过反射调用没有参数的构造方法生成对象,同时通过反射对应的setter注入配置的值了。这种方式是Spring最为主要的方式,在实际工作中使用广泛。

接口注入

  有些时候资源并非来自于自身系统,而是来自于外界,比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源,这个时候我们可以采用接口注入的形式来获取它

SpringMVC或Struts处理请求的流程

  • 用户发送请求至前端控制器DispatcherServlet
  • DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  • 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  • DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
  • 执行处理器(Controller,也叫后端控制器)。
  •  Controller执行完成返回ModelAndView
  •  HandlerAdapter将controller执行结果 ModelAndView返回给DispatcherServlet
  •  DispatcherServlet将ModelAndView传给 ViewReslover视图解析器(
  • 但是如果加上@Responsebody注解,则返回值不通过viewResolver,而是直接返回object
  •  ViewReslover解析后返回具体View
  •  DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
  •  DispatcherServlet响应用户

Spring AOP解决了什么问题

AOP(Aspect Oriented Programming)是基于切面编程的,可无侵入的在原本功能的切面层添加自定义代码,一般用于日志收集、权限认证等场景。

  • Jointpoint(连接点):具体的切面点点抽象概念,可以是在字段、方法上,Spring中具体表现形式是PointCut(切入点),仅作用在方法上。
  • Advice(通知): 在连接点进行的具体操作,如何进行增强处理的,分为前置、后置、异常、最终、环绕五种情况。
  • 目标对象:被AOP框架进行增强处理的对象,也被称为被增强的对象。
  • AOP代理:AOP框架创建的对象,简单的说,代理就是对目标对象的加强。Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理。
  • Weaving(织入):将增强处理添加到目标对象中,创建一个被增强的对象的过程

jdk动态代理和cglib代理的异同点

Spring中的AOP代理可以是JDK动态代理,也可以是CGLIB代理

JDK动态代理:

  • 通过实现InvocationHandlet接口创建自己的调用处理器;
  • 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
  • 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;

JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。

CGLib动态代理:

CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。

两者对比:

  • JDK动态代理是面向接口的。
  • CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)
  • 官方介绍的时候CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,不过需要额外的依赖包
  • Spirng默认采用JDK动态代理实现机制

使用注意:

  • 如果要被代理的对象是个实现类,那么Spring会使用JDK动态代理来完成操作
  • 如果要被代理的对象不是个实现类那么,Spring会强制使用CGLib来实现动态代理

Spring中BeanFactory和FactoryBean有什么区别

共同点:

         都是接口

区别:

BeanFactory,以Factory结尾,表示它是一个工厂类(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

      FactoryBean,一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑

Spring框架中IOC的原理是什么

(1).IoC是Inversion of Control的缩写,有的翻译成“控制反转”,还有翻译成为“控制反向”或者“控制倒置”。

(2).1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IoC 这个概念。简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IoC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图所示:

即把各个对象类封装之后,通过IoC容器来关联这些对象类。这样对象与对象之间就通过IoC容器进行联系,但对象与对象之间并没有什么直接联系。

如果去掉IoC容器后系统中的对象A与对象B就有了直接关系,如下图所示:

Spring事务的传播属性

事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 

例如:methodA事务方法调用methodB事务方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。

Spring定义了七种传播行为:

Spring 如何实现数据库事务

 

编程式事务:允许用户在代码中精确定义事务的边界。很少使用

声明式事务:(基于AOP)有助于用户将操作与事务规则进行解耦。简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

声明式事务管理只需要用到@Transactional 注解和@EnableTransactionManagement(开启事务管理功能)。它是基于 Spring AOP 实现的,并且通过注解实现,实现起来简单,对原有代码没有入侵性。

@Transactional注解不起作用的集中情况:

①Transactional注解标注方法修饰符为非public时,@Transactional注解将会不起作用。

②在类内部调用调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启。

③事务方法内部捕捉了异常,没有抛出新的异常,导致事务操作不会进行回滚。

④ 异常类型是不是unchecked异常。如果我想check异常也想回滚怎么办,注解上面写明异常类型即可。@Transactional(rollbackFor=Exception.class)

Spring IoC AOP自己用代码如何实现

Hibernate和Ibatis这类ORM框架的区别?什么是ORM,解决的痛点是什么

Hibernate对一二级缓存的使用,Lazy-Load的理解

 
 
 

猜你喜欢

转载自www.cnblogs.com/reload-sun/p/12216745.html