spring

1.spring

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

1.1介绍

Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。
Spring是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是它仍然可以和其他的框架无缝整合。

1.2spring的主要模块

Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
Spring Core:核心类库,所有功能都依赖于该类库,提供IOC和DI服务;
Spring AOP:AOP服务;
Spring Web:提供了基本的面向Web的综合特性,提供对常见框架如Struts2的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器;
Spring MVC:提供面向Web应用的Model-View-Controller,即MVC实现。
Spring DAO:对JDBC的抽象封装,简化了数据访问异常的处理,并能统一管理JDBC事务;
Spring ORM:对现有的ORM框架的支持;

1.3为什么要使用spring

spring提供ioc技术,容器会帮我们管理依赖的对象,从而不需要自己创建和管理依赖对象,更轻松的实现了程序的解耦
spring提供了事务支持,使得事务操作变的更加方便
提供了面向切面编程,跟方便的处理某一类问题
更方便的框架集成,spring可以更方便的集成第三方框架

1.4spring的生命周期

1.Bean定义
1、通过ComponentScan定义扫描的路径查看带有Component注解的类
2、找到资源后,开始解析并且将定义信息保存,这个时候还没有进行初始化,只有Bean的定义
3、将Bean定义发布到IOC容器中
2.Bean初始化
默认不进行延时初始化Spring会直接将定义的Bean进行初始化和依赖注入到对应的属性中,可以调整ComponentScan的lazyInit的值,默认是false
3.Bean生存期
4.Bean销毁
自定义销毁方法,执行destroy方法

Bean的初始化过程

Spring Bean的初始化过程是指在Spring容器启动时,创建和准备Bean实例的一系列步骤。以下是Spring Bean的初始化过程的概述:

  1. 定义Bean的配置
  2. 创建Bean实例
  3. 设置Bean的属性
  4. 实现Aware接口
  5. Bean的初始化方法
  6. Bean后置处理器的前置处理
  7. Bean的初始化方法调用
  8. Bean后置处理器的后置处理
    一旦Bean的初始化过程完成,该Bean就可以在应用程序中使用了。当应用程序关闭时,Spring容器会按照相反的顺序执行销毁Bean的步骤,包括调用销毁方法和Bean后置处理器的销毁方法。

这就是Spring Bean的初始化过程的概述。它提供了一个灵活且可扩展的机制,允许开发人员在Bean的不同生命周期阶段插入自定义逻辑,以满足特定的需求。

1.5spring注入bean的方式

setter()注入
构造器注入
静态工厂注入
实例工厂

1.6 spring容器的启动流程

原文章

(1)初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中:
(2)将配置类的BeanDefinition注册到容器中:
(3)调用refresh()方法刷新容器:

2.BeanFactory和ApplicationContext有什么区别?

(1)BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理。ApplicationContext接口作为BeanFactory的子类,除了提供BeanFactory所具有的功能外,
还提供了更完整的框架功能:

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

(2)①BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
③ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好了。相对于BeanFactory,ApplicationContext 唯一的不足是占用内存空间,当应用程序配置Bean较多时,程序启动较慢。
(3)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
(4)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

1.7.Spring的自动装配:

(1)在Spring框架xml配置中共有5种自动装配:

no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
byType:通过参数的数据类型进行自动装配。
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

(2)基于注解的自动装配方式:

使用@Autowired、@Resource注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、Setter方法

1.@Autowired和@Resource之间的区别:

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。
(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。

2.事务

2.1spring事务的种类

spring 事务分为编程式事务与声明式事务
编程式事务

编程式事务管理使用TransactionTemplate。

声明式事务

声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
优点
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

2.2隔离级别

1.三个问题

「脏读」 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
「不可重复读」 :是指在一个事务内,多次读同一数据。
「幻读」 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

2.隔离级别

1.读未提交,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
2.提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
3.可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
4.序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

2.3失效问题

1.抛出检查异常导致事务不能正确回滚
原因:Spring 默认只会回滚非检查异常
解决方案:配置 rollbackFor 属性 (@Transactional(rollbackFor = Exception.class )
2.业务方法内自己 try-catch 异常导致事务不能正确回滚
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解决方案: 1)异常原样抛出,即在 catch 块添加 throw new RuntimeException(e);2) 手动设置 TransactionStatus.setRollbackOnly() ,在 catch 块添加 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
3.aop 切面顺序导致导致事务不能正确回滚
原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常
解决方案: 1) 和失效原因二一样
2) 调整切面顺序,在 MyAspect 上添加 @Order(Ordered.LOWEST_PRECEDENCE - 1) (不推荐)
4.非 public 方法导致的事务失效
原因:Spring 为方法创建代理、添加事务通知、前提条件都是该方法是 public 的
解决方案:1)使用public方法
5.父子容器导致的事务失效
原因:子容器扫描范围过大,把未加事务配置的 service 扫描进来
解决方案:1)各扫描各的,不要图简便
2)不要用父子容器,所有 bean 放在同一容器

