Spring+SpringBoot面试总结(近两万字)

spring框架有一系列模块
使用 Spring 进行开发各种配置过于麻烦比如开启某些 Spring 特性时,需要用 XML 或 Java 进行显式配置。于是,Spring Boot 诞生了!
Spring 旨在简化 J2EE 企业应用程序开发。Spring Boot 旨在简化 Spring 开发(减少配置文件,开箱即用!)。
但是springboot只是简化了配置,但是如果需要构建 MVC架构的Web程序还是需要使用SpringMVC

SpringBoot项目的搭建

通过Idea脚手架搭建

新建一个springboot项目 可以更改Url改为国内的 可以选择使用maven 也可选择jdk版本和springboot版本 可设置group artifact 这个会影响在仓库中的位置
后续添加web依赖 就可以直接运行启动类
spring-boot-maven-plugin插件是将项目打包成jar包的插件。该插件打包后的SpringBoot项目无需依赖web容器,可以直接使用JDK运行

启动类一定要放在最外层包下面,因为项目启动后,加载文件加载包都是在启动类的同级目录下的

配置文件

由于SpringBoot极大简化了Spring配置,所以只有一个application.properties配置文件,且Spring的自动配置功能使得大部分的配置都有默认配置,该文件的功能是覆盖默认配置信息,该文件不写任何信息都可以启动项目。启动后默认端口号为8080,我们可以覆盖该配置:

SpringlOC的理解,原理与实现?

lOC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。lOC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理
控制 对象创建的能力 反转 控制权交给外部环境
传统的方法需要我们手动new出来,使用IOC只需要容器帮助实例化对象

IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可。 IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
Spring中通过xml配置 SpringBoot中通过注解配置

IOC这种设计思想由DI实现
DI: 依赖注入,把对应的属性的值注入到具体的对象中

容器
存储对象,使用map结构存储,在spring一般存在三级缓存,
整个bean的生命周期从创建到销毁都是由容器管理

接下来涉及到bean的生命周期

底层实现
也就跟bean的生命周期有关
在这里插入图片描述
q 底层实现与bean的生命周期有关 有待整理

循环依赖问题

什么是循环依赖问题

A依赖B B依赖A(A里面有属性B,B里面有属性A)
先创建A对象,实例化A对象,此时A对象的b属性为空,填充属性b
从容器中查找B对象,如果找到了就不存在循环依赖问题(因为可以直接赋值可)找不到就需要创建B对象
实例化B对象此时B对象中的a属性为空 ,需要填充a属性
所以再又从容器中找A对象,但是找不到(因为上面创建的A对象还是在实例化阶段,)
这就是完整的循环依赖问题

解决思路

此时分析会发现A对象是存在的,只不过此时的A对象不是一个完整的状态,只完成了实例化但是未完成初始化,

如果在程序调用过程中,拥有了某个对象的引用,能否在后期给他完成赋值操作,可以优先把非完整状态的对象优先赋值,等待后续操作来完成赋值,相当于提前暴露了某个不完整对象的引用,

所以解决问题的核心在于实例化和初始化分开操作,这也是解决循环依赖问题的关键,

当所有的对象都完成实例化和初始化操作之后,还要把完整对象放到容器中
此时在容器中存在对象的几个状态,完成实例化但未完成初始化,完整状态,因为都在容器中,所以要使用不同的map结构来进行存储,此时就有了一级缓存 和二级缓存
如果一级缓存中有了,那么二级缓存中就不会存在同名的对象,
一级缓存中放的是完整对象,二级缓存中放的是非完整对象
在这里插入图片描述

为什么需要三级缓存

一二级缓存能够解决普通对象的循环依赖问题但是代理对象就不行
如果A对象是代理对象只用二级缓存就没那么好
通过这个第三级缓存,里面有对象工厂,能够决定产生代理对象还是普通对象
A就会生成一个代理工厂放到三级缓存,当B需要时就会由这个代理工 厂生成一个不完整的代理对象放到二级缓存
在这里插入图片描述

如果构造方法出现了循环依赖问题怎么解决

