面试题-Spring

Spring概述

什么是spring?

spring是一个轻量级的java开发框架,它的出现简化了企业级应用开发的难度,是Java开发人员更专注于业务代码逻辑的编写。它有两个特点:IOC控制翻转和AOP面向切面编程。

谈谈IOC和AOP

IOC(控制翻转)

平时我们需要使用对象,都是自己new一个出来。这样在我们项目中,就会出现大量的冗余对象。而IOC控制翻转所在的事情,就是我们之前自己new对象的操作取代了,对象由spring的IOC容器创建并管理。我们需要用到一个对象的时候,只需要向IOC容器的beanFactory索取即可。总体来说,就是将对象创建的权利从程序员手上,交到了IOC容器手中。

AOP(面向切面编程)

AOP是对OOP(面向对象编程)的一种补充,面向对象编程是将事务的特征和行为抽象成一个个对象,而面向切面编程则是在一个个对象的基础上的进一步抽象。找寻这些对象都需要做的事情,然后就成一把刀一样在这些对象上切出一个面,来加入对象都需要做的事情。AOP是通过静态代理或者动态代理来完成的,其他动态代理更为灵活,分为Java自带的代理接口和Cglib动态代理。我们通常使用AOP来完成日志、事务、安全检查相关的操作。

Spring由哪些模块组成?

  1. spring core:提供了框架的基本组成部分,如IOC、DI等功能。
  2. spring beans:提供了BeanFactory,是工厂模式的一个经典实现,spring将管理对象称为bean。
  3. spring context:构建与core封装包基础上的context封装包,提供了一种框架式的对象访问方法。
  4. Spring jdbc:拱了一个JDBC的抽象层,消除了繁琐的JDBC编码和数据库厂商特有的错误代码解析,用于简化JDBC的操作。
  5. Spring AOP:提供了面对想切面的编程实现,让你可以自定义拦截器、切点等。
  6. Spring web:提供了针对web开发的继承特性,例如文件上传,利用servlet listeners进行IOC容器初始化和针对web的applicationContext。
  7. Spring test:主要为测试提供支持的,支持使用Junit或TestNG对Spring组件进行单元测试和集成测试。

Spring框架中都用到了哪些设计模式?

  1. 工厂模式:BeanFactory就是简单工厂模式的体现的,用来创建对象的实例。
  2. 单例模式:Bean默认为单例模式。
  3. 代理模式:Spring的AOP面向切面编程就用来到了JDK的动态代理和CGLIB动态代理。
  4. 模板方法:比如,RestTemplate、JpaTemplate都用了模板方法,用来解决代码重复的问题。
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现ApplicationListener

Spring如何管理事务的?

Spring的事务声明有两种方式:编程式和声明式。
spring主要是通过“声明式事务”的方式对事务进行管理,即在配置文件、注解进行事务声明,通过AOP将事务切面切入程序,最大的好处是大大减少了代码量。

Spring IOC容器配置Bean的方式?

  1. XML文件bean标签的方式
  2. 注解的方式
  3. java Config代码类的方式

Bean是如何被管理的?【重点】

