框架
框架
1.Spring
-1.Spring用到了哪些设计模式
Spring框架在其设计和实现中使用了多种设计模式,这些设计模式有助于提高代码的可重用性、灵活性和可维护性。以下是一些在Spring中常用的设计模式:
- 工厂模式:Spring通过BeanFactory和ApplicationContext来创建对象,其中BeanFactory就是简单工厂模式的体现。工厂模式隐藏了对象实例化的复杂性,并提供一个统一的方式来获取对象。
- 单例模式:在Spring中,默认情况下,bean以单例的形式存在。这意味着对于每个bean定义,Spring容器只会创建一个共享的实例。这样做可以减少系统资源的消耗,并减少垃圾回收器的压力。
- 代理模式:Spring的面向切面编程(AOP)功能使用到了JDK的动态代理和CGLIB字节码生成技术。这使得可以在不修改源代码的情况下,为对象添加额外的行为。
- 策略模式:例如,Resource接口的不同实现类针对不同的资源文件实现了不同的资源获取策略。策略模式允许在运行时选择算法或操作的具体实现。
- 模板方法模式:Spring中的JdbcTemplate、RestTemplate等都是模板方法模式的应用。它们提供了一个操作的框架,而具体的实现步骤则可以由子类来提供,从而实现代码复用和减少重复代码。
- 观察者模式:Spring事件驱动模型使用了观察者模式。当某个事件发生时,所有注册为该事件监听者的bean都会收到通知。
- 装饰器模式:在Spring中,装饰器模式用于给对象添加额外的功能,这比生成子类更为灵活。
- 适配器模式:在Spring MVC中,处理器适配器将不同的控制器适配成统一的Handler接口,使得不同的控制器可以被统一处理。
- 责任链模式:在AOP中,责任链模式用于组织多个拦截器,形成一个处理请求的责任链。
- 桥接模式:在Spring中,桥接模式可以用来解耦抽象部分和实现部分,例如动态切换不同的数据源。
综上所述,这些设计模式的使用不仅体现了Spring框架的灵活性和扩展性,也使得开发人员能够更加专注于业务逻辑,而不必关心底层的细节实现。
0.Spring的优点/为什么用Spring框架
Spring 是一款开源的轻量级的控制反转(IoC)和面向切面(AOP) 开发框架,可以提高开发人员的开发效率以及系统的可维护性。
1.通过Ioc控制反转和DI依赖注入实现松耦合,能够降低组件之间的耦合度,使得代码更加灵活、易于维护和扩展(Spring就是一个大工厂,可以将所有对象的创建和依赖关系维护都交给spring管理)
2.支持AOP面向切面的编程,把日志、事务管理等从业务逻辑中分离出来,提高了代码的复用性和可维护性。
3.支持声明式事务,可以通过声明式事务来管理数据库操作的事务
4.Spring是非侵入式的。使用Spring的框架不用继承Spring的类和接口,
5.Spring框架拥有庞大的社区支持,提供了丰富的文档、教程等资料,用的人多大家交流问题也更加方便
1.IOC和DI
1.什么是IOC控制反转呢?
IOC-Inversion of Control,即控制反转。IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 外部来管理。
为什么叫控制反转?
- 控制:指的是对象创建(实例化、管理)的权力
- 反转:控制权交给外部环境(Spring 框架、IoC 容器)
2.DI依赖注入
(1)什么是依赖注入呢?
在Spring创建对象的过程中,把对象依赖的属性注入到对象中。spring就是通过反射来实现注入的。依赖注入主要有三种方式:注解注入(@Autowired),构造器注入和属性注入。
3.IOC和DI的作用
最终目标就是:充分解耦,具体实现靠:
- 使用IOC容器管理bean(IOC)
- 在IOC容器内将有依赖关系的bean进行关系绑定(DI)
- 最终:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.
4.Spring的IOC和DI用了什么设计模式
Spring 的 IoC(控制反转)和 DI(依赖注入)使用了工厂模式、单例模式和代理模式等设计模式。具体如下:
- 工厂模式:IoC 容器负责创建对象,当需要创建一个对象时,只需配置好相应的配置文件或注解,无需关心对象的创建过程。IoC 容器就像一个工厂,它根据配置文件或注解来生成和管理对象实例。
- 单例模式:在 Spring 框架中,默认情况下,Bean 是单例的,即在整个应用中只有一个实例。这是通过单例模式实现的,确保每个 Bean 的生命周期内只有一个共享的实例。
- 代理模式:AOP(面向切面编程)是 Spring 的另一个核心特性,它使用代理模式来实现。通过代理模式,可以在不修改原始类代码的情况下,为对象提供额外的功能,例如事务管理和日志记录。
综上所述,这些设计模式共同工作,使得 Spring 框架能够提供一个高度解耦、易于扩展和维护的应用程序架构。
2.AOP
1.什么是AOP
AOP是面向切面编程,将公共逻辑(事务管理、日志、缓存等)封装成切面,跟业务代码进行分离,抽取公共模块复用,降低耦合。切面就是那些与业务无关,但所有业务模块都会调用的公共逻辑。
具体组成:
1.连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在SpringAOP中,理解为方法的执行
2.切入点(Pointcut):匹配连接点的式子
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 连接点范围要比切入点范围大,是切入点的方法也一定是连接点,但是是连接点的方法就不一定要被增强,所以可能不是切入点。
切入点定义依托一个不具有实际意义的方法进行,即无参数、无返回值、方法体无实际逻辑,然后在上面加个@PointCut注解。
3.通知(Advice):在切入点处执行的操作,也就是共性功能,是通知类的一个函数
4.通知类:定义通知的类
5.切面(Aspect):描述通知与切入点的对应关系。
2.AOP怎么实现
AOP有两种实现方式:静态代理和动态代理。
静态代理
静态代理:代理类在编译阶段生成,在编译阶段将通知织入Java字节码中,也称编译时增强。AspectJ使用的是静态代理。
缺点:代理对象需要与目标对象实现一样的接口,并且实现接口的方法,会有冗余代码。同时,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理
动态代理:代理类在程序运行时创建,AOP框架不会去修改字节码,而是在内存中临时生成一个代理对象,在运行期间对业务方法进行增强,不会生成新类。
3.Spring的AOP是怎么实现的
Spring
的AOP
是通过动态代理实现的。而Spring
的AOP
使用了两种动态代理,分别是JDK的动态代理,以及CGLib的动态代理。
如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK 动态代理,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib动态代理 生成一个被代理对象的子类来作为代理。
4.JDK动态代理和CGLIB动态代理的区别
JDK动态代理
如果目标类实现了接口,Spring AOP会选择使用JDK动态代理目标类。代理类根据目标类实现的接口动态生成,不需要自己编写,生成的动态代理类和目标类都实现相同的接口。JDK动态代理的核心是InvocationHandler
接口和Proxy
类。
缺点:目标类必须有实现的接口。如果某个类没有实现接口,那么这个类就不能用JDK动态代理。
CGLIB动态代理
通过继承实现。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library)可以在运行时动态生成类的字节码,动态创建目标类的子类对象,在子类对象中增强目标类。
优点:目标类不需要实现特定的接口,更加灵活。
缺点:CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final
,那么它是无法使用CGLIB做动态代理的。
5.Spring AOP的相关术语
- 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
- 在SpringAOP中,理解为方法的执行
- 切入点(Pointcut):匹配连接点的式子
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 一个具体的方法:如com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
- 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
- 连接点范围要比切入点范围大,是切入点的方法也一定是连接点,但是是连接点的方法就不一定要被增强,所以可能不是切入点。
- 在SpringAOP中,一个切入点可以描述一个具体方法,也可也匹配多个方法
- 通知(Advice):在切入点处执行的操作,也就是共性功能
- 在SpringAOP中,功能最终以方法的形式呈现
- 通知类:定义通知的类
- 切面(Aspect):描述通知与切入点的对应关系。
Spring通知类型
Spring切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning ):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。
6.Spring一般是怎么使用AOP(公共日志保存/事务处理)
1.怎么用AOP实现公共日志保存
使用aop中的环绕通知+切点表达式,这个表达式就是要找到要记录日志的方法,然后通过环绕通知的参数获取请求方法的参数,比如类信息、方法信息、注解、请求方式等,获取到这些参数以后,保存到ES数据库
2.怎么用AOP实现事务处理
在方法前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
7.多个切面的执行顺序如何控制?
可以使用@Order
注解直接定义切面顺序
1 | // 值越小优先级越高 |
3.Spring事务
1.什么是事务
事务是逻辑上的一组操作,要么都执行,要么都不执行。
2.事务的特性ACID
原子性(Atomicity
):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性(Consistency
):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
隔离性(Isolation
):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性(Durability
):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
3.Spring事务实现方式(声明式和编程式)
Spring事务机制主要包括声明式事务和编程式事务。
- 编程式事务:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
- 声明式事务:在 XML 配置文件中配置或者直接基于注解(推荐使用) : 实际是通过 AOP 实现(基于
@Transactional
的全注解方式使用最多)
4.Spring事务有几种事务传播行为
事务传播行为是为了解决业务层方法之间互相调用的事务问题。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
在TransactionDefinition接口中定义了七个事务传播行为:
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
使用PROPAGATION_REQUIRES_NEW
时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。
使用PROPAGATION_NESTED
时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
5.Spring事务隔离级别
Spring定义了一个枚举类:Isolation
,里面有5个Spring事务隔离级别
1.ISOLATION_DEFAULT
:使用后端数据库默认的隔离级别,MySQL 默认采用的 REPEATABLE_READ
隔离级别 Oracle 默认采用的 READ_COMMITTED
隔离级别.
2.读未提交ISOLATION_READ_UNCOMMITTED` :最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
3.读已提交ISOLATION_READ_COMMITTED`: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
4.可重复读ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
5.可串行化ISOLATION_SERIALIZABLE` : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
6.Spring事务失效场景
1.访问权限问题:如果事务方法的访问权限不是定义成public,这样会导致事务失效,因为spring要求被代理方法必须是public
的。
2. 方法用final修饰:如果事务方法用final修饰,将会导致事务失效。因为spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法添加事务功能。
3.对象没有被spring管理:使用spring事务的前提是:对象要被spring管理,需要创建bean实例。如果类没有加@Controller、@Service、@Component、@Repository等注解,即该类没有交给spring去管理,那么它的方法也不会生成事务。
4.在代码中捕获异常没有抛出:事务通知只有捉到了目标抛出的异常才能进行后续的回滚处理。如果我们自己捕获了异常处理掉了没有抛出,这种情况下spring事务不会回滚。
5.表不支持事务:如果MySQL使用的存储引擎是myisam,这样的话是不支持事务的。因为myisam存储引擎不支持事务。
7.@Transactional注解,注解在什么时候会失效
@Transactional注解在以下情况下会失效:
- 方法内部调用了同类中的另一个非事务方法。因为Spring的事务管理是基于AOP代理实现的,当一个类中的方法调用另一个方法时,实际上是通过代理对象进行调用的。如果被调用的方法没有使用@Transactional注解,那么事务管理将不会生效。
- 异常类型不正确。@Transactional注解默认只对RuntimeException类型的异常进行回滚,如果是其他类型的异常,需要指定rollbackFor属性。例如,如果要对Exception类型的异常进行回滚,可以这样写:@Transactional(rollbackFor = Exception.class)。
- 数据库引擎不支持事务。如果使用的数据库引擎不支持事务,那么@Transactional注解将无法生效。例如,MySQL的MyISAM存储引擎就不支持事务。
- 多数据源配置错误。在使用多数据源的情况下,如果不同数据源之间的事务管理没有正确配置,那么@Transactional注解可能会失效。需要确保每个数据源都有对应的事务管理器和事务通知器。
- 事务传播行为设置不当。@Transactional注解有一个propagation属性,用于设置事务的传播行为。如果不正确地设置了传播行为,可能会导致事务失效。例如,如果设置为Propagation.REQUIRES_NEW,那么每次调用都会创建一个新的事务,而不是加入到当前事务中。
4.bean
0.Sping 的bean为什么用到了单例模式?
Spring中的Bean默认使用单例模式,以下是几个可能的原因:
- 节省资源:单例模式确保一个类只有一个实例,这样可以减少系统资源的消耗,特别是当一个对象的创建和销毁需要较大成本时,如数据库连接或大型配置对象。
- 共享状态:单例模式允许多个对象共享一个实例的状态,这在需要共享数据或维持特定状态的场景中非常有用。
- 性能提升:由于不需要频繁地创建和销毁对象,单例模式可以提高应用程序的性能。
- 容器管理:Spring容器负责管理单例Bean的生命周期,包括创建、初始化、装配和其他生命周期回调,这有助于简化Bean的管理和维护。
综上所述,Spring中的Bean使用单例模式主要是为了节省资源、共享状态、提高性能以及方便容器管理。这些因素共同作用,使得单例模式成为Spring Bean的默认选择。当然,如果需要,开发者也可以将Bean定义为原型模式,以便每次请求都创建一个新的实例。
1.什么是bean
Bean 代指的就是那些被 IoC 容器所管理的对象。
我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
2.将一个类声明为 Bean 的注解有哪些?
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用于接受用户请求并调用Service
层返回数据给前端页面。
3.@Component 和 @Bean 的区别是什么?
都是使用注解定义 Bean。@Bean 是使用 Java 代码装配 Bean,@Component 是自动装配 Bean。
@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。
@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
当我们引用第三方库中的类需要装配到 Spring
容器时,则只能通过 @Bean
来实现。
4.Bean 的作用域/作用范围有哪些?
Spring 中 Bean 的作用域通常有下面几种:
- singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
- prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续
getBean()
两次,得到的是不同的 Bean 实例。 - request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
- session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。
- application/global-session (仅 Web 应用可用):每个 Web 应用在启动时创建一个 Bean(应用 Bean),该 bean 仅在当前应用启动时间内有效。
- websocket (仅 Web 应用可用):每一次 WebSocket 会话产生一个新的 bean。
5.bean是线程安全的吗
Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。
prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题,这取决于 Bean 是否有状态(就是说这个bean是否是包含可变的成员变量的对象)。如果这个 bean 是有状态的话,那就存在线程安全问题(**有状态 Bean 是指)。
不过,大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
单例bean线程安全问题解决办法
对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:
- 在 Bean 中尽量避免定义可变的成员变量。
- 在类中定义一个
ThreadLocal
成员变量,将需要的可变成员变量保存在ThreadLocal
中(推荐的一种方式)。
5.SpringBoot如何实现自动装配?
SpringBoot通过**@EnableAutoConfiguration注解和自动配置类**实现自动装配。
SpringBoot的自动装配机制主要涉及以下几个步骤:
- 使用@SpringBootConfiguration注解:该注解中包含@EnableAutoConfiguration,它是实现自动装配的起始点。
- 启用自动配置:通过@EnableAutoConfiguration注解启用自动配置功能,该注解会导入一个特殊的组件,即AutoConfigurationImportSelector类型的组件。
- 加载自动配置类:AutoConfigurationImportSelector类型的组件会根据项目的依赖以及spring.factories文件中的配置,自动导入相应的自动配置类到Spring容器中。
- 按需加载配置类:所有被扫描到的自动配置类将根据条件进行生效,比如对应的配置文件是否存在,依赖是否满足等。满足条件的自动配置类将会为容器装配所需的组件。
- 自定义配置:如果需要替换或修改默认的自动配置,可以通过定义自己的@Bean来替换默认的Bean,或者在application.properties中修改相关参数来达到定制化配置的目的。
- SPI机制:SpringBoot还采用了Service Provider Interface (SPI)机制来实现一些自动装配的功能,使得程序可以自动地、可插拔地进行装配。
综上所述,SpringBoot的自动装配大大简化了项目的配置工作,允许开发者更专注于业务代码的编写,而无需过多关心底层的配置细节。不过,理解其背后的原理有助于更好地掌控SpringBoot应用的配置和开发。
6.bean自动装配的方式/注入方式有哪些?
Spring的自动装配有三种模式:byType(根据类型),byName(根据名称)、constructor(根据构造函数)。
byType
找到与依赖类型相同的bean注入到另外的bean中,这个过程需要借助setter注入来完成,因此必须存在set方法,否则注入失败。
默认情况下@Autowired是按类型匹配的(byType)。如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。
当需要创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,可以使用@Qualifier
注解和 @Autowired
通过指定应该装配哪个 bean 来消除歧义。
byName
将属性名与bean名称进行匹配,如果找到则注入依赖bean。
constructor
无论名称是否匹配,存在单个实例则优先按类型进行参数匹配,当存在多个类型相同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败。
7.bean的生命周期
1.调用bean的构造方法创建Bean
2.通过反射调用setter方法进行属性的依赖注入
3.如果Bean实现了BeanNameAware
接口,Spring将调用setBeanName
(),设置 Bean
的name(xml文件中bean标签的id)
4.如果Bean实现了BeanFactoryAware
接口,Spring将调用setBeanFactory()
把bean factory设置给Bean
5.如果存在BeanPostProcessor
,Spring将调用它们的postProcessBeforeInitialization
(预初始化)方法,在Bean初始化前对其进行处理
6.如果Bean实现了InitializingBean
接口,Spring将调用它的afterPropertiesSet
方法,然后调用xml定义的 init-method 方法,两个方法作用类似,都是在初始化 bean 的时候执行
7.如果存在BeanPostProcessor
,Spring将调用它们的postProcessAfterInitialization
(后初始化)方法,在Bean初始化后对其进行处理
8.Bean初始化完成,供应用使用,这里分两种情况:
8.1 如果Bean为单例的话,那么容器会返回Bean给用户,并存入缓存池。如果Bean实现了DisposableBean
接口,Spring将调用它的destory
方法,然后调用在xml中定义的 destory-method
方法,这两个方法作用类似,都是在Bean实例销毁前执行。
8.2 如果Bean是多例的话,容器将Bean返回给用户,剩下的生命周期由用户控制。
8.BeanFactory和FactoryBean的区别?
BeanFactory:是所有Spring Bean的容器根接口,给Spring 的容器定义一套规范,给IOC容器提供了一套完整的规范,比如我们常用到的getBean方法等
FactoryBean:是一个bean。但是他不是一个普通的bean,是可以创建对象的bean。。通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的具体的细节.
9.BeanFactory和ApplicationContext有什么区别?
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
两者区别如下:
1、功能上的区别。BeanFactory是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能,如继承MessageSource、支持国际化、统一的资源文件访问方式、同时加载多个配置文件等功能。
2、加载方式的区别。BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
而ApplicationContext是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单例Bean,那么在需要的时候,不需要等待创建bean,因为它们已经创建好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
10.怎么让2个bean按顺序加载?
当一个 bean 需要在另一个 bean 初始化之后再初始化时,可以使用@DependOn注解
11.spring的bean是否会被jvm的gc回收
Spring的单例(singleton)bean在Spring容器运行期间通常不会被JVM的垃圾回收器(GC)回收,而原型(prototype)bean则可能在不再被引用时被回收。具体如下:
- 单例(Singleton)Beans:默认情况下,Spring容器中创建的bean是单例的,即在整个应用程序生命周期内只存在一个实例。这些单例bean通常与Spring容器具有相同的生命周期,除非Spring容器被关闭或bean被显式销毁,否则它们会一直存在于内存中,因此不会被JVM的GC回收。
- 原型(Prototype)Beans:与单例bean不同,原型bean每次请求时都会创建一个新的实例。这意味着它们的生命周期通常较短,使用完毕后如果没有其他引用指向它们,就可能会被JVM的GC回收。
- 作用域和生命周期:Spring bean的作用域和生命周期也会影响其是否被回收。例如,如果一个bean被配置为在某个特定的生命周期结束或某个条件满足时销毁,那么它将可能被JVM回收。
- 弱引用和软引用:如果在Spring配置中使用了弱引用或软引用,那么即使bean仍然在Spring容器中,也可能在JVM的下一次GC周期中被回收,因为弱引用和软引用的对象更容易被GC处理。
- Spring容器关闭:当Spring容器关闭时,所有的单例bean都会被销毁,此时它们将变得不可达,从而可能被JVM的GC回收。
综上所述,Spring的bean是否会被JVM的GC回收取决于其作用域、生命周期以及Spring容器的状态。单例bean在Spring容器运行期间通常不会被回收,而原型bean和其他特定条件下的bean可能会被回收。
12.springboot中不想加载一个bean如何做
要在Spring Boot中阻止一个bean被加载,您可以采取以下几种方法:
- 自定义@ComponentScan:通过自定义@ComponentScan注解,您可以指定需要扫描的包路径,从而排除不想加载的bean。在@ComponentScan注解中使用excludeFilters属性来排除特定的类。
- 使用@SpringBootApplication的exclude属性:如果您使用的是@SpringBootApplication注解,可以利用它的exclude属性来排除自动配置类。例如,如果您不想加载某个自动配置类,可以在@SpringBootApplication注解中列出这个类。
- 使用@EnableAutoConfiguration的exclude属性:在Spring Boot的启动类上使用@EnableAutoConfiguration(exclude = {ClassNotToLoad.class}),这样可以排除掉不需要自动配置的类。
- 使用TypeExcludeFilter:创建一个自定义的TypeExcludeFilter实现,并在@ComponentScan注解中引用它。这样可以实现更精细的控制,只排除特定类型的bean。
- 移除相关注解:如果某个bean是通过@Component、@Service、@Repository等注解自动注册到Spring容器中的,您可以通过移除这些注解来阻止它的加载。
- 调整bean加载顺序:在某些情况下,您可能需要控制bean的加载顺序。虽然@Order、@AutoConfigureOrder、@AutoConfigureAfter和@AutoConfigureBefore等注解主要用于控制Spring组件的顺序,但它们也可以在一定程度上影响bean的加载顺序。
总的来说,您可以通过上述方法来控制Spring Boot项目中哪些bean被加载,哪些不被加载。这些方法可以帮助您更好地管理Spring容器中的bean,确保只有需要的bean被实例化和使用。
5.Spring循环依赖问题(三级缓存)
循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A。
首先,有两种Bean注入的方式。
构造器注入和属性注入。
就有了2种循环依赖。
对于构造器注入的循环依赖,Spring处理不了,会直接抛出异常。我们可以使用@Lazy懒加载,什么时候需要对象再进行bean对象的创建
对于属性注入的循环依赖,是通过三级缓存处理来循环依赖的。
1.singletonObjects:一级缓存,里面放置的是已经完成所有创建动作的单例对象,也就是说这里存放的bean已经完成了所有创建的生命周期过程,在项目运行阶段当执行多次getBean()时就是从一级缓存中获取的。 2.earlySingletonObjects:二级缓存,里面存放的只是进行了实例化的bean,还没有进行属性设置和初始化操作,也就是bean的创建还没有完成,还在进行中,这里是为了方便被别的bean引用 3.singletonFactories:三级缓存,Spring中的每个bean创建都有自己专属的ObjectFactory工厂类,三级缓存缓存的就是对应的bean的工厂实例,可以通过该工厂实例的getObject()方法获取该bean的实例。
下面我来讲一下通过三级缓存处理来循环依赖的过程吧。
第一,先实例A对象,同时会创建ObjectFactory对象存入三级缓存singletonFactories
第二,A在初始化的时候需要B对象,这个走B的创建的逻辑
第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories
第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个是可能是A的普通对象,另外一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三级缓存的关键
第五,B通过从通过二级缓存earlySingletonObjects 获得到A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects
第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects
第七,二级缓存中的临时对象A清除
为什么要有第三级缓存?
如果没有AOP的话确实可以两级缓存就可以解决循环依赖的问题,如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象
6.Spring启动过程
- 读取web.xml文件。
- 创建 ServletContext,为 ioc 容器提供宿主环境。
- 触发容器初始化事件,调用 contextLoaderListener.contextInitialized()方法,在这个方法会初始化一个应用上下文WebApplicationContext,即 Spring 的 ioc 容器。ioc 容器初始化完成之后,会被存储到 ServletContext 中。
- 初始化web.xml中配置的Servlet。如DispatcherServlet,用于匹配、处理每个servlet请求。
7.Spring 框架中用到了哪些设计模式?
关于下面这些设计模式的详细介绍,可以看我写的 Spring 中的设计模式详解open in new window 这篇文章。
- 工厂设计模式 : Spring 使用工厂模式通过
BeanFactory
、ApplicationContext
创建 bean 对象。 - 代理设计模式 : Spring AOP 功能的实现。
- 单例设计模式 : Spring 中的 Bean 默认都是单例的。
- 模板方法模式 : Spring 中
jdbcTemplate
、hibernateTemplate
等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。 - 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
- 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
- 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配
Controller
。
2.Spring MVC
1.什么是Spring MVC
SpringMVC是一种基于 Java 的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于Spring框架的一个模块。
它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
2.什么是MVC模式
MVC的全名是Model View Controller
,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。
MVC设计模式是一种软件架构模式,它通过将应用程序分为三个核心组件来组织代码,以实现业务逻辑、数据和界面显示的分离。这三个组件分别是:
- 模型(Model):模型代表应用程序的数据结构和业务逻辑,它负责处理应用程序的数据和业务规则。模型可以直接管理数据库,并且当数据发生变化时,它能够通知视图进行更新。
- 视图(View):视图是用户界面的组成部分,它负责展示模型中的数据。视图通常会从模型中获取数据并显示给用户,但它并不直接与模型交互,而是通过控制器来进行数据的更新和请求。
- 控制器(Controller):控制器作为模型和视图之间的中介,它负责接收用户的输入,并根据输入更新模型或视图。控制器处理用户的请求,决定调用哪个模型来处理数据,并选择相应的视图来显示响应。
MVC设计模式的目的是提高代码的复用性和灵活性,使得开发人员可以独立地修改视图或业务逻辑而不影响其他部分。这种分离还简化了测试过程,因为每个组件都可以单独进行测试。
总之,理解MVC设计模式的关键在于认识到其将应用程序的不同关注点分离开来,每个组件都有其明确的职责,相互之间通过定义良好的接口进行通信。这种设计模式在现代软件开发中非常常见,尤其是在构建复杂的桌面、Web和企业级应用程序时。
3.什么是拦截器
1.什么是拦截器
- 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
- 作用:
- 在指定的方法调用前后执行预先设定的代码
- 阻止原始方法的执行
2.怎么自定义拦截器
Spring MVC 拦截器对应HandlerInterceor接口,该接口位于org.springframework.web.servlet的包中,定义了三个方法,若要实现该接口,就要实现其三个方法:
- 前置处理(preHandle()方法):该方法在执行控制器方法之前执行。返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。
- 后置处理(postHandle()方法):该方法在执行控制器方法调用之后,且在返回ModelAndView之前执行。由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图,可通过此方法对请求域中的模型和视图做进一步的修改。
- 已完成处理(afterCompletion()方法):该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。
3.拦截器工作流程
1.所有的拦截器(Interceptor)
和处理器(Handler)
都注册在HandlerMapping
中。
2.Spring MVC中所有的请求都是由
DispatcherServlet`分发的。
3.当请求进入DispatcherServlet.doDispatch()
时候,首先会得到处理该请求的Handler
(即Controller
中对应的方法)以及所有拦截该请求的拦截器。拦截器就是在这里被调用开始工作的。
4.当有拦截器后,请求会先进入preHandle方法,如果方法返回true,postHandle
、afterCompletion
才有可能被执行。如果返回false,则直接跳过后面方法的执行。
4.拦截器和过滤器的区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
- 拦截器提供了三个方法,分别在不同的时机执行;过滤器仅提供一个方法
4.什么是restful风格
RESTful风格是一种软件架构风格,使用统一的资源标识符(URI)来表示资源、使用标准的HTTP方法(如GET、POST、PUT、DELETE)来对资源进行操作、使用状态码来表示操作结果等.
GET代表获取一个资源,POST代表添加一个资源,PUT代表修改一个资源,DELETE代表删除一个资源
常用的RESTful状态码包括:
- 200 OK:表示请求成功。
- 201 Created:表示新资源已经成功创建。
- 204 No Content:表示服务器成功处理了请求,但没有返回任何内容。
- 400 Bad Request:表示请求无效,例如缺少必需的参数。
- 401 Unauthorized:表示未经授权,需要进行身份验证。
- 403 Forbidden:表示服务器理解请求,但拒绝执行。
- 404 Not Found:表示请求的资源不存在。
- 405 Method Not Allowed:表示请求中指定的方法不被允许。
- 500 Internal Server Error:表示服务器遇到了一个未曾预料的状况,导致无法完成对请求的处理。
5.SpringMVC执行流程/工作原理
- 客户端(浏览器)发送请求,
DispatcherServlet
拦截请求。 DispatcherServlet
根据请求信息调用HandlerMapping
。HandlerMapping
根据 URL 去匹配查找能处理的Handler
(也就是我们平常说的Controller
控制器) ,并会将请求涉及到的拦截器和Handler
一起封装。DispatcherServlet
调用HandlerAdapter
适配器执行Handler
。Handler
完成对用户请求的处理后,会返回一个ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的View
。ViewResolver
会根据逻辑View
查找实际的View
。DispaterServlet
把返回的Model
传给View
(视图渲染)。- 把
View
返回给请求者(浏览器)
6.Spring MVC的主要组件?
DispatcherServlet
:核心的中央处理器,负责接收请求、分发,并给予客户端响应。HandlerMapping
:处理器映射器,根据 URL 去匹配查找能处理的Handler
,并会将请求涉及到的拦截器和Handler
一起封装。HandlerAdapter
:处理器适配器,根据HandlerMapping
找到的Handler
,适配执行对应的Handler
;Handler
:请求处理器,处理实际请求的处理器。ViewResolver
:视图解析器,根据Handler
返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet
响应客户端
7.Spring MVC的异常处理
三种方法:
- 使用系统定义好的异常处理器 SimpleMappingExceptionResolver
- 使用自定义异常处理器
- 使用异常处理注解(
@ControllerAdvice
+@ExceptionHandler
)
3.Springboot
0.Javaweb、spring、springmvc和springboot有什么区别,都是做什么用的?
JavaWeb是 Java 语言的 Web 开发技术,主要用于开发 Web 应用程序,包括基于浏览器的客户端和基于服务器的 Web 服务器。
Spring是一个轻量级的开源开发框架,主要用于管理 Java 应用程序中的组件和对象,并提供各种服务,如事务管理、安全控制、面向切面编程和远程访问等。它是一个综合性框架,可应用于所有类型的 Java 应用程序。
SpringMVC是 Spring 框架中的一个模块,用于开发 Web 应用程序并实现 MVC(模型-视图-控制器)设计模式,它将请求和响应分离,从而使得应用程序更加模块化、可扩展和易于维护。
Spring Boot是基于 Spring 框架开发的用于开发 Web 应用程序的框架,它帮助开发人员快速搭建和配置一个独立的、可执行的、基于 Spring 的应用程序,从而减少了繁琐和重复的配置工作。
综上所述,JavaWeb是基于 Java 语言的 Web 开发技术,而 Spring 是一个综合性的开发框架,SpringMVC用于开发 Web 应用程序实现 MVC 设计模式,而 Spring Boot 是基于 Spring 的 Web 应用程序开发框架。
1.Spring Boot 的核心注解是哪个?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
- @ComponentScan:Spring组件扫描。
2.Springboot自动配置原理
在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
其中@EnableAutoConfiguration
是实现自动化配置的核心注解。
该注解通过@Import
注解导入对应的配置选择器。关键的是内部就是读取了该项目和该项目引用的Jar包的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。
在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
一般条件判断会有像@ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
3.Spring Boot 打成的 jar 和普通的 jar 有什么区别 ?
- Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过
java -jar xxx.jar
命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类。 - Spring Boot 的 jar 无法被其他项目依赖,主要还是他和普通 jar 的结构不同。普通的 jar 包,解压后直接就是包名,包里就是我们的代码,而 Spring Boot 打包成的可执行 jar 解压后,在
\BOOT-INF\classes
目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,将 Spring Boot 项目打包成两个 jar ,一个可执行,一个可引用。
4.springboot 默认的包扫描路径?
SpringBoot默认的包扫描路径是启动类所在的包及其子包。
在SpringBoot中,默认情况下,框架会自动扫描启动类所在包下的所有Java类,以便发现并注册Spring组件,如@Service、@Repository、@Controller等。这种自动扫描机制简化了配置过程,使得开发者无需手动指定每个组件的扫描路径。
5.springboot的启动类的注解@SpringbootApplication工作原理:
@SpringBootApplication是Spring Boot的核心注解,它主要包含以下三个注解:
- @SpringBootConfiguration:表示这是一个Spring Boot的配置类。
- @EnableAutoConfiguration:让Spring Boot根据项目中的依赖自动配置相关的bean。
- @ComponentScan:告诉Spring Boot扫描哪些包下的类作为组件(component)。
工作原理如下:
- 当Spring Boot应用启动时,会扫描主程序类上的@SpringBootApplication注解。
- 根据@SpringBootConfiguration注解,将当前类作为配置类。
- 根据@ComponentScan注解,扫描指定包下的类,并将其注册为Spring容器中的Bean。
- 根据@EnableAutoConfiguration注解,Spring Boot会自动根据项目中的依赖配置相关的Bean。例如,如果项目中引入了spring-boot-starter-web依赖,那么Spring Boot会自动配置Tomcat和Spring MVC等相关的Bean。
- Spring Boot会将所有配置好的Bean注入到Spring容器中,完成自动配置。
- 最后,Spring Boot会调用主程序类的main方法,启动应用。
6.spring boot应用启动慢怎么优化启动时间?
要优化Spring Boot应用的启动时间,可以采取以下几种方法:
- 减少业务初始化:检查并减少应用中的初始化逻辑,尤其是那些耗时的操作,如建立数据库连接、Redis连接和其他各种连接池。尽量减少不必要的依赖,能够异步处理的就异步处理。
- 使用Spring Boot 2.2及以上版本的新特性:Spring Boot 2.2版本引入了
spring.main.lazy-initialization
属性,将其配置为true
可以实现所有Bean的延迟初始化,这样可以在一定程度上提高启动速度。但需要注意的是,这样可能会导致第一次访问时响应较慢。 - 并行化:考虑将一些初始化流程并行化,比如使用多线程或异步任务来同时处理多个初始化操作。
- 优化依赖管理:确保项目中的依赖是最优的,避免引入不必要的库和框架。同时,可以使用Maven或Gradle的懒加载特性,只在需要时才加载某些依赖。
- 特定于配置文件的配置:根据不同的环境(开发、测试、生产)选择合适的配置,以减少不必要的资源消耗和初始化操作。
- 分析并优化代码执行路径:使用性能分析工具(如VisualVM、JProfiler等)来监控应用启动过程中的资源消耗和瓶颈,针对性地进行优化。
- 优化日志系统:如果应用在启动时加载了大量的日志配置,可以考虑简化日志系统,或者延迟加载日志配置文件。
- 数据库优化:如果应用启动时需要加载大量的数据,可以考虑优化数据库查询,或者将数据加载操作移到后台线程中进行。
- 资源文件优化:对于资源文件(如图片、模板等),可以考虑使用懒加载或者CDN服务,以减少应用启动时的加载压力。
- 硬件资源:确保服务器硬件资源充足,如内存、CPU等,以支持应用快速启动。
- 容器优化:如果应用部署在容器中,可以考虑优化容器的启动参数和资源配置。
- 外部服务调用:如果应用启动时需要调用外部服务,可以考虑将这些调用延迟到应用启动后进行,或者使用缓存来存储这些服务的返回结果。
- 代码质量:提高代码质量,避免复杂的构造函数和大量的单例模式,这些都可能导致应用启动变慢。
- 监控和日志:启用详细的启动日志,监控应用启动过程中的各个阶段,以便于找到具体的瓶颈所在。
- 第三方库和服务:检查并优化第三方库和服务的使用,有些库可能在启动时执行了大量操作,可以考虑替换或者升级这些库。
- JVM参数调优:根据实际情况调整JVM参数,如增加初始堆大小、优化垃圾回收策略等,以提高应用启动效率。
- 分布式配置中心:如果使用了分布式配置中心,可以考虑在启动时只加载必要的配置,其他配置可以按需加载。
- 预热应用:在生产环境中,可以通过预热应用的方式,提前加载一些资源和数据,以减少实际用户使用时的启动时间。
- 自动化测试:通过自动化测试来持续监控和优化应用的启动时间,确保每次变更都能满足性能要求。
总之,通过上述方法,您可以有效地优化Spring Boot应用的启动时间,提高用户体验和应用的可用性。在实施这些优化措施时,请根据实际情况和具体需求进行选择和调整。
4.Spring框架常用注解
1.Spring常用注解
第一类是:声明bean,有@Component、@Service、@Repository、@Controller
第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resourse
第三类是:设置作用域 @Scope
第四类是:spring配置相关的,比如@Configuration,@ComponentScan 和 @Bean
第五类是:跟aop相关做增强的注解 @Aspect,@Before,@After,@Around,@Pointcut
1.@Component、@Controller、@Repositor和@Service 的区别?
@Component:表明一个类会作为组件类,并告知Spring要为这个类创建bean。
@Controller:将类标记为 Spring Web MVC 控制器。
@Service:将类标记为业务层组件。
@Repository:将类标记为数据访问组件,即DAO组件。
2.@Bean和@Component有什么区别?
都是使用注解定义 Bean。
1.@Bean 是使用 Java 代码装配 Bean,@Component 是自动装配 Bean。
2.@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
3.当我们引用第三方库中的类需要装配到 Spring
容器时,则只能通过 @Bean
来实现。
3.@Qualifier 注解有什么作用
默认情况下@Autowired是按类型匹配的(byType)。如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合。
当一个接口存在多个实现类且它们都已经被 Spring 容器所管理的话,这时候就会有多个同类型的bean,根据类型注入
这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。
可以使用@Qualifier
注解和 @Autowired
通过指定应该装配哪个 bean 来消除歧义。
4.注入bean的2个注解@Autowired和@Resource的区别?
1.@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。
2.Autowired
默认的注入方式为byType
(根据类型进行匹配。
1 | @Resource` 有两个比较重要且日常开发常用的属性:`name`(名称)、`type`(类型)。如果仅指定 `name` 属性则注入方式为`byName`,如果仅指定`type`属性则注入方式为`byType |
3.当存在多个同类型的bean(比如一个接口有两个实现类,且它们都已经被 Spring 容器所管理。),@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过 @Qualifier
注解来指定名称,@Resource
可以通过 name
属性来指定名称。
2.Spring MVC常见注解
- @Controller:用于标识此类的实例是一个控制器。
- @RequestMapping:映射Web请求(访问路径和参数)。
- @ResponseBody:注解返回数据而不是返回页面
- @RequestBody:注解实现接收 http 请求的 json 数据,将 json 数据转换为 java 对象。
- @PathVariable:用于接收路径参数,使用{参数名称}描述路径参数
- @RequestParam:用于接收url地址传参或表单传参
- @RestController:@Controller+@ResponseBody
- @ExceptionHandler标识一个方法为全局异常处理的方法。
1.@Controller 注解有什么用?
@Controller
注解标记一个类为 Spring Web MVC 控制器。Spring MVC 会将扫描到该注解的类,然后扫描这个类下面带有 @RequestMapping
注解的方法,根据注解信息,为这个方法生成一个对应的处理器对象.
2.@RequestMapping 注解有什么用?
@RequestMapping
注解,用于配置处理器的 HTTP 请求方法,URI等信息,这样才能将请求和方法进行映射。这个注解可以作用于类上面,也可以作用于方法上面,在类上面一般是配置这个控制器的 URI 前缀。
3.@RestController 和 @Controller 有什么区别?
@RestController
注解,在 @Controller
基础上,增加了 @ResponseBody
注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回 JSON 数据格式。
4.@RequestMapping 和 @GetMapping 注解有什么不同?
@RequestMapping
:可注解在类和方法上;@GetMapping
仅可注册在方法上@RequestMapping
:可进行 GET、POST、PUT、DELETE 等请求方法;@GetMapping
是@RequestMapping
的 GET 请求方法的特例。
5.@RequestParam 和 @PathVariable 两个注解的区别
@PathVariable主要用于接收http://host:port/path/{参数值}数据。@RequestParam主要用于接收http://host:port/path?参数名=参数值数据,这里后面也可以不跟参数值。
@RequestParam和@PathVariable这两者之间区别不大,主要是请求的URL不一样
用@RequestParam请求接口时,URL是:http://www.test.com/user/getUserById?userId=1
用@PathVariable请求接口时,URL是:http://www.test.com/user/getUserById/2
3.Springboot常见注解-@SpringBootApplication
Spring Boot的核心注解是@SpringBootApplication , 他由几个注解组成 :
- @SpringBootConfiguration: 组合了- @Configuration注解,实现配置文件的功能;
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项
- @ComponentScan:Spring组件扫描
@SpringBootApplication注解工作流程
以下是Spring Boot启动类注解的工作流程:
- 初始化SpringApplication:在Spring Boot应用启动时,首先会进行SpringApplication的初始化。这个过程包括设置源(即设置Spring容器启动时依赖的初始配置类),设置WEB应用程序类型(例如SERVLET或REACTIVE),以及配置一些基本的环境变量、资源、构造器和监听器。
- 加载配置环境:在初始化之后,Spring Boot会根据项目的配置(如application.properties或application.yml文件)加载配置环境。这些配置信息将被用来设置Spring应用的各种参数,如数据库连接、服务器端口等。
- 创建上下文环境:接着,Spring Boot会创建应用上下文环境。在这个过程中,Spring Boot会根据项目中定义的Beans和自动配置项来构建Spring容器。
- 自动化配置:Spring Boot的一个核心特性是自动化配置,它会根据项目的依赖和配置自动配置Spring应用。例如,如果项目中包含了spring-boot-starter-web依赖,Spring Boot会自动配置嵌入式的Tomcat服务器和Spring MVC相关的组件。
- 启动应用:最后,通过调用
SpringApplication.run(SpringBoot.class, args)
方法,Spring Boot会启动内嵌的Web服务器(默认为Tomcat),并开始监听指定的端口,等待接收请求。
5.MyBatis
0.Mybatis和Mybatis-plus的区别
MyBatis和MyBatis-Plus在实现方式、功能支持以及编程风格方面存在一些差异。具体分析如下:
- 实现方式:MyBatis是基于XML或注解方式进行数据库操作的持久化框架,提供了基本的CRUD操作及动态SQL生成等功能。MyBatis-Plus则是在MyBatis的基础上进行了扩展和增强,通过提供更加简化的API使得开发更为便捷,同时在性能、效率和易用性上都有所优化。
- 功能支持:MyBatis-Plus相较于MyBatis增加了许多额外的实用组件,例如条件构造器、代码生成器、分页插件和性能分析拦截器等,这些工具可以大幅减少开发者编写SQL语句的工作量并提升开发效率。而MyBatis则相对原始,需要手动编写大量SQL语句来实现复杂功能。
- 编程风格:在MyBatis中,通常需要定义mapper.xml文件和使用相应的SQL查询语句,而在MyBatis-Plus中,开发者可以通过继承BaseMapper和使用Lambda表达式,以一种更接近于声明式编程的方式来进行数据库操作。这种方式类似于Spring Data JPA,极大地简化了数据访问层的代码。
总的来说,Mybatis-Plus是在Mybatis基础上的一个增强工具,它兼容所有Mybatis原生特性,引入Mybatis-Plus不会对现有的Mybatis构架产生影响。如果项目追求更高的开发效率和便利性,可以选择Mybatis-Plus;如果需要更多的灵活性和控制性,或者对自动生成的代码有顾虑,那么使用Mybatis可能更合适。
1.Mybatis是什么?
- MyBatis框架是一个开源的数据持久层框架。
- 它的内部封装了通过JDBC访问数据库的操作,支持普通的SQL查询、存储过程和高级映射,几乎消除了所有的JDBC代码和参数的手工设置以及结果集的检索。
- MyBatis作为持久层框架,其主要思想是将程序中的大量SQL语句剥离出来,配置在配置文件当中,实现SQL的灵活配置。
- 这样做的好处是将SQL与程序代码分离,可以在不修改代码的情况下,直接在配置文件当中修改SQL。
2.Mybatis分页方式以及是怎么分页的
MyBatis分页一般可以分为如下两类。 1)逻辑分页,先查询出所有的数据并缓存到内存,再根据业务相关需求,从内存数据中筛选出合适的数据进行分页。 2)物理分页,直接利用数据库支持的分页语法来实现,比如 MySQL 中提供了分页关键词Limit。
MyBatis 提供的4种分页方式 1)在MyBatis Mapper 配置文件中直接写分页 SQL,这种方式比较灵活,实现也简单
2)RowBounds 实现逻辑分页,也就是一次性加载所有符合查询条件的目标数据,根据页参数值在内存中实现分页。当然,在数据量比较大的情况下,JDBC 驱动本身会做一些优化也就是说,不会把所有结果存储在 ResultSet 中,而是只加载一部分数据,再根据需求从数据库中加载。这种方式不适合数据量较大的场景,而且有可能会因频繁访问数据库造成比较大的压力o
3)Interceptor 拦截器实现,通过拦截需要分页的 select 语句,并在这个 select 语句中动态拼接分页关键字,从而实现分页查询,Interceptor 是 MyBatis 提供的一种针对不同生命周期的拦截器,比如拦截执行器方法、拦截参数的处理、拦截结果集的处理、拦截 SQL 语法构建的处理。我们可以通过拦截不同阶段的处理来实现 MyBatis 相关功能的扩展。采用这种方式的好处就是,可以提供统一的处理机制,不需要我们单独维护分页相关的功能
4)插件(PageHelper)及(MyBaits-Plus、tkmybatis)框架实现,这些插件本质上也是使用Mybatis 的拦截器来实现的。它们帮我们实现了扩展和封装,节省了分页扩展封装的工作量,在实际开发中拿来即用即可。
3.MyBatis执行流程/工作原理
①读取MyBatis配置文件:mybatis-config.xml加载运行环境和映射文件
②构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由spring进行管理
③会话工厂创建SqlSession对象,这里面就含了执行SQL语句的所有方法
④操作数据库的接口,Executor执行器,同时负责查询缓存的维护
⑤Executor接口的执行方法中有一个MappedStatement类型的参数,封装了映射信息
⑥输入参数映射
⑦输出结果映射
4.MyBatis延迟加载
1.Mybatis是否支持延迟加载?
延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。
Mybatis支持一对一关联对象和一对多关联集合对象的延迟加载
在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false,默认是关闭的
2.延迟加载的底层原理知道吗?
延迟加载的基本原理是,使用 CGLIB 动态代理创建目标对象的代理对象,当调用目标方法时,进入拦截器方法。
比如调用a.getB().getName()
,拦截器 invoke()方法发现 a.getB()是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用a.setB(b)
,于是 a 的对象 b 属性就有值了,接着完成a.getB().getName()
方法的调用。
5.Mybatis的一级,二级缓存
Mybatis里面设计了二级缓存来提升数据的检索效率,避免每次数据的访问都需要去查询数据库。
一级缓存是SqlSession级别的缓存:Mybatis对缓存提供支持,默认情况下只开启一级缓存,一级缓存作用范围为同一个SqlSession。在SQL和参数相同的情况下,我们使用同一个SqlSession对象调用同一个Mapper方法,往往只会执行一次SQL。因为在使用SqlSession第一次查询后,Mybatis会将结果放到缓存中,以后再次查询时,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession只会取出当前缓存的数据,不会再次发送SQL到数据库。若使用不同的SqlSession,因为不同的SqlSession是相互隔离的,不会使用一级缓存。
二级缓存需要自己开启:可以使缓存在各个SqlSession之间共享。当多个用户在查询数据的时候,只要有任何一个SqlSession拿到了数据就会放入到二级缓存里面,其他的SqlSession就可以从二级缓存加载数据。
Mybatis的二级缓存什么时候会清理缓存中的数据?
当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了新增、修改、删除操作后,默认该作用域下所有 select 中的缓存将被 clear。