(因为先前的二级缓存存的是进行了实例化的半成品对象,即刚进行了构造函数)
在这里插入图片描述
可以开启延迟加载,即对象什么时候需要用什么时候采取创建对象,而不是在实例化的时候就把对象创建了

bean

spring项目的搭建(基于xml)

创建maven项目后。 添加 spring 框架支持 ( spring-context + spring-beans )
.将 bean 对象存储到 spring 容器中
(1) 在 spring 项目中添加配置文件
在 【 resources 】目录下创建【 spring-config.xml 】文件,并往里面放入代码。
(2) 创建一个 bean 对象
假设我们创建一个 User 类,并计划外面往里面传入一个 name 参数。
在这里插入图片描述

在这里插入图片描述
启动类的相关配置
在这里插入图片描述

q 基于注解和configuration管理bean

Spring容器的创建

①BeanFactory
这是 IOC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员用。
②ApplicationContext
BeanFactory 的子接口,最常用

我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
org.springframework.beans和 org.springframework.context 这两个包是 IoC 实现的基础

基于XML管理bean

入门案例
在这里插入图片描述
注意:此时获取的bean是没有属性值

依赖注入之setter注入
首先需要有一个类,这里假定是Student类,有属性和get和set方法(默认是无参构造器)
配置bean时为属性赋值

<bean id="studentOne" class="com.atguigu.spring.bean.Student">
<!-- property标签:通过组件类的setXxx()方法给组件对象设置属性 -->
<!-- name属性:指定属性名(这个属性名是getXxx()、setXxx()方法定义的,和成员变量无关)
-->
<!-- value属性:指定属性值 -->
<property name="id" value="1001"></property>
<property name="name" value="张三"></property>
<property name="age" value="23"></property>
<property name="sex" value="男"></property>
</bean>

依赖注入之构造器注入
首先在之前Student类的基础上添加有参构造器
配置bean

<bean id="studentTwo" class="com.atguigu.spring.bean.Student">
<constructor-arg value="1002"></constructor-arg>
<constructor-arg value="李四"></constructor-arg>
<constructor-arg value="33"></constructor-arg>
<constructor-arg value="女"></constructor-arg>
</

为类类型属性赋值 (在student类中有clazz类)
创建Clazz类和Student类后,在Student类中添加返回类型为Clazz的set和get方法
首先在XML中定义Clazz的Bean对象

<bean id="clazzOne" class="com.atguigu.spring.bean.Clazz">
<property name="clazzId" value="1111"></property>
<property name="clazzName" value="财源滚滚班"></property>
</

为Student中的clazz属性赋值:

<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
</bean>

为数组类型属性赋值
Student类中添加数组属性

②配置bean

<bean id="studentFour" class="com.atguigu.spring.bean.Student">
<property name="id" value="1004"></property>
<property name="name" value="赵六"></property>
<property name="age" value="26"></property>
<property name="sex" value="女"></property>
<!-- ref属性:引用IOC容器中某个bean的id,将所对应的bean为属性赋值 -->
<property name="clazz" ref="clazzOne"></property>
<property name="hobbies">
<array>
<value>吃饭</value>
<value>睡觉</value>
<value>打豆豆</value>
</array>
</property>
</bean>

基于注解管理bean

注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
使用组件要先扫描才能找到注解(后面有补充)
使用注解后,每个组件仍然应该有一个唯一标识
可以将@Autowired注解加在成员变量,构造器 或者set方法上 这样都可以自动装配所需的对象

@Autowired工作流程
在这里插入图片描述

javaConfig管理Bean

在这里插入图片描述

bean的作用域

在Spring中可以通过配置bean标签的scope属性来指定bean的作用域范围

singleton:单例模式,即每个容器中只存在一个 Bean 实例。默认情况下,Spring 的 Bean 是单例的,即在容器启动时就创建好了,并在整个应用程序的生命周期中共享。

prototype:原型模式,即每次从容器中获取 Bean 时都会创建一个新的实例。每次获取 Bean 都会返回一个新的对象,这意味着 Bean 对象不会被共享。

