Spring常见面试题汇总(超详细回答)

1.什么是Spring框架?

Spring框架是一个开源的Java应用程序开发框架,它提供了很多工具和功能,可以帮助开发者更快地构建企业级应用程序。通过使用Spring框架,开发者可以更加轻松地开发Java应用程序,并且可以更加灵活地组织和管理应用程序中的对象和组件。

Spring框架的核心思想是依赖注入(DI)和面向切面编程(AOP)。依赖注入(DI)可以帮助我们更好地组织和管理应用程序中的对象,使得应用程序更加松耦合,易于扩展和维护。面向切面编程(AOP)可以帮助我们更好地管理应用程序中的横切关注点,比如日志、事务、安全等,使得应用程序更加模块化和可维护。

除了核心容器以外,Spring框架还提供了很多其他的组件和框架,比如Spring MVC可以帮助我们更加轻松地开发Web应用程序,Spring JDBC可以帮助我们更加轻松地访问和操作数据库,Spring ORM可以帮助我们更加轻松地使用ORM框架等等。

总的来说,Spring框架可以帮助我们更加轻松地开发Java应用程序,提高开发效率,减少代码冗余和重复,使得应用程序更加灵活和易于维护。

spring官网地址:https://spring.io/

2.Spring包含哪些模块

Spring框架包含了很多模块,以下是一些重要的Spring模块:

  1. Spring Core:Spring框架的核心模块,提供了依赖注入(DI)和控制反转(IoC)的功能,用于管理对象之间的依赖关系。

  1. Spring MVC:基于MVC(Model-View-Controller)模式的Web框架,提供了用于处理请求、响应以及Web应用程序其他方面的API。

  1. Spring Data:Spring框架的数据访问层模块,提供了通用的数据访问抽象层和一些特定数据访问技术的实现,如JPA、Hibernate等。

  1. Spring Security:Spring框架的安全模块,提供了基于Spring框架的认证和授权功能,可以保护Web应用程序免受各种攻击。

  1. Spring Integration:Spring框架的集成模块,提供了一些集成技术的实现,如消息队列、Web服务、FTP等。

  1. Spring Batch:Spring框架的批处理模块,提供了处理大规模数据批处理的功能,包括事务处理、并发处理、失败处理等。

  1. Spring Cloud:Spring框架的云原生模块,提供了在云环境下构建微服务应用程序所需的一些基础设施和工具,如服务注册、配置中心、断路器等。

除了以上列举的模块,Spring框架还包含了很多其他的模块,比如Spring WebFlux、Spring Test、Spring WebSocket等,每个模块都提供了一些特定的功能和工具,可以帮助开发者更加方便地开发Java应用程序。

3.什么是IOC? 如何实现的?

IoC(Inversion of Control)是一种设计模式,它将对象之间的依赖关系的管理交给框架来处理,从而实现对象之间的松耦合和可维护性。

传统的编程模式中,对象之间的依赖关系是由对象自己来管理的,当对象A需要对象B的协助完成某个功能时,对象A会主动创建对象B,然后在适当的时候调用对象B的方法。这种方式会导致对象之间的依赖关系紧密耦合,使得代码难以维护和扩展。

在Spring框架中,实现IoC的方式是通过依赖注入(Dependency Injection,DI)来实现的。DI有三种实现方式:

  1. 构造函数注入(Constructor Injection):通过构造函数来注入依赖对象。

  1. 属性注入(Setter Injection):通过setter方法来注入依赖对象。

  1. 接口注入(Interface Injection):通过接口方法来注入依赖对象。

当我们定义一个Bean时,可以通过注解或XML配置文件来定义Bean之间的依赖关系,然后由容器来动态注入依赖对象,使得对象之间的依赖关系更加松散,易于维护和扩展。

具体实现过程如下:

  1. 定义Bean:通过注解或XML配置文件来定义Bean,并在Bean的构造函数、属性或方法上使用特定的注解,表明Bean所依赖的其他Bean。

  1. 加载配置文件:启动应用程序时,IoC容器会读取并解析配置文件,将其中的Bean定义加载到容器中。

  1. 创建Bean:当需要使用某个Bean时,IoC容器会根据配置文件中的定义,创建该Bean的实例。

  1. 注入依赖:当IoC容器创建一个Bean时,它会检查该Bean所依赖的其他Bean是否已经创建,如果已经创建,则会将依赖的Bean注入到该Bean中,否则会等待所依赖的Bean创建完成后再注入依赖。

  1. 返回Bean:当IoC容器完成Bean的创建和依赖注入后,将该Bean返回给调用方使用。