在spring框架中,一旦把一个bean纳入到Spring IOC容器之中,这个bean的生命周期就会交由容器进行管理,一般担当管理者角色的是BeanFactory或ApplicationContext。认识一下Bean的生命周期活动,对更好的利用它有很大的帮助。
概况来说主要有四个阶段:实例化、初始化、使用、销毁。

  1. 首先容器启动后,会对scope为singleton且非懒加载的bean进行实例化。
  2. 按照bean定义信息配置信息,注入所有的属性。
  3. 如果bean实现了BeanNameAware接口,会回调该接口的setBeanName()方法,传入该Bean的id,此时该bean就获得了自己在配置文件中的id。
  4. 如果Bean实现BeanFactoryAware接口,会回调该接口的setBeanFactory()方法,传入该bean的BeanFactory,这样该bean就获得了自己所在的BeanFactory。
  5. 如果Bean实现了ApplicationContextAware接口,会回调该接口的setApplicationContext()方法,传入该Bean的ApplicationContext,这样该bean就获得了自己所在的ApplicationContext。
  6. 如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation()方法。
  7. 如果Bean实现了InitializingBean接口,则会回调该接口的afterPropertiesSet()方法。
  8. 如果Bean配置了init-method方法,则会执行init-method配置的方法。
  9. 如果有Bean实现了BeanPostProcessor接口,则会回调该接口的postProcessAfterInitialization()方法。
  10. 经过流程9之后,就可以正式使用该bean了,对于scope为singleton的bean,spring的ioc容器中会缓存一份该bean的实例,而对于scope为prototype的bean,每次被调用都会new一个新的对象,声明周期就交给调用方管理了,不再是Spring容器进行管理了。
  11. 容器关闭后,如果Bean实现了DisposableBean接口,则会回调该接口的destroy()方法。
  12. 如果Bean配置了destroy-method方法,则会执行destroy-method配置的方法,至此,整个Bean的生命周期结束了。

Spring中的自动装配有哪些限制?

  • 如果使用了构造器注入或者settter注入,那么将覆盖自动装配的依赖关系。
  • 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
  • 优先使用显式的装配来进行更精确的依赖注入而不是使用自动装配。

Spring中Bean的作用域有哪些?

  1. singleton作用域: 在spring IOC容器中仅仅存在一个Bean实例,Bean是以单例方式存在的,为默认值。
  2. prototype作用域:每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXXXBean().
  3. request作用域:每一个http请求中的bean是单例的,该作用域仅适用于WebApplicationContext环境
  4. session作用域:每一个session中的bean是单例的,不同Session使用不同的Bean,仅适用于WebApplicationContext环境
  5. globalSession作用域:一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境。

什么是IOC和DI?DI是如何实现的?

IOC是控制反转的意思,之前没有引入IOC容器时,加入对象A依赖于对象B,那么对象A在创建初始化或者运行到某一点的时候,需要用到B对象时,就需要我们手动自己去创建一个B对象。这个时候无论是创建B对象,还是使用B对象的控制权都在我们自己的手上。但是在引入IOC容器之后,情形就完成变化了,当对象A运行过程中需要用到对象B的时候,不是需要我们自己再手动创建一个B对象了,而是IOC容器会自动创建一个B对象,帮助我们注入到A对象中。通过前后的对比,我们不难发现。对象A获取依赖对象B的过程,由主动行为编程了被动行为。对象的创建、管理权,从我们手上转移到了IOC容器中了。这就是IOC控制翻转。
关于DI依赖注入:其实DI与IOC控制反转是从不同方面描述的同一件事情,都是指将对象的创建、管理权从使用者的手上,通过依赖注入到IOC容器中。

IOC的优缺点

优点

降低了类之间的耦合,可维护性得到了提高。

缺点

  • 因为IOC容器中对象的生成是通过反射形成的,所以在运行效率上会有一些损失。
  • 要做一些额外的配置工作

你如何理解AOP中得到连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念?

  • 连接点:程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
  • 切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
  • 增强(Advice):增强是植入到目标类连接点上的一段程序代码。Spring提供的增强连接口都是带方位名的。如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。
  • 引介(Introduction):引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能可以动态的为该业务类添加接口的实现逻辑。让业务类成为这个接口的实现类。
  • 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:

    1. 编译期织入:需要特殊的Java编译期(例如AspectJ的ajc)
    2. 装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强
    3. 运行时织入:在允许时为目标生成代理实现增强。spring采用动态代词的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
  • 切面(Aspet):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

Spring自动装配的方式有哪些?

  1. no:不进行自动装配,而是手动设置Bean的依赖关系
  2. byName:根据Bean的名字进行自动装配
  3. byType:根据Bean的类型进行自动装配
  4. constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
  5. autodetect:如果有默认的构造器,这通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。

Spring中如何使用注解来配置Bean?有哪些相关的注解?

