Java面试题超详细整理《Spring篇》

Posted 龙源lll

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试题超详细整理《Spring篇》相关的知识,希望对你有一定的参考价值。

什么是 Spring 框架?

Spring 是一个开源的轻量级的Java开发框架,以 Ioc(Inverse Of control:反转控制)和AOP(Aspect Oriented Programming: 面向切面编程) 为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多企业级应用技术,其还整合众多开源的第三方框架,逐渐成为最受欢迎的Java EE企业应用框架。

Spring框架的特征:

  • 轻量高效:Spring 是一个开源的轻量级的Java开发框架
  • 控制反转IOC:通过 Spring 提供的 IOC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • 面向切面AOP:通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用OOP实现的功能可以通过AOP轻松应付。
  • 声明式事务:可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 容器: Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一个容器,你可以配置你的每个bean如何被创建。
  • 架:集成各种框架,Spring可以将降低各种框架的使用难度,提供了对各种优秀框架(Struts、 Hibernate、 Hessian、 Quart等)的直接支持。

列举一些重要的Spring模块?

Spring是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。

  • 核心容器Core Container: Core是所有组件的核心,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注入功能。Beans 组件和 Context 组件是实现IOC和依赖注入的基础
  • AOP :提供了面向切面的编程实现,Aspects : 该模块为与AspectJ的集成提供支持(AspectJ:Java生态系统中最完整的AOP 框架)。
  • 数据访问/集成:JDBC (Java数据库连接)、JMS(Java消息服务)、ORM(用于支持Hibernate等ORM工具)
  • Web :为创建Web应用程序提供支持。
  • Test : 提供了对 JUnit 和 TestNG 测试的支持。


详细讲解一下核心容器模块

核心容器是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。

Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。


谈谈自己对于 Spring IoC 和 AOP 的理解

控制反转:IOC