request:针对每一次 HTTP 请求创建一个新的 Bean 实例。每个 HTTP 请求都会创建一个新的 Bean 实例,并且在当前请求结束后销毁。

session:针对每一次 HTTP 会话创建一个新的 Bean 实例。每个 HTTP 会话都会创建一个新的 Bean 实例,并且在当前会话结束后销毁。

globalSession:针对全局 HTTP 会话(通常只在基于 Portlet 的 Web 应用中可用)创建一个新的 Bean 实例。

如何配置 bean 的作用域呢?

xml 方式:<bean id="..." class="..." scope="singleton"></bean>
注解方式:

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

Bean 是线程安全的吗?

Bean 是否线程安全,取决于其作用域和状态。
prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个 bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。不过,大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:在 Bean 中尽量避免定义可变的成员变量。在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。Bean 的

bean的生命周期(对象的创建使用销毁)

准备工作

1.加载Bean定义
通过 loadBeanDefinitions 扫描所有xml配置、注解等将Bean记录在beanDefinitionMap
(创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义,然后使用 Java 反射 API 来创建 Bean 的实例。)

对于BeanDefinition的补充

Spring容器在进行实例化时,会将xml配置的<bean>的信息封装成一个BeanDefinition对象,Spring根据BeanDefinition来创建Bean对象,里面有很多的属性用来描述Bean
相关属性
beanClassName: bean 的类名
initMethodName:初始化方法名称
properryValues:bean 的属性值
scope:作用域
lazyInit:延迟初始化

创建Bean对象

通过 createBean 遍历 beanDefinitionMap 开始创建bean对象
具体步骤是
创建对象,填充属性,初始化示例,注册销毁

实例化
通过反射的方式生成对象
容器通过 createBeanInstance方法 进行对象构造,使用反射机制从Bean定义的BeanClass拿到类的构造方法(对于有多个构造方法的情况是,如果有@Autowired修饰的就优先拿),通过反射完成类的构造(此时Bean对象构造成功)
(Bean 属性赋值/填充:为 Bean 设置相关属性和依赖,例如@Autowired 等注解注入的对象、@Value 注入的值、setter方法或构造函数注入依赖和值、@Resource注入的各种资源。)

填充属性
通过populateBean(),方法为bean所需要的属性进行填充,对于**@Autowired修饰的变量就会根据三级缓存进行依赖注入
调用
aware**接口相关的方法:invokeAwareMethod(完成BeanName,BeanFactory,BeanClassLoader对象的属性设置,可以实现这些接口就能拿到这些参数,让 Bean 对其所属的容器或环境有意识(Awareness)比如BeanNameAware:允许 Bean 获取到自己在容器中的名称(Bean 的 ID)。通过实现该接口,Bean 可以在初始化过程中获知自己在容器中的名称,以及对这个名称进行一些操作)

Bean 初始化
初始化示例
通过InitializingBean方法对这个实例进行初始化
初始化容器信息,通过invokeAwareMethod方法
初始化构造方法

如果Bean实现InitializingBean接口进行处理【未实现则不进行】
通过 invokeInitMethods 方法进行初始化:为实现了各种Aware接口(信息感知接口)的Bean设置诸如beanName、oeanFactory等容器信息(可以在bean实例中感知获取到对应的信息)

通过invokelnitMethods方法执行Bean的初始化方法(通过实现imnitializingBean接口而实现的afterPropertiesset方法(自己实现的 表示在Bean填充属性后执行)) 然后如果有标注的initMethod方法就会再执行这个方法

在初始化方法执行前后还会执行BeanPostProcessors(Bean后置处理器,继承这个接口实现自己的处理器)

(如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。)
Bean的后置处理
可以实现BeanPostProcessor接口重写其中的postProcessBeforeInitialization(前置) postProcessAfterInitialization(后置)
在invokeInitMethods 的前后进行

在后置处理中处理了包括:AOP

注册销毁