实现IoC的原理是通过控制反转(Inversion of Control,IoC)来实现的。

  • 控制 :指的是对象创建(实例化、管理)的权力

  • 反转 :控制权交给外部环境(Spring 框架、IoC 容器)

Spring常见面试题汇总(超详细回答)_第1张图片

传统的编程模式中,对象之间的依赖关系是由对象自己来管理的,当一个对象需要使用另一个对象时,需要自己创建并管理另一个对象。而IoC的思想是将对象之间的依赖关系的管理交给框架来处理,对象只需要定义它们所需的依赖关系,框架会在运行时动态地注入依赖对象,从而实现对象之间的松耦合和可维护性。因此,实现IoC的核心思想是将对象的创建和对象之间的依赖关系的管理交给框架来处理。

4.什么是AOP? 有哪些AOP的概念?

AOP(Aspect-Oriented Programming)是一种编程思想,它通过将应用程序分解成多个切面,来实现对应用程序进行横向切割的目的,从而实现代码的复用和系统的解耦。

AOP的核心思想是将与业务逻辑无关的代码,如日志、事务、异常处理等,从业务逻辑代码中分离出来,形成独立的模块,以便于复用和维护。

AOP的一些概念如下:

  1. 切面(Aspect):切面是一个模块化的横切关注点,它通过对某个点进行拦截,来实现对目标对象的增强。

  1. 连接点(Join Point):连接点是在应用程序执行过程中能够插入切面的点,例如方法调用或异常处理等。

  1. 切入点(Pointcut):切入点是一组连接点的集合,它定义了在哪些连接点处应用切面。

  1. 通知(Advice):通知是切面在连接点上执行的操作,例如在方法调用前、方法调用后、方法返回时或方法抛出异常时执行的操作。

  1. 织入(Weaving):织入是将切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、类加载时或运行时进行。

  1. 目标对象(Target Object):目标对象是应用程序中需要被增强的对象。

  1. 代理对象(Proxy Object):代理对象是一个中间件对象,它拦截对目标对象的访问并调用切面提供的通知。

通过AOP的概念,我们可以将应用程序按照不同的横向关注点进行切割,并将与业务逻辑无关的代码分离出来,从而提高应用程序的可维护性和可扩展性。常见的AOP框架有Spring AOP和AspectJ。

5.AOP 有哪些应用场景?

AOP(Aspect-Oriented Programming)可以在许多场景下被应用。以下是一些AOP的应用场景:

  1. 日志记录:在方法调用前后记录日志,用于排查错误和分析系统性能。

  1. 安全性控制:控制用户对系统中某些资源或方法的访问权限,确保系统安全。

  1. 事务管理:在方法调用前后开启、提交或回滚事务,确保数据的一致性和完整性。

  1. 缓存管理:通过在方法调用前检查缓存中是否有目标对象的缓存结果,减少系统的响应时间和网络开销。

  1. 性能监控:记录方法调用的时间、次数和异常等信息,以便于分析系统性能。

  1. 异常处理:在方法调用过程中捕获异常并记录或通知开发人员,以便于及时处理异常情况。

  1. 数据验证:在方法调用前对输入参数进行验证,确保输入数据的正确性和合法性。

  1. 代码复用:将与业务逻辑无关的代码抽离出来,以便于复用和维护。

6.AOP Advice通知的类型?

在AOP(Aspect-Oriented Programming)中,通知(Advice)是在切面(Aspect)中定义的一些方法,用于在连接点(Join Point)处执行特定的操作。通知可以分为以下几种类型:

  1. 前置通知(Before Advice):在连接点之前执行的通知,例如在方法调用前记录日志或开启事务等。

  1. 后置通知(After Advice):在连接点之后执行的通知,例如在方法调用后记录日志或提交事务等。

  1. 返回通知(After Returning Advice):在方法返回结果之后执行的通知,例如在方法调用后记录返回结果或关闭资源等。

  1. 异常通知(After Throwing Advice):在方法抛出异常时执行的通知,例如在方法调用抛出异常时记录异常信息或回滚事务等。

  1. 环绕通知(Around Advice):在方法调用之前和之后都可以执行的通知,它可以自由控制方法调用前后的逻辑,例如在方法调用前记录日志或在方法调用后提交事务等。