2.4事务的传播

在这里插入图片描述

2.5特性(ACID)

原子性:一个事务要么全部成功,全部失败,不会停在某个阶段
一致性 :事务的前后处于一致状态
隔离性:多个事务同时操作相同数据库的同一个数据时,一个事务的执行不受另外一个事务的干扰
持久性:一个事务一旦提交,则数据将持久化到本地,除非其他事务对其进行修改

2.6应用

1.注解作用域

事务常用注解

  1. @EnableTransactionMannagement
    表示spring开启注解事务配置的支持
    2.@Transactional
    spring注解配置事务的核心注解,可以出现在接口、类、方法上
    3.@TransactionEventListener
    用于配置事务的监听器,使我们在事务提交和回滚前后可以做一些额外的功能

2.分类

1.声明式事务

1)基于注解方式实现(常用)
2)基于xml配置文件方式实现
注意:在Spring中进行声明式事务管理,底层使用的是AOP编程式事务

2.编程式事务

3.ioc

	对于某个具体的对象而言,以前是由自己控制它所引用对象的生命周期,而在IOC中,所有的对象都被 Spring 控制,控制对象生命周期的不再是引用它的对象,而是Spring容器,由 Spring 容器帮我们创建、查找及注入依赖对象,而引用对象只是被动的接受依赖对象,所以这叫控制反转。

3.1IOC的原理

Spring 的 IoC 的实现原理就是工厂模式加反射机制,而在 Spring 容器中,Bean 对象如何注册到 IoC 容器,

3.2 循环依赖

  1. 什么是循环依赖
    循环依赖是指两个或多个Bean之间相互依赖的情况,形成了一个闭环依赖关系。比如说
    Bean A正在创建,并且需要引用Bean B。
    Bean B也正在创建,并且需要引用Bean A。
    由于Bean A尚未完成创建,无法提供给Bean B,同时Bean B也无法提供给Bean A。

  2. 如何解决
    在Spring框架中,默认情况下是不支持循环依赖的,因为它会导致无限递归或对象引用不完整的问题。然而,Spring提供了一些机制来解决或预防循环依赖问题:
    主要的解决逻辑就是对对象创建分层,以及对象创建的时机

  3. 提前暴露:spring容器会提前解析到次创建对象会出现循环依赖的情况了,因此先去创建对象,先不给属性赋值,创建完对象后再去给属性赋值。

  4. 使用@Lazy注解:延迟bean的加载。这样,当循环依赖发生时,Spring会返回一个代理对象,以延迟依赖注入的完成,从而打破循环依赖。

  5. 重新设计应用程序结构:如果出现复杂的循环依赖问题,可能需要重新设计应用程序的结构,以减少或避免循环依赖的发生。

4.aop

4.1代理模式

1.静态代理

AspectJ静态代理(也称编译增强)
AOP框架会在编译阶段生成AOP代理类,并将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

2.动态代理

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

① JDK动态代理只提供接口的代理,不支持类的代理,要求被代理类实现接口。JDK动态代理的核心是InvocationHandler接口和Proxy类,在获取代理对象时,使用Proxy类来动态创建目标类的代理类(即最终真正的代理类,这个类继承自Proxy并实现了我们定义的接口),当代理对象调用真实对象的方法时, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理对象; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。
② 如果被代理类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

  1. 静态代理与动态代理的区别???

静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

4.2动态代理技术

动态代理技术分为jdk动态代理和cglib动态代理
1.jdk动态代理
jdk动态代理是不需要第三方库的支持,只需要jdk环境就可以进行代理,使用条件:

1.业务目标对象实现接口
2.实现InvocationHadler接口
3.使用proxy.newproxyInstance( )方法生成代理对象
JDK动态代理有个限制,只能为接口创建代理实例
JDK动态代理是基于拦截器和反射来实现的