IoC(Inverse of Control:控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理,大大增加了项目的可维护性且降低了开发难度。 IoC 在其他语言中也有应用,并非 Spring 特有。

IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象(在xml里配置的bean节点、@Controller、@Component),在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里,或扫到上述注解的类,通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入( @Autowired、@Resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这注解,根据类型或id(对象名)注入;

依赖注入(Dependency Injection)即组件之间的依赖关系由Ioc容器管理,由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。


面向切面编程:AOP

AOP(Aspect-Oriented Programming:面向切面编程):将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情。

系统是由许多不同的组件所组成的,每个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。AOP能够将这些与核心功能无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,在需要用到的地方直接使用,这样减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP是基于动态代理实现的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理。

Spring AOP 和 AspectJ AOP 有什么区别?
Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。
Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。


Spring 常用注解

将一个类声明为Spring的 bean :

  • @Controller:用来标注控制层组件,通过Controller注解标记的类就是一个SpringMVC对象,分发处理器会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping注解,可以把Request请求header部分的值绑定到方法的参数上。
  • @RestController:相当于Controller注解与ResponseBody的组合效果
  • @Component:通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用
  • @Reponsitory:用来标注Dao层,在daoImpl类上注释
  • @Service:用来标注业务层

其他常用注解:

  • @ResponseBody:异步请求,该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入Response对象的body区,返回的值不是html页面,而是其他某种格式的数据时使用。
  • @RequestMapping:一个用来处理请求地址映射的注解,可以作用与类上或方法上,作用于类上标表示类的所有响应请求的方法都是以该地址作为父路径。
  • @Autowired:可以对类成员变量、方法以及构造函数进行标注,完成自动装配的工作。通过该注解来消除get、set方法。
  • @PathVariable:用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出url中的变量作为参数
  • @RequestParam:主要用于在SpringMVC控制层获取参数,类似于request.getParameter(“name”);
  • @ModelAttribute:该Controller的所有方法在调用前,限制性此方法,可用于注解和方法参数中,可以把这个ModelAttribute特性,应用在BaseController中,所有的Controller继承BaseCotroller,即可实现在调用Controller时,先执行@ModelAttribute方法。

@RestController与@Controller的区别是什么?

Controller 返回一个页面:单独使用 @Controller 不加 @ResponseBody的话一般使用在要返回一个视图的情况,这种情况属于比较传统的Spring MVC 的应用,对应于前后端不分离的情况。

@RestController (等价于@Controller +@ResponseBody)返回JSON 或 XML 形式数据:@RestController只返回对象,对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是目前日常开发所接触的最常用的情况(前后端分离)。

@ResponseBody 注解的作用是将 Controller 的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到HTTP 响应(Response)对象的 body 中,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。


@RequestMapping注解的属性

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

RequestMapping注解有六个属性:

  • value: 指定请求的实际地址,指定的地址可以是URI Template 模式;
  • method: 指定请求的method类型, GET、POST、PUT、DELETE等;
  • consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json,text/html;
  • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
  • params: 指定request中必须包含某些参数值是,才让该方法处理。
  • headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求

@Component 和 @Bean 的区别是什么?

  • 作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法。
  • @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。
  • @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。
@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

//上面的代码相当于下面的 xml 配置
<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

@PathVariable和@RequestParam的区别

  • 请求路径上有个id的变量值,可以通过@PathVariable来获取 @RequestMapping(value =“/page/{id}”, method = RequestMethod.GET)
  • @RequestParam用来获得静态的URL请求入参 spring注解时action里用到。

@Autowired和@Resource之间的区别

  • @Autowired可用于:构造函数、成员变量、Setter方法,@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

  • @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。


Bean Factory和 Application Context有什么区别?

Applicationcontext是 Beanfactory的子接口,ApplicationContext提供了更完整的功能:

  • ①继承 Messagesource,因此支持国际化
  • ②统一的资源文件访问方式。
  • ③提供在监听器中注册bean的事件。
  • ④同时加载多个配置文件。
  • ⑤载入多个(有继承关系)上下文,使得每个上下文都专注于一个特定的层次,比如应用的web层。

加载方式:

  • Bean Factroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时调用 getbean(),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的 Spring的配置问题。如果Bean的某一个属性没有注入, Beanfacotry加载后,直至第一次使用调用 getbean方法才会抛出异常。
  • Applicationcontext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现 spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 Applicationcontext启动后预载入所有的单实例Bean,通过预载入单实例bean,确保当你需要的时候,不需要等待,直接使用。

创建方式:
Bean Factory通常以编程的方式被创建, Applicationcontext还能以声明的方式创建,如使用ContextLoader。

注册方式:
Bean Factory和 Application Context,都支持 Bean Postprocessor、 Beanfactory Postprocessor的使用,但两者之间的区别是: Beanfactory需要手动注册,而 Applicationcontext则是自动注册。

相对于基本的 Beanfactory, Applicationcontext唯的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。


ApplicationContext通常的实现是什么?

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

Spring 中的 bean 生命周期?

解析类-》加载对象-》依赖注入-》回调Aware方法-》后置处理器处理(初始化及初始化前后)-》使用-》销毁

  • Bean 容器解析类得到配置文件中 Spring Bean 的定义(BeanDefinition)。(解析)
  • Bean 容器利用 Java Reflection API 创建一个Bean的实例。(加载对象)
  • 如果涉及到一些属性值(加了@Autowride注解的属性)利用 set()方法给对象设置属性。(依赖注入)
  • 回调Aware方法:如果实现了其他 *.Aware接口,就调用相应的方法。(回调Aware方法)
    ①如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。
    ②如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
  • 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor对象,执行postProcessBeforeInitialization() 方法。(后置处理器初始化前方法)
  • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。(初始化方法)
  • 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
  • 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。(后置处理器初始化后方法,会进行AOP)
  • 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

图示:

后置处理器BeanPostProcessor :在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。后置处理器分为 Bean Factory后置处理器(视线了Spring的扫描)和Bean后置处理器(实现了@ Autowired注解的属性自动赋值、AOP)

bean 标签有两个重要的属性(init-method和destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解


Spring 中的 bean 的作用域有哪些?

  • singleton(单例模式,默认) : 每个容器中只有一个bean的实例,单例的模式由 Beanfactory自身来维护。该对象的生命周期是与 Spring IOC容器一致的(但在第一次被注入时才会创建)。
  • prototype (原型模式): 每次请求都会创建一个新的 bean 实例。在每次注入时都会创建个新的对象
  • request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
  • session:每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
  • application:bean被定义为在 Servletcontext的生命周期中复用一个单例对象(可以实现跨容器)
  • global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。

Spring 中的单例 bean 线程安全吗?

存在安全问题的。Spring中的bean默认是单例模式的,框架并没有对bean进行多线程的封装处理,当多个线程操作同一个对象的时候,对这个对象的成员变量的写操作会存在线程安全问题。

但是大多数bean都是无状态的(不具有数据存储功能),比如说controller、 service和dao层,我们一般只是调用里面的方法,多线程调用一个实例的方法,会在内存中复制变量,这是自己的线程的工作内存,是安全的。但是dao层会操作数据库 Connection, Connection是带有状态的,比如说数据库事务, Spring的事务管理器,需要我们进行处理。

因此,不要在bean中声明任何有状态的实例变量或类变量,如果必须如此,可以参考一下方案:

  • 使用 Threadloca把变量变为线程私有的,比如在dao层,可以使用Threadlocal为不同线程维护了一套独立的 connection副本,保证线程之间不会互相影响。
  • 改变 Bean 的作用域为 “prototype”:每次请求都会创建一个新的 bean 实例,自然不会存在线程安全问题。
  • 如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用 synchronized、OCK、CAS等这些实现线程同步的方法了。

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

  • 工厂设计模式: 由一个工厂类根据传入的参数,动态决定创建那个产品类。Spring使用工厂模式通过 BeanFactory、ApplicationContext 创建bean 对象。
  • 单例设计模式 : 保证一个类仅有一个实例,并提供一个访问它的全局访问点。Spring 中的 Bean 默认都是单例的。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式(HandlerAdapter)适配Controller,对应的每一种Controller都有一种对应的适配器实现类。
  • 代理设计模式 : Spring AOP 功能的实现。AOP把切面应用到对象并创建新的代理对象。
  • 观察者模式:Spring的事件驱动模型使用的是观察着模式,Spring中Observer模式常用的地方是Listener的实现
  • 策略模式:Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring框架本身大量使用了Resource的接口来访问底层资源。
  • 装饰器模式:动态地给一个对象添加额外的职责。Spring中类名包括Wrapper或Decorator使用了装饰器模式。
  • 模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。

Spring事务的实现方式

Spring 管理事务的方式有几种?

  • 编程式事务,在代码中硬编码。(不推荐使用)
  • 声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为:

  • 基于XML的声明式事务
  • 基于注解的声明式事务

说一下 Spring的事务机制

  • spring事务底层是基于数据库事务和AOP机制的(事务这个概念是数据库层面的, Spring只是基于数据库中的事务进行了扩展)
  • 首先对于使用了@Transactional注解的bean,Spring会创建一个代理对象作为Bean
  • 当调用代理对象的方法时,会先判断该方法上是否加了@ Transactional注解
  • 如果加了,那么则利用事务管理器创建一个数据库连接,并把事务的自动提交设置为 false,然后再去执行原本的业务逻辑方法(sql)。
  • 如果执行业务逻辑方法没有出现异常,那么代理逻辑中就会将事务进行提交
  • 如果执行业务逻辑方法出现了异常,那么则会将事务进行回滚
  • 针对哪些异常回滚事务是可以配置的,可以利用@Transactional注解中的 rollbackfor属性进行配置,默认情况下会对 Runtimeexception和Error进行回滚。
  • Spring事务的隔离级别对应的就是数据库的隔离级别
  • Spring事务的传播机制是 Spring事务自己实现的,也是 Spring事务中最复杂的
  • Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开个事务,那么实际上就是先建立一个数据库连接,在此新数据库连接上执行sql

@Transactional注解了解吗?

Exception分为运行时异常RuntimeException和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

当@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

@Transactional(rollbackFor = Exception.class):
在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事务在遇到非运行时异常时也回滚。


spring事务什么时候会失效?

spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用了。常见情况有如下几种:@Transactional标识的方法只有被代理对象调用时,注解才会生效

  • 发生自调用,类里面使用this调用本类的方法,此时这个this对象不是代理类,而是Userservice对象本身(解决方法很简单,让那个this变成 Euserservice的代理类即可)
  • 方法不是public的,Transactional只能用于public的方法上,否则事务不会失效,如果要用在非public方法上,可以开启( Aspectj代理模式)。
  • 异常被吃掉,事务不会回滚或者抛出的异常没有被定义,默认为 RuntimeException。

Spring 事务中的隔离级别有哪几种?

spring事务隔离级别就是数据库的隔离级别:外加一个默认级别

  • isolation_default(默认级别):使用后端数据库默认的隔离级别
  • read_uncommitted(读未提交读):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • read_committed(读以提交、不可重复读):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • repeatable_read(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • serializable(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

数据库的配置隔离级别是 Read Commited,而 Spring配置的隔离级别是 Repeatable Read,请问这时隔离级别是以哪个为准?
以 Spring配置的为准,如果 spring设置的隔离级别数据库不支持,效果取决于数据库


Spring 事务中哪几种事务传播行为?

多个事务方法相互调用时事务如何在这些方法间传播:
方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。

  • REQUIRED(Spring默认的事务传播类型):如果当前没有事务,则自己新建一个事务,如果当前存在事务,则加入这个事务
  • SUPPORTS:当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
  • MANDATORY:当前存在事务,则加入当前事务,如果当前事务不存在,则拋出异常
  • REQUIRES_NEW:创建一个新事务,如果存在当前事务,则挂起该事务
  • NOT_SUPPORTED:以非事务方式执行如果当前存在事务,则挂起当前事务
  • NEVER:不使用事务,如果当前事务存在,则抛出异常
  • NESTED:如果当前事务存在,则在嵌套事务中执行,否则 REQUIRED的操作一样(开启一个事务)

NESTED和 REQUIRES_NEW的区别:
REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而 NESTED则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。在 NESTED情况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW情况下,原有事务回滚,不会影响新开启的事务

NESTED和 REQUIRED的区别:
REQUIRED情况下,调用方存在事务时,则被调用方和调用方使用同一事务,那么被调用方出现异常时,由于共用一个事务,所以无论调用方是否 catch其异常,事务都会回滚而在 NESTED情况下,被调用方发生异常时,调用方可以 catch其异常,这样只有子事务回滚,父事务不受影响。


什么是bean的自动装配?自动装配有哪些方式?

在Spring框架中,在配置文件中设定了bean的依赖关系,Spring 容器能够自动装配相互合作的bean。这意味着容器不需要配置,能通过Bean工厂自动处理bean之间的协作,Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。

使用@Autowired注解自动装配的过程:
使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:

  • 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
  • 如果查询的结果不止一个,那么@Autowired会根据名称来查找;
  • 如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用Autowire来配置自动装载模式。在Spring框架xml配置中共有5种自动装配:

  • no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
  • byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
  • byType:通过参数的数据类型进行自动装配。
  • constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
  • autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。
<!--byName-->
<bean id="cutomer" class="com.XXX.XXX.Cutomer" autowire="byName"/>
<bean id="person" class="com.XXX.XXX.Person"/>

<!--byName-->
<bean id="cutomer" class="com.XXX.XXX.Cutomer" autowire="byType"/>
<bean id="person" class="com.XXX.XXX.Person"/>

<!--construtor-->
<bean id="cutomer" class="com.XXX.XXX.Cutomer" autowire="construtor"/>
<bean id="person" class="com.XXX.XXX.Person"/>

自动装配有哪些局限性?

  • 重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。
  • 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String字符串,和类。
  • 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

Spring MVC的工作流程了解吗?

MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。Spring MVC 可以帮助我们进行更简洁的Web层的开发,并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

SpringMVC工作流程:

  • 用户发送请求至前端控制器 Dispatcherservlet(用来处理HTTP请求与响应)
  • Dispatcherservlet 收到请求调用 Handler Mapping处理器映射器。
  • 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器及处理器拦截器(如果有则生成)并返回给 Dispatcherservlet。
  • Dispatcherservlet调用 HandlerAdapter处理器适配器
  • HandleraAapter经过适配调用具体的处理器(Controller,也叫后端控制器)
  • Controller执行完成返回 ModelAndView。
  • HandlerAdapter将 controller执行结果 ModelAndView返回给 Dispatcherservlet。
  • Dispatcherservlet将 Modelandview传给 ViewReslover视图解析器。
  • Viewreslover解析后返回具体View
  • Dispatcherservlet根据view进行渲染视图(即将模型数据填充至视图中)。
  • Dispatcherservle响应用户(浏览器)。

Spring MVC 的简单原理图如下:


Spring MVC的主要组件?

  • 前端控制器 DispatcherServlet:用于接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。
  • 处理器映射器HandlerMapping:根据请求的URL来查找Handler,SpringMVC中会有许多请求,每个请求都需要一个Handler处理,处理器映射根据请求决定使用那个Handler。
  • 处理器适配器HandlerAdapter:Handler可以是任何形式的,但是Servlet的处理方式是固定(request+response),这时需要通过处理器适配器来进行适配,调用具体的处理器,返回ModelAndView。
  • 后端控制器(处理器)(Handler):主要负责处理前端请求,完成业务逻辑,生成ModelAndView对象返回给HandlerAdapter。
  • 视图解析器(ViewResolver):主要负责解析ModelAndView对象,生成View对象返回给前端控制器。
  • 视图View:View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等)

参考文章:
https://gitee.com/SnailClimb/JavaGuide#/SnailClimb/JavaGuide
https://www.bilibili.com/video/BV1Z44y1q7wf

以上是关于Java面试题超详细整理《Spring篇》的主要内容,如果未能解决你的问题,请参考以下文章

Java面试题超详细整理《Spring篇》

Java面试题超详细整理《Redis篇》

Java面试题超详细整理《Redis篇》

Java面试题超详细整理《Redis篇》

Java面试题超详细整理《Redis篇》

Java面试题超详细整理《JVM篇》