以上通知类型都是用于在连接点处执行特定的操作,并通过切面将这些通知应用到目标对象中。这些通知类型可以根据实际业务需求进行组合,从而实现对目标对象的增强,提高应用程序的可维护性和可扩展性。

7.AOP 有哪些实现方式?

  1. 基于静态代理的AOP实现

基于静态代理的AOP实现,是通过手动编写切面类来实现的。在这种实现方式下,需要为每一个目标对象编写一个对应的切面类,并在切面类中实现通知方法。在使用目标对象时,需要将目标对象和切面类进行组合,生成一个新的代理对象,该代理对象拦截目标对象的方法调用,并在方法调用前后执行通知。

具体实现步骤如下:

1)定义切面类,实现通知方法

切面类是实现AOP的关键,其中包含了通知方法和目标对象引用。通知方法用于在目标对象的方法调用前后执行通用逻辑。通知方法的类型有前置通知、后置通知、环绕通知、异常通知和最终通知等。

2)定义目标对象

目标对象是需要被代理的对象,通常是业务代码实现类。在使用目标对象时,需要将目标对象和切面类进行组合,生成一个新的代理对象。

3)组合目标对象和切面类,生成代理对象

生成代理对象时,可以手动编写代码,也可以使用Spring框架提供的AOP实现。在Spring中,可以使用XML配置文件或注解方式进行AOP的实现。

  1. 基于动态代理的AOP实现

基于动态代理的AOP实现,是通过Java的动态代理机制来实现的。在这种实现方式下,通过定义一个切面类和切面方法,然后使用JDK动态代理或CGLIB动态代理生成一个代理对象。在代理对象中,所有方法调用都会被拦截,并在方法调用前后执行通知。

具体实现步骤如下:

1)定义切面类,实现通知方法

切面类是实现AOP的关键,其中包含了通知方法和目标对象引用。通知方法用于在目标对象的方法调用前后执行通用逻辑。通知方法的类型有前置通知、后置通知、环绕通知、异常通知和最终通知等。

2)使用JDK动态代理或CGLIB动态代理生成代理对象

在使用JDK动态代理时,需要实现InvocationHandler接口,并重写invoke方法。在invoke方法中,需要对目标对象进行方法调用,并在方法调用前后执行通知。使用Proxy类的newProxyInstance方法,将目标对象和InvocationHandler对象进行组合,生成代理对象。

在使用CGLIB动态代理时,需要使用Enhancer类和MethodInterceptor接口。Enhancer类用于生成代理对象,MethodInterceptor接口用于在代理对象的方法调用前后执行通知。在使用Enhancer类时,需要设置目标对象和MethodInterceptor对象,并调用create方法生成代理对象。

总体来说,基于动态代理的AOP实现比基于静态代理的AOP实现更灵活,可以在运行时动态地生成代理对象,并且不需要为每一个目标对象编写对应的切面类。不过,基于动态代理的AOP实现需要对目标对象进行代理,因此目标对象必须实现一个接口或是继承一个类。此外,使用CGLIB动态代理时,代理对象的生成过程可能会比较耗时。

8.谈谈你对CGLib的理解?

CGLib是一个强大的、高性能的代码生成库,用于在运行时扩展Java类和实现Java接口。与JDK动态代理不同,它不需要目标类实现接口,而是通过生成目标类的子类并重写其方法来实现代理。这使得CGLib代理更灵活,因为它可以代理任何非final类,包括没有实现接口的类。

CGLib通过使用字节码生成库ASM来动态生成字节码,它能够将生成的代理类的字节码加载到JVM中,并使用它来创建代理对象。CGLib通过生成的字节码来扩展目标类的行为,它可以在目标类的方法执行前后插入代理逻辑,实现方法级别的拦截,从而实现AOP的功能。

CGLib代理相对于JDK动态代理的优点是,它不需要代理类实现接口,而且生成的代理类比JDK动态代理更快。但是,它的缺点是生成的代理类较大,且它不支持对final方法和final类进行代理。

举个栗子:

假设我们有一个Person类,它有一个sayHello方法:

public class Person {
    public void sayHello() {
        System.out.println("Hello, I am a person.");
    }
}

现在我们想要在调用sayHello方法前后输出日志,可以通过CGLib实现代理。

首先,我们需要定义一个MethodInterceptor接口的实现类,用于在代理对象的方法调用前后执行一些逻辑:

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class LogInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before invoking " + method.getName() + "...");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After invoking " + method.getName() + "...");
        return result;
    }
}

在这个intercept方法中,我们在方法调用前输出了一条日志,然后调用了proxy.invokeSuper(obj, args)方法来调用目标对象的原始方法,最后在方法调用后输出了另一条日志。

接下来,我们使用CGLib动态代理来生成代理对象:

import net.sf.cglib.proxy.Enhancer;

public class CGLibDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        enhancer.setCallback(new LogInterceptor());
        Person proxy = (Person) enhancer.create();
        proxy.sayHello();
    }
}

在这个例子中,我们首先创建了一个Enhancer对象,并设置了要代理的目标类和MethodInterceptor回调。然后使用Enhancer的create方法生成代理对象,最后调用代理对象的sayHello方法。当我们运行这个程序时,会输出以下结果:

Before invoking sayHello...
Hello, I am a person.
After invoking sayHello...

可以看到,我们成功地使用CGLib实现了对Person类的代理,并在方法调用前后输出了日志。

9.Spring AOP和AspectJ AOP有什么区别?

  1. 实现方式:

Spring AOP是基于动态代理实现的。Spring AOP通过代理对象实现对目标对象的增强,具体的增强代码放在通知(Advice)中,代理对象会在目标对象执行方法时,调用通知中的增强代码。Spring AOP支持两种类型的代理:JDK动态代理和CGLib动态代理。

JDK动态代理:基于接口的代理,只能对实现了接口的类进行代理。

CGLib动态代理:基于类的代理,可以代理没有实现接口的类。CGLib动态代理通过修改字节码来生成代理类,相比于JDK动态代理,它的效率更高,但是也存在一些限制,比如无法代理final方法。

AspectJ AOP是基于编译时和运行时织入实现的。AspectJ AOP通过在编译时或者运行时修改字节码来实现对目标对象的增强,具体的增强代码放在切面(Aspect)中。切面由切点(Pointcut)、通知(Advice)和切点表达式(Pointcut Expression)组成。

切点:指定需要增强的连接点,可以使用切点表达式来指定连接点。

通知:指定增强的具体操作,包括前置通知、后置通知、返回通知、异常通知和环绕通知。

切点表达式:用来指定连接点的表达式,可以使用AspectJ提供的表达式语言。

  1. 使用场景:

Spring AOP适用于轻量级的应用场景,比如Web应用。它主要用于实现跨越层(Cross-Cutting Concerns)的功能,比如事务管理、安全控制、日志记录等。Spring AOP的语法简单,易于使用和配置,但是功能相对简单,不适用于复杂的应用场景。

AspectJ AOP适用于复杂的应用场景,比如企业级应用。它可以实现更加复杂和灵活的切面,比如实现缓存、验证、权限控制等功能。AspectJ AOP提供了比Spring AOP更加强大和灵活的语法,但是也更加复杂和难以配置,需要有一定的编程和调试经验。

总的来说,Spring AOP和AspectJ AOP都是AOP框架,各有优缺点,根据实际需求来选择。如果应用场景比较简单,可以选择Spring AOP,如果应用场景比较复杂,需要实现更加灵活和复杂的切面,可以选择AspectJ AOP。

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

在Spring中,Bean的作用域(Scope)指定了在容器中创建的Bean实例的生命周期,不同的作用域决定了Bean实例的可见范围和生命周期长度。Spring框架支持以下五种Bean作用域:

  1. singleton(单例):在整个应用中只创建一个Bean实例,并在容器启动时就创建,以后每次请求都返回同一个实例。

  1. prototype(原型):每次请求都会创建一个新的Bean实例,适用于一些状态不可共享的Bean。

  1. request(请求):在每个HTTP请求中创建一个Bean实例,该Bean实例仅在当前请求中有效,对于不同的请求,会创建不同的Bean实例。

  1. session(会话):在每个HTTP Session中创建一个Bean实例,该Bean实例仅在当前Session中有效,对于不同的Session,会创建不同的Bean实例。

  1. global-session(全局会话):在基于portlet的web应用中使用,该作用域仅在基于portlet的web应用中有效,一个portlet的多个请求共享一个Bean实例。