2.cglib动态代理
通过继承的方式实现代理,在子类中采用拦截技术拦截所有父类的方法的调用并顺势植入横切逻辑

3.spring中何时使用JDK或Cglib

如果目标对象实现了接口默认采用jdk动态代理
如果目标对象实现了接口,可以强制使用Cglib
如果目标对象没有实现接口,必须采用Cglib,spring会自动在JDK动态代理与Cglib之间转换

4.3介绍

Aop 是面向切面编程,AOP的的核心是切面。AOP在不修改源代码本身的前提下使用运行时的动态代理技术对已有的代码逻辑增强。AOP可以实现组件化,可拔插式的功能扩展,通过简单配置即可将功能增强到指定的切入点。可用于权限认证、日志、事务处理。

4.4术语

Target目标对象:被代理的原始对象
proxy代理对象:目标对象被织入通知后的产物
JoinPoint连接点:目标对象的所属类中定义的所有方法均为连接点
PointCut切入点:被切面拦截增强的连接点(切入点一定是连接点,连接点不一定是切入点)
Advice通知:增强的逻辑代码,即拦截到目标对象的连接点之后要做的事情
Aspect 切面:切入点+通知
introduction引介:特殊的通知类型,可以在不修改原有类的代码的前提下,在运行期为原始类动态添加新的属性/方法

4.5 原理

Aop的底层是由运行时动态代理支撑,在bean初始化流程中,借助BeanPostProcessor(后置处理器)将原始目标对象织入通知,生成代理对象,AOP设计原理是对原有的业务逻辑进行横切增强,使用不同的通知织入方式,她有不同的底层原理支撑(编译期,类加载器,对象创建期)

4.6执行时机

@Before前置通知:目标对象的方法调用之前触发
@After 后置通知:目标对象的方法调用之后触发
@AfterReturning 返回通知:目标对象的方法调用完成,在返回结果之后触发
@AfterThrowing 异常通知:目标对象的方法运行中抛出/触发异常后触发
@Around环绕通知:编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,他可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事甚至不调用目标对象的方法
注意:AfterReturning与AfterTrowing两者是互斥的!如果方法调用成功无异常,则会有返回值;如果方法抛出了异常,则不会有返回值

4.7 aop的实现过程

1.首先创建一个java类作为切面。使用@Aspect注解进行标识,并使用@Comment 注解将切面对象加入到bean容器中,需要使用@Comment是因为@Advice是java的注解spring 无法直接识别到,因此需要@Comment注解将切面对象加入到spring中。然后再使用@Pointcut注解定义切入点,支持三种切入点的用法,分别是使用方法签名,通配符的方式,自定义注解的方式进行。再结合执行时机@Before前置通知、@After后置通知、@Around环绕通知、@AfterReturning返回通知 、@AfterThrowing异常通知。声明执行的时机在切入点的前后做操作。spring会自动注入桥接对象(代表目标对象) 使用ProceedingJoinPoint对象的proceed()方法显式地执行目标方法。

@Pointcut
当使用Spring AOP的@Pointcut注解时,有几种常用的用法。下面是一些常见的@Pointcut用法示例:

  1. 使用方法签名:可以通过方法签名来定义切入点,表示匹配特定的方法。
@Pointcut("execution(public void com.example.MyService.myMethod())")
public void myMethodPointcut() {}

上述示例中的切入点myMethodPointcut()匹配了com.example.MyService类中的myMethod()方法。

  1. 使用通配符:可以使用通配符来模糊匹配类名、方法名和参数。
@Pointcut("execution(public * com.example.*.*Service.*(..))")
public void serviceMethodsPointcut() {}

上述示例中的切入点serviceMethodsPointcut()匹配了com.example包下所有以Service结尾的类中的任意方法。

  1. 使用注解:可以使用注解来标记目标方法,并通过@within@annotation来匹配带有特定注解的方法。
@Pointcut("@annotation(com.example.MyAnnotation)")
public void annotatedMethodsPointcut() {}

@Pointcut("@within(com.example.MyAnnotation)")
public void withinAnnotatedClassesPointcut() {}

上述示例中的切入点annotatedMethodsPointcut()匹配了被com.example.MyAnnotation注解标记的方法,而切入点withinAnnotatedClassesPointcut()匹配了类上标记有com.example.MyAnnotation注解的所有方法。