在启动类上添加@EnableAutoConfiguration或者@SpringBootApplication来开启自动配置,然后使用以下的注解来标注需要由Spring IOC容器进行对象托管的类。

  1. @Component:其他所有类
  2. @Controller:controller控制器
  3. @Service:service业务逻辑层
  4. @Repository:dao数据交互层
    这几个注解的作用其实都是相同的,都是将类注入到ioc容器中,只是为了方便区别字面上有所不同罢了。

Spring MVC 的工作流程

  1. 客户端的所有请求都叫给前端控制dispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
  2. 前端控制器DispatcherServlet收到请求后,将根据请求的信息以及HandlerMapping的配置找到处理该请求的对应Handler处理器。
  3. spring会通过HandlerAdapter对该处理器进行封装,然后用统一的接口对各种Handler中的方法进行调用。
  4. Handler完成对用户请求的处理后,会返回一个ModelAndView对象给前端控制器DispatcherServlet。
  5. DispatcherServlet借助逻辑视图解析器ViewResolver来解析刚返回得到的ModelAndView对象
  6. 最后拿到真正的视图对象,浏览器对模型数据进行渲染
  7. 客户端得到响应,可能是一个普通的html页面,也可能是xml或者是JSON字符串,还可以是一张图片或者PDF文件。

ApplicationContext通常的实现是什么?

  • FlieSystemXmlApplciationContext: 此容器从一个XML文件中加载bean是的定义,XML bean配置文件的全路径名必须提供给他的构造函数。
  • ClassPathXmlApplicationContext: 此容器也从一个XML文件中加载Beans的定义,这里,你需要正确设置classpath,因为这个容器将在classpath里面找到bean配置。
  • WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个web应用的所有bean。

Bean工厂和APlication contexts有什么区别?

  • 相同点:两者都是通过xml配置文件加载bean
  • 不同点:applicationContext相比于BeanFactory,提供了更多的扩展功能。BeanFactory是延迟加载,如果Bean的某一个属性没有注入,BeanFactory加载后,直至第一次使用调用getBean方法才会抛出异常。而ApplicationContext则在初始化自身就进行了效验,这样有利于检查说依赖属性是否注入。所以通常情况下我们选择使用ApplicationContext。

spring有几种依赖注入方式

  1. 变量注入

    • 直接在变量上面使用@Autowired的既是变量注入
    • 优点就是注入方式简单,非常简洁。
    • 缺点:注入的对象不能用final修饰;可能会导致循环依赖的出现;对于IOC容器以外的环境,除了使用反射来提供它需要的依赖,无法复用该实现类;
  2. 构造器注入

    • 显式注明必须强制注入,通过强制指明依赖注入来保证这个类的运行,防止出现空指针异常。
    • 注入对象可以使用final修饰。
    • 非IOC容器环境也可以使用new实例化该类的对象。
    • 避免了循环依赖,如果真的存在循环依赖,spring项目启动的时候就会报错。
    • 缺点就是当我们需要注入的对象过多的时候,构造函数的代码会显得非常的臃肿。
  3. setter方法注入

    • 依赖注入中使用的依赖是可选的,选择依赖的意思是注入的依赖是可以为NULL。
    • 允许在类构造完成后重新注入
    • 缺点就是注入对象不能使用final修饰

    总结:如果注入的属性是必选的属性,则通过构造器注入的方式;如果注入的属性是可选的,则通过setter注入的方式;至于变量注入,不建议使用,虽然很省劲。

什么是spring beans?

我们的spring应用程序就是由一个个spring beans组成的,它们通过注解或者xml配置文件的方式定义,然后注入到的IOC容器中,由IOC容器管理整个生命周期。

spring中的bean是线程安全的吗?

首先spring IOC容器本身并没有提供线程安全策略,因此我们对于bean的线程安全问题,我们需要结合具体的bean作用域来说:

  1. singleton:单例bean,不会创建新的bean,所有的线程都公用一个bean对象,因此可能会存在线程安全问题。
  2. prototype:原型bean,因为每次都会创建一个bean对象,所以不会出现线程安全问题。

你可能感兴趣的:(spring面试)