其中,单例是默认的作用域,当没有指定作用域时,Spring会默认将Bean作为单例。除了全局会话作用域,其他四种作用域都只适用于Web应用。在Spring中,可以通过在Bean定义中指定scope属性来指定Bean的作用域,如下所示:

在注解中,可以使用@Scope注解来指定Bean的作用域,如下所示:

@Component
@Scope("prototype")
public class ExampleBean {
    // ...
}

需要注意的是,在单例作用域中,如果Bean有状态(stateful),则需要考虑线程安全问题,否则可能会导致多线程并发访问出现问题。因此,对于有状态的Bean,建议使用原型作用域。

11.Spring中的单例bean的线程安全问题?

在Spring中,单例Bean的线程安全问题是需要考虑的。由于单例Bean在容器启动时就会被创建,因此所有的请求都会共享同一个Bean实例。如果Bean有状态(stateful),即Bean的属性会随着请求的处理而改变,那么在多线程并发访问的情况下,就需要考虑线程安全问题。

在单例Bean中,如果有多个线程同时访问Bean实例并且Bean实例中有共享的数据,那么就可能出现数据不一致的情况,这是因为多个线程会竞争共享数据的访问权,导致数据出现冲突。为了解决这个问题,需要在多线程环境下保证Bean实例的线程安全。

有多种方式可以保证Spring中的单例Bean的线程安全:

1.在bean对象中尽量避免定义可变的成员变量(不太现实)。

2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal中(推荐的一种方式)。

需要注意的是,Spring框架本身并不提供对单例Bean的线程安全保证,因此需要在编写代码时自行考虑和实现线程安全的措施。

12.如何定义bean的范围?

在Spring中,可以使用@Scope注解来定义bean的作用域范围,常见的作用域包括:

  • singleton:单例模式,一个容器中只有一个实例,是Spring默认的作用域。

  • prototype:原型模式,每次获取bean时都会创建一个新的实例。

  • request:每个HTTP请求都会创建一个新的bean实例,该作用域仅适用于Web应用程序上下文中。

  • session:每个HTTP会话都会创建一个新的bean实例,该作用域仅适用于Web应用程序上下文中。

  • global session:全局HTTP会话作用域,仅在使用基于portlet的Web应用程序时才适用。

例如,定义一个prototype作用域的bean可以这样写:

@Component
@Scope("prototype")
public class MyBean {
    // ...
}

13.Spring中的bean生命周期?

  1. 实例化Bean:

Spring容器实例化Bean的过程是通过BeanFactory接口的实现类DefaultListableBeanFactory来完成的。在DefaultListableBeanFactory中,具体的实例化过程由AbstractAutowireCapableBeanFactory类的createBean()方法实现。该方法首先会调用doCreateBean()方法,创建Bean的实例,并进行依赖注入;然后调用populateBean()方法,为Bean设置对象属性;最后调用initializeBean()方法,对Bean进行初始化操作。

  1. 设置对象属性:

依赖注入的实现是通过DefaultListableBeanFactory类的autowireBean()方法完成的。该方法会根据Bean的定义,自动注入对应的依赖项。具体的注入方式由AbstractAutowireCapableBeanFactory类的populateBean()方法实现。在该方法中,会依次调用BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法,完成前置处理和后置处理的逻辑。

  1. BeanPostProcessor的前置处理:

BeanPostProcessor的前置处理是通过BeanPostProcessor接口的实现类来完成的。Spring容器在创建Bean实例后,会扫描所有的BeanPostProcessor,并将其注册到容器中。在Bean实例化和依赖注入完成后,Spring容器会调用所有注册的BeanPostProcessor的postProcessBeforeInitialization()方法,执行前置处理的逻辑。具体的前置处理逻辑可以根据业务需求自定义实现。

  1. 初始化Bean:

Bean的初始化是通过AbstractAutowireCapableBeanFactory类的initializeBean()方法实现的。该方法会依次调用BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法,完成前置处理和后置处理的逻辑。然后调用invokeInitMethods()方法,执行Bean的初始化方法。

  1. BeanPostProcessor的后置处理:

BeanPostProcessor的后置处理是通过BeanPostProcessor接口的实现类来完成的。在Bean的初始化方法执行完成后,Spring容器会调用所有注册的BeanPostProcessor的postProcessAfterInitialization()方法,执行后置处理的逻辑。具体的后置处理逻辑可以根据业务需求自定义实现。

  1. 使用Bean:

使用Bean的过程是由应用程序完成的。应用程序可以通过Spring容器获取Bean的实例,并调用其方法来完成具体的业务逻辑。

  1. 销毁Bean:

Bean的销毁是通过AbstractAutowireCapableBeanFactory类的destroyBean()方法实现的。在容器关闭或调用destroy()方法时,Spring容器会调用Bean的销毁方法。具体的销毁逻辑可以根据业务需求自定义实现。在销毁方法执行完成后,Spring容器会将Bean实例从容器中移除。

Spring常见面试题汇总(超详细回答)_第2张图片

14.说说自己对于Spring MVC的了解?

Spring MVC是基于MVC(Model-View-Controller)设计模式实现的一种Web应用程序开发框架。它是Spring Framework的一个重要组成部分,提供了一个灵活、松耦合、可扩展、高效的Web开发框架。

在Spring MVC中,M代表模型(Model),即数据模型;V代表视图(View),即用户界面;C代表控制器(Controller)

Spring MVC下我们一般把后端项目分为Service层(处理业务)、Dao层(数据库操作)、Entity层(实体类)、Controller层(控制层,返回数据给前台页面)。

Spring常见面试题汇总(超详细回答)_第3张图片

15.说说Spring MVC的工作原理

Spring MVC的工作原理可以分为以下几个步骤:

  1. 客户端发送请求,请求被前端控制器DispatcherServlet截获。

  1. DispatcherServlet查询处理器映射HandlerMapping,根据请求URI找到对应的处理器Controller。

  1. HandlerAdapter将请求发送给处理器Controller进行处理,Controller根据请求参数处理业务逻辑,调用业务逻辑层的Service。

  1. Service处理业务逻辑,返回数据给Controller。

  1. Controller将数据封装为ModelAndView对象,其中Model表示数据模型,View表示视图名称。

  1. DispatcherServlet查询视图解析器ViewResolver,根据视图名称找到对应的视图View。

  1. View渲染模型数据,将视图返回给DispatcherServlet。

  1. DispatcherServlet将视图发送给客户端,完成请求响应。

Spring常见面试题汇总(超详细回答)_第4张图片

在这个过程中,Spring MVC的核心组件包括:

  1. DispatcherServlet:前端控制器,负责拦截所有请求并进行处理。

  1. HandlerMapping:处理器映射,用于将请求映射到对应的处理器。

  1. HandlerAdapter:处理器适配器,用于将请求发送给处理器进行处理。

  1. Controller:处理器,用于处理具体的业务逻辑。

  1. Service:业务逻辑层,负责处理具体的业务逻辑。

  1. Model:数据模型,用于封装业务逻辑返回的数据。

  1. View:视图,用于展示数据给用户。

  1. ViewResolver:视图解析器,用于将视图名称解析为实际的视图对象。

Spring MVC的工作流程是非常灵活的,可以根据实际需求进行定制和扩展。开发人员可以自定义处理器映射、处理器适配器、视图解析器等组件,以满足不同的业务场景需求。同时,Spring MVC也提供了很多现成的插件和组件,如国际化插件、文件上传插件等,可以大大提高开发效率。

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

Spring框架是一个非常重要的开源框架,它涉及到许多设计模式,以下是Spring框架中使用的一些设计模式:

  1. 单例模式:Spring框架中的bean默认是单例的,可以通过配置更改其作用域。单例模式保证了在整个应用程序中只有一个实例。

  1. 工厂模式:Spring框架中的BeanFactory是一个工厂模式的典型实现。BeanFactory负责实例化并管理应用程序中的对象。

  1. 代理模式:Spring框架中的AOP(面向切面编程)机制是通过代理模式来实现的。Spring中使用代理对象对目标对象进行包装,从而实现对目标对象的增强。

  1. 观察者模式:Spring框架中的事件驱动机制就是一个观察者模式的实现。事件源产生事件后,会通知已经注册的监听器进行处理。

  1. 模板方法模式:Spring框架中的JdbcTemplate是一个典型的模板方法模式的实现。JdbcTemplate定义了一系列操作数据库的基本方法,而具体的实现则由其子类完成。

  1. 适配器模式:Spring框架中的HandlerAdapter就是一个适配器模式的典型实现。HandlerAdapter负责将请求发送给处理器进行处理,从而使得不同类型的处理器可以被统一处理。