桥接对象

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    @Around("execution(* com.example.MyService.*(..))")
    public Object aroundMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method execution");
        
        // 执行目标方法
        Object result = joinPoint.proceed();
        
        System.out.println("After method execution");
        
        return result;
    }
}

使用@Around注解来标记aroundMethodExecution()方法作为环绕通知。在方法体内部,我们可以自定义处理逻辑,并使用ProceedingJoinPoint对象的proceed()方法显式地执行目标方法。

5 mvc

5.1五个组件

		前端控制器DispatcherServlet
		HandlerMapping处理器映射器
		HandlerAdapter处理器适配器
		ModelAndView
		ViewReslover视图解析器

5.2 运行流程

客户端发送HTTP请求到前端控制器DispatcherServlet。
DispatcherServlet根据请求的URL和配置的请求映射规则,将请求转发给对应的处理器(Controller)。
处理器(Controller)接收请求并进行相应的业务处理,包括数据处理、调用服务层方法等。
处理器(Controller)处理完业务逻辑后,通常会返回一个逻辑视图名或ModelAndView对象,表示要展示的视图以及传递给视图的数据。
DispatcherServlet根据配置的视图解析器(ViewResolver),将逻辑视图名解析为实际的视图对象。
视图对象根据数据模型和模板文件,生成最终的视图内容。
DispatcherServlet将生成的视图内容返回给客户端,完成请求的响应。

在整个流程中,前端控制器DispatcherServlet起到了中心协调的作用,负责接收请求、路由请求、选择处理器(Controller)、选择视图解析器(ViewResolver)、渲染视图等。处理器(Controller)负责处理具体的业务逻辑,包括数据处理、调用服务层方法等。视图解析器(ViewResolver)负责将逻辑视图名解析为实际的视图对象。视图对象根据数据模型和模板文件生成最终的视图内容,并由DispatcherServlet返回给客户端。

6.常用注解

@Controller - 用于 Spring MVC 项目中的控制器类。
@Service - 用于服务类。
@RequestMapping - 用于在控制器处理程序方法中配置 URI 映射。
@ResponseBody - 用于发送 Object 作为响应,通常用于发送 XML 或 JSON 数据作为响应。
@PathVariable - 用于将动态值从 URI 映射到处理程序方法参数。
@Autowired - 用于在 spring bean 中自动装配依赖项。
@Qualifier - 使用 @Autowired 注解,以避免在存在多个 bean 类型实例时出现混淆。
@Scope - 用于配置 spring bean 的范围。
@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。
@Aspect,@Before,@After,@Around,@Pointcut - 用于切面编程(AOP)。

7.RestFul风格

Restful风格是一种设计和构建Web服务的风格,它基于HTTP协议,并遵循一些规范和原则。下面是一些使用Restful风格的基本指导原则和示例:

  1. 使用HTTP方法来表示操作:

    • GET:用于获取资源或执行查询操作。
    • POST:用于创建新资源。
    • PUT:用于更新现有资源。
    • DELETE:用于删除资源。
  2. 使用URI来表示资源:

    • 每个资源都有唯一的标识URI,例如:/users、/products/1。
    • 使用URI路径来表示资源的层级结构。
  3. 使用HTTP状态码来表示操作结果:

    • 200 OK:表示请求成功。
    • 201 Created:表示资源创建成功。
    • 204 No Content:表示请求成功,但无响应内容。
    • 400 Bad Request:表示请求错误。
    • 404 Not Found:表示资源未找到。
  4. 使用请求和响应的内容类型:

    • 请求时使用Accept头部来指定期望的响应内容类型。
    • 响应时使用Content-Type头部来指定响应的内容类型。
  5. 使用URL参数进行过滤和排序:

    • 使用查询参数来进行资源的过滤、排序、分页等操作,例如:/users?name=John&sort=age&limit=10。
  6. 使用HTTP状态转移(HATEOAS):

    • 在响应中提供相关链接,使客户端能够发现和访问其他相关资源。

以下是一个使用Restful风格设计用户管理接口的示例:

GET /users
- 获取所有用户

GET /users/{id}
- 获取指定ID的用户

POST /users
- 创建新用户

PUT /users/{id}
- 更新指定ID的用户

DELETE /users/{id}
- 删除指定ID的用户
  1. 使用的相关注解
    @PathVariable(“id”)
@GetMapping("user/{id}")
public void getUser(@PathVariable("id") Integer id){

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值