Spring Bean 的销毁流程主要包括两个阶段:销毁前和销毁后。

  1. 销毁前阶段:

    • 当 Spring 容器关闭时,会触发 Bean 的销毁工作。
    • 首先,会调用实现了 DisposableBean 接口的 destroy() 方法。该方法允许在销毁 Bean 之前做一些清理工作,例如释放资源等。
    • 然后,会调用自定义的销毁方法。我们可以在 Bean 中通过在方法上使用 **@PreDestroy** 注解,或在配置文件中使用 <destroy-method> 指定自定义的销毁方法。这些方法会在调用 destroy() 方法之后被调用。
  2. 销毁后阶段:

    • 在销毁 Bean 后,会调用实现了 BeanPostProcessor 接口的 postProcessBeforeDestruction() 方法。该方法允许在销毁 Bean 之后进行一些额外的处理。
    • 最后,会将销毁的 Bean 从 Spring 容器中移除。

需要注意的是,当 Bean 的作用域为原型(prototype)时,Spring 容器不会负责销毁这些 Bean。因此,我们需要自己确保在不再使用一个原型 Bean 时,手动进行销毁。可以通过自定义销毁方法或使用其他方式来实现。

q bean的生命周期 需要配合javaguide

spring 自动装配 bean 有哪些方式

xml

第一种 no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean

第二种 byName:通过bean的名称进行自动装配,(会自动搜索有没有与XXX同名的SetXXX)

byType:通过参数的数据类型进行自动装配

constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配

注解扫描方式

XML配置方式:
可以在Spring的XML配置文件中使用context:component-scan标签来启用组件扫描,并指定要扫描的包路径。

示例:

<context:component-scan base-package="com.example.app" />

基于Java Config的方式:
可以使用@Configuration注解的Java类作为配置类,通过在配置类中使用@ComponentScan注解来启用组件扫描,并指定要扫描的包路径。

示例:

@Configuration
@ComponentScan(basePackages = "com.example.app")
public class AppConfig {
    // 配置其他Bean等
}
基于注解的方式:
可以将@ComponentScan注解直接添加到Spring Boot应用程序的主类上,从而启用组件扫描,并指定要扫描的包路径。

示例:

@SpringBootApplication
@ComponentScan(basePackages = "com.example.app")
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

三、Spring AOP

AOP作用:与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码

在Spirng当中动态代理的使用
如果目标对象实现了接口,默认情况下会采用JDK的动态代理来实现AOP ,也可以强制使用CGlib来实现AOP
如果目标对象没有实现接口,必须采用Cglib库,Spirng会自动在JDK和CGlib用切换

Cglib动态代理
Cglib是动态代理对代理对象的Class文件加载进来,通过修改其字节码生成的子类来处理 , Cglib是基于继承父类生成的代理类.

JDK动态代理:
实现InvocationHandler接口,然后重写invoke方法
newProxyInstance方法中,通过传入被代理的对象objectTarget,使用Proxy.newProxyInstance方法创建了一个代理对象。
在invoke方法中,它是在调用代理对象的方法时被调用的。在该方法中可以写需要添加的操作,然后通过反射调用了被代理对象的相应方法,并将结果返回。
对于目标方法是通过反射来调用的而Cglib是直接调用父类对应的方法

3.1、代理的实现

3.1.1、静态代理

通过将目标类和代理类实现相同的接口,让代理类持有真实类对象,然后在代理类方法中调用真实方法,在调用真实方法的前后增加我们需要的功能来达到增强的功能。
缺点:代码冗余,不利维护。

3.1.2、动态代理

public class JdkProxy implements InvocationHandler {
 
    private Object objectTarget;
 