除了以上这些设计模式,Spring框架还使用了许多其他的设计模式,如策略模式、装饰器模式、命令模式等。这些设计模式的使用,使得Spring框架具有了更好的灵活性、可扩展性和可维护性。

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

  1. 作用范围不同

@Component注解是标注在类上的,用于将一个类标记为Spring容器中的一个组件,通常用来标注Service、Repository、Controller等组件。

例如,我们有一个Service类:

@Service
public class UserService {
    // ...
}

@Bean注解则是标注在方法上的,用于声明一个方法返回的对象应该被Spring容器管理。这个方法可以是在配置类中声明的一个方法,也可以是在任意类中声明的一个方法。

例如,我们有一个配置类:

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDaoImpl();
    }
}
  1. 作用不同

@Component注解是用于组件扫描和自动装配,它能够被Spring扫描并自动装配到其他类中。

例如,我们有一个Controller需要使用UserService:

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    // ...
}

@Bean注解是用于配置类中的bean定义,用于声明需要被Spring容器管理的对象。

例如,我们需要在配置类中创建一个DataSource对象:

@Configuration
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        return dataSource;
    }
}
  1. 生命周期不同

@Component注解标记的bean默认是单例模式,而且它的生命周期由Spring容器管理,也就是说,在Spring容器启动时,会创建一个单例对象,直到容器关闭时才会销毁。

@Bean注解标记的方法返回的bean默认也是单例模式,但是这个bean的生命周期和方法的调用有关。也就是说,每次调用这个方法时,都会返回同一个实例。如果需要创建多例的bean,则可以通过@Scope注解来设置其作用范围为“prototype”。

例如,我们在配置类中声明一个多例的bean:

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public Date currentDate() {
        return new Date();
    }
}

这样,在每次调用currentDate方法时,都会创建一个新的Date对象。

综上所述,@Component和@Bean的使用场景不同。@Component主要用于标注组件、服务层、控制器等组件,而@Bean主要用于在配置类中声明需要被Spring容器管理的对象。此外,它们的作用域也不同,@Component注解标记的bean默认是单例模式,而@Bean注解标记的方法返回的bean默认也是单例模式,但是可以通过设置@Scope注解来更改其作用范围。

18.将一个类声明为Spring的bean的注解有哪些?

在Spring框架中,可以使用多个注解将一个类声明为bean,其中一些常见的注解包括:

  1. @Component:是一个通用的注解,可以用于任何类,表明这个类会被Spring框架自动扫描,并注册为bean。

  1. @Service:通常用于声明服务层的bean。

  1. @Repository:通常用于声明数据访问层(DAO)的bean。

  1. @Controller:通常用于声明控制器层(Controller)的bean。

除此之外,还有一些其他的注解可以用于声明bean,例如:

  1. @Configuration:用于声明配置类,其中定义了多个@Bean方法,用于返回各种不同类型的bean。

  1. @Bean:用于声明一个方法,这个方法返回的对象将会被注册为bean。

  1. @Import:用于将其他的配置类导入到当前的配置类中,可以方便地组织各种不同的bean。

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

  1. 编程式事务管理(硬编码,不推荐使用)

编程式事务管理是通过在代码中显式地编写事务管理代码来实现事务管理。Spring框架提供了TransactionTemplate和TransactionManager两个核心类来支持编程式事务管理。

在使用TransactionTemplate时,我们需要先获取一个TransactionTemplate实例,然后使用execute()方法执行需要事务管理的代码块。例如:

@Service
public class UserService {
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void transferMoney(String fromAccount, String toAccount, double amount) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                // 执行转账操作
                // 如果出现异常,会自动回滚事务
            }
        });
    }
}

在使用TransactionManager时,我们需要先获取一个TransactionDefinition实例,然后使用getTransaction()方法获取一个事务,并在事务中执行需要事务管理的代码块。例如:

@Service
public class UserService {
    
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    public void transferMoney(String fromAccount, String toAccount, double amount) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 执行转账操作
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            throw e;
        }
    }
}
  1. 声明式事务管理

声明式事务管理是通过在Spring配置文件中声明事务管理器和事务属性来实现事务管理。Spring框架使用AOP技术将事务管理器和事务属性织入到目标方法中,从而实现事务管理。

在使用声明式事务管理时,我们需要先配置一个事务管理器和一些事务属性,例如事务的传播行为、隔离级别、超时时间等等。然后在需要事务管理的方法上添加@Transactional注解,表示该方法需要进行事务管理。例如:

@Service
@Transactional
public class UserService {
    
    @Autowired
    private UserDao userDao;
    
    public void transferMoney(String fromAccount, String toAccount, double amount) {
        // 执行转账操作
    }
}

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

在Spring事务中,隔离级别用来描述并发事务之间的关系,它规定了一个事务对于数据的读取能够具有的隔离程度。Spring框架中提供了五个隔离级别:

  1. TransactionDefinition.ISOLATION_DEFAULT:默认隔离级别,由底层的数据库驱动决定隔离级别,通常为数据库的默认隔离级别。

  1. TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但未提交的数据。该级别可以产生脏读、不可重复读和幻读的问题。

  1. TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以避免脏读问题,但不可重复读和幻读问题仍然可能发生。

  1. TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在执行过程中多次读取同一数据集时,其结果是一致的。该级别可以避免脏读和不可重复读问题,但仍然可能发生幻读问题。

  1. TransactionDefinition.ISOLATION_SERIALIZABLE:该隔离级别表示一个事务在执行过程中对于数据的修改会进行排队,即串行化执行,从而避免了脏读、不可重复读和幻读问题,但也降低了并发性能。

不同的隔离级别在解决并发事务问题时采用了不同的策略,为了保证应用程序数据的一致性和正确性,在选择隔离级别时需要根据具体的业务场景和需求进行选择。

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

在Spring事务中,事务的传播行为指的是在一个事务方法调用另一个事务方法时,另一个事务方法如何处理事务的行为。Spring事务支持以下七种传播行为:

  1. PROPAGATION_REQUIRED(默认值):如果当前存在事务,则在该事务中执行;否则,创建一个新的事务并在其中执行方法。

  1. PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

  1. PROPAGATION_MANDATORY:强制执行当前事务,如果当前没有事务,则抛出异常。

  1. PROPAGATION_REQUIRES_NEW:创建新的事务,并在新事务中执行方法,如果当前存在事务,则暂停当前事务。

  1. PROPAGATION_NOT_SUPPORTED:以非事务方式执行方法,如果当前存在事务,则暂停当前事务。

  1. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

  1. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务中执行;否则,执行与PROPAGATION_REQUIRED相同的操作。嵌套事务是当前事务的子事务,并与当前事务共享一部分数据源连接。如果嵌套事务失败,则仅回滚嵌套事务,而不会回滚当前事务。如果当前事务失败,则嵌套事务和当前事务都将被回滚。

这些传播行为可以使用@Transactional注解中的propagation属性来指定。

22.Bean Factory和ApplicationContext有什么区别?

Bean Factory和ApplicationContext都是Spring框架中负责管理Bean的容器,但是它们之间有一些区别:

  1. Bean Factory是Spring框架的基础设施,提供了最基本的IOC功能,而ApplicationContext是Bean Factory的超集,包含了Bean Factory的所有功能,并且还提供了更多的企业级特性,如事件传播、国际化、AOP等。

  1. 在实现上,ApplicationContext是Bean Factory的扩展,ApplicationContext除了提供了Bean Factory的所有功能之外,还提供了更多的功能,例如:资源管理、事件发布等等。

  1. 一般来说,Bean Factory适用于轻量级应用,而ApplicationContext则适用于大型企业应用,因为ApplicationContext提供了更多的企业级特性和功能,例如:事务管理、远程访问、JNDI访问等等。

  1. ApplicationContext在启动时就实例化所有的单例Bean,而Bean Factory是在获取Bean时才进行实例化。

总之,ApplicationContext是Bean Factory的超集,提供了更多的企业级特性和功能,一般情况下我们优先使用ApplicationContext。

你可能感兴趣的:(面试复习,spring,java,mvc,面试)