    public Object newProxyInstance(Object objectTarget){
        this.objectTarget = objectTarget;
        return Proxy.newProxyInstance(objectTarget.getClass().getClassLoader(), objectTarget.getClass().getInterfaces(), this);
    }
    /**
     *
     * @param proxy 被代理的对象
     * @param method 要调用的方法
     * @param args 方法调用时所需要的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        try{
            System.out.println("通过JDK动态代理调用前" + method.getName());
            result = method.invoke(objectTarget, proxy);
            System.out.println("通过JDK动态代理调用后" + method.getName());
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
}

q 动态代理怎么实现的,接上文

通知类型

Before(前置通知):目标对象的方法调用之前触发
After (后置通知):目标对象的方法调用之后触发
AfterReturning(返回通知):目标对象的方法调用完成,在返回结果值之后触发
AfterThrowing(异常通知):目标对象的方法运行中抛出 / 触发异常后触发。
返回通知和异常通知互斥
Around (环绕通知):编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法

3.2、AOP使用

基于注解的AOP

待实现类

@Component
public class CalculatorPureImpl implements Calculator {
	@Override
	public int add(int i, int j) {
		int result = i + j;
		System.out.println("方法内部 result = " + result);
		return result;
	}
	@Override
	public int sub(int i, int j) {
		int result = i - j;
		System.out.println("方法内部 result = " + result);
		return result;
}

创建切面类并配置

// @Aspect表示这个类是一个切面类
@Aspect
// @Component注解保证这个切面类能够放入IOC容器
@Component
public class LogAspect {
@Before("execution(public int com.atguigu.aop.annotation.CalculatorImpl.*
(..))")
//表示匹配 com.atguigu.aop.annotation.CalculatorImpl 类中任意公共方法的执行。
public void beforeMethod(JoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	String args = Arrays.toString(joinPoint.getArgs());
	System.out.println("Logger-->前置通知,方法名:"+methodName+",参
	数:"+args);
}
@After("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("Logger-->后置通知,方法名:"+methodName);
}
@AfterReturning(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("Logger-->返回通知,方法名:"+methodName+",结
	果:"+result);
}
@AfterThrowing(value = "execution(*
com.atguigu.aop.annotation.CalculatorImpl.*(..))", throwing = "ex")
public void afterThrowingMethod(JoinPoint joinPoint, Throwable ex){
	String methodName = joinPoint.getSignature().getName();
	System.out.println("Logger-->异常通知,方法名:"+methodName+",异常:"+ex);
}
@Around("execution(* com.atguigu.aop.annotation.CalculatorImpl.*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint){
	String methodName = joinPoint.getSignature().getName();
	String args = Arrays.toString(joinPoint.getArgs());
	Object result = null;
	try {
		System.out.println("环绕通知-->目标对象方法执行之前");
		//目标对象(连接点)方法的执行
		result = joinPoint.proceed();
		System.out.println("环绕通知-->目标对象方法返回值之后");
		} catch (Throwable throwable) {
			throwable.printStackTrace();
			System.out.println("环绕通知-->目标对象方法出现异常时");
		} finally {
			System.out.println("环绕通知-->目标对象方法执行完毕");
		}
		return result;
		}
}

SpringMVC的执行流程

SpringMVC核心组件

DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应。
HandlerMapping:处理器映射器,根据 URL 去匹配查找能处理的 Handler ,并会将请求涉及到的拦截器和 Handler 一起封装。HandlerAdapter:处理器适配器,根据 HandlerMapping 找到的 Handler ,适配执行对应的 Handler;
Handler:请求处理器,处理实际请求的处理器。
ViewResolver:视图解析器,根据 Handler 返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给 DispatcherServlet 响应客户端

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 返回给请求者(浏览器)

q 这个视图是什么

统一异常处理

使用@ControllerAdvice + @ExceptionHandler注解

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    @ExceptionHandler(BaseException.class)
    public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
      //......
    }

    @ExceptionHandler(value = ResourceNotFoundException.class)
    public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
      //......
    }
}

这种异常处理方式下,会给所有或者指定的 Controller 织入异常处理的逻辑(AOP),当 Controller 中的方法抛出异常的时候,由被@ExceptionHandler 注解修饰的方法进行处理。可在@ControllerAdvice指定只作用指定的controller

6.2、SpringMVC能干什么

灵活的URL映射:可以将URL请求映射到对应的控制器处理方法,支持多种URL映射模式。(注解相关)
请求参数绑定:可以将请求参数自动绑定到控制器方法的参数中,简化参数的获取和处理。(注解相关)
数据验证:提供了数据验证机制,可以通过注解和验证器对请求数据进行验证和校验。
视图解析与渲染:支持多种视图解析器,可以根据请求的结果选择不同的视图进行渲染,如JSP、Thymeleaf、Freemarker等。
拦截器:提供了拦截器机制,可以在请求处理前后执行一些共享的预处理和后处理逻辑。
文件上传:支持文件上传功能,可以方便地处理文件上传的请求和处理。
异常处理:提供了全局的异常处理机制,可以统一处理应用程序中的异常,返回适当的错误页或错误信息。

Spring事务

程序是否支持事务取决于使用的数据库

如果事务设置为只读属性
@Transactional(readOnly = true)就只能进行读操作
在其中进行了增删改操作就会抛出异常
@Transactional(timeout = 3)
超时回滚 释放资源
也可以设置回滚策略
@Transactional(noRollbackFor = ArithmeticException.class)比如就是发生算数时异常不回滚

@Transactional注解的参数

propagation事务的传播行为,默认值为 REQUIRED,可选的值在上面介绍过
isolation事务的隔离级别,默认值采用 DEFAULT,可选的值在上面介绍过
timeout事务的超时时间,默认值为-1(不会超时)。如果超过该时间限制但事务还没有完成,则自动回滚事务。
readOnly指定事务是否为只读事务,默认值为 false。
rollbackFor用于指定能够触发事务回滚的异常类型,并且可以指定多个异常类型。

Spring事务的传播行为

REQUIRED:如果当前存在事务,则方法在该事务中执行;如果当前没有事务,则创建一个新的事务。
SUPPORTS:如果当前存在事务,则方法在该事务中执行;如果当前没有事务,则以非事务的方式执行。
MANDATORY:方法必须在一个已存在的事务中执行,否则将抛出异常。
REQUIRES_NEW:创建一个新的事务,并挂起当前事务(如果有的话)。
NOT_SUPPORTED:以非事务的方式执行方法,如果当前存在事务,则挂起该事务。
NEVER:以非事务的方式执行方法,如果当前存在事务,则抛出异常。
NESTED:如果当前存在事务,则在嵌套事务中执行;如果当前没有事务,则表现行为类似于 REQUIRED。

REQUIRED:如果当前存在事务的解释就是调用method2的主体是否存在事务

@Transactional
public void method1() {
    // 事务处理逻辑
    method2();
}

@Transactional(propagation = Propagation.REQUIRED)
public void method2() {
    // 事务处理逻辑
}

Spring事务实现原理

@Transactional 的工作机制是基于 AOP 实现的,AOP 又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理,如果目标对象没有实现了接口,会使用 CGLIB 动态代理。

如果一个类或者一个类中的 public 方法上被标注@Transactional 注解的话,
Spring 容器就会在启动的时候为其创建一个代理类,在调用被@Transactional 注解的 public 方法的时候,实际调用的是TransactionInterceptor 类中的 invoke()方法。
这个方法的作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务。
在invoke方法里面实现目标方法的调用 以及开启事务回滚事务的处理
具体来说什么时候事务会回滚还是取决于数据库

Spring事务失效的场景

异常捕获处理,自己处理了异常,没有抛出,解决:手动抛出抛出检查异常,
配置rollbackFor属性为Exception(回滚时就能处理检查异常)
非public方法导致的事务失效,改为public

五、SpringBoot的自动装配

在Spring Boot项目中的引导类上有一个注解**@SpringBootApplication**,这个注解是对三个注解进行了封装,分别是:
@SpringBootConfiguration
@@EnableAutoConfiguration
@ComponentScan
其中**@EnableAutoConfiguration是实现自动化配置的核心注解。该注解通过@|mport注解导入对应的配置选择器。内部就是读取了该项目和该项目引用的Jar包的的classpath路径下META-INF/spring.factories文件中的所配置的类的全类名。 在这些配置类中所定义的Bean会根据条件注解所指定的**条件来决定是否需要将其导入到Spring容器中。
条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的cass文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。

5.1、使用和不使用SpringBoot在项目上有什么使用区别

1.配置管理

  • 不使用Spring Boot:在传统的Java项目中,需要手动配置和管理各种框架和组件的配置文件,如Spring配置文件、数据库配置文件等。
  • 使用Spring Boot:Spring Boot提供了自动配置的特性,它可以根据项目的依赖和约定,自动配置大部分常见的框架和组件,简化了配置的过程。同时,Spring Boot还提供了一种集中式的配置方式,使用application.properties或application.yml文件来集中管理配置,灵活且易于维护。

2.项目启动方式

  • 不使用Spring Boot:在传统的Java项目中,需要手动编写启动类或使用外部的web容器来启动项目。
  • 使用Spring Boot:Spring Boot内置了一个嵌入式的web服务器(如Tomcat、Jetty),可以直接通过运行主类的方式来启动Spring Boot项目,无需依赖外部的web容器。

3.依赖管理

  • 不使用Spring Boot:在传统的Java项目中,需要手动管理和解决各个框架和组件的版本兼容性,手动引入各个框架和组件的依赖。
  • 使用Spring Boot:Spring Boot提供了一系列的“Starter”依赖,用于集成和自动配置常用的框架和组件。只需引入相应的starter依赖,Spring Boot将自动解决版本兼容性和自动配置相关的依赖。

4.项目部署

  • 不使用Spring Boot:在传统的Java项目中,需要将依赖的框架和组件打包成WAR或JAR文件,并且手动配置和管理部署环境。
  • 使用Spring Boot:Spring Boot项目可以直接打包成可执行的JAR文件,包含所有的依赖和配置文件,简化了项目的部署过程。可以通过java -jar命令启动项目,也可以与Docker等容器技术结合使用,方便部署和扩展。

静态资源访问
需要引用大量的js css 图片等静态资源
可以在resource目录下创建static放图片,通过路径.图片就能访问到图片

基于你引入的依赖jar包,对SpringBoot应用进行自动装配
为SpringBoot框架的开箱即用提供了基础支撑、

5.2、SpringBoot自动装配原理

SpringBoot的启动流程(简化版)
在Springboot配置类中是有一个run方法

package com.hmdp;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.hmdp.mapper")
@SpringBootApplication
public class HmDianPingApplication {

    public static void main(String[] args) {
        SpringApplication.run(HmDianPingApplication.class, args);
    }

}
首先创建IOC容器,然后加载源配置类(源配置类就是使用@SpringBootApplication所修饰的类),接下来SpringBoot会自动找到所有的配置类

在这里插入图片描述
第三步的介绍
processConfigurationClasses方法
在这里插入图片描述
介绍parse方法
在这里插入图片描述
加载配置类的流程
从源配置类通过Parse方法不断递归的处理**@ComponentScan@Import注解,不断地去遍历新的配置类,直到没有新的配置类
处理每个配置类,将配置类本身
注册**到IOC容器中,处理配置类中的@Bean方法将其返回类型注册到IOC容器中
处理通过@Import导入的ImportBeanDefinitionRegistrar

在这里插入图片描述

6.3、如何解决 get 和 post 乱码问题?

解决post请求乱码问题:在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf­8;

6.3、过滤器和拦截器的区别

过滤器在Servlet容器中,如Tomcat、Jetty等,在请求到达Servlet之前执行预处理操作,然后在响应返回给客户端之前执行后处理操作。而拦截器则是在Spring MVC框架中,在请求被Controller处理之前和之后进行拦截处理。

过滤器对应用的所有资源产生影响
拦截器则是基于Bean的方式,可以对指定的Controller、方法或者路径进行拦截。

过滤器一般用于请求的预处理、响应的后处理、字符编码转换、请求的过滤等。拦截器除了提供类似过滤器的功能外,还可以在请求处理之前,通过HandlerInterceptor接口的preHandle方法进行权限验证、日志记录、数据绑定等操作

6.4、要将一个第三方的类配成为Bean有哪些方式?

第三方类由于无法更改不能加上@Conponent注解
所以只能在使用时操作@Bean(在返回的方法使用)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值