Spring

基础

1.什么是Spring框架

Spring是一个开源的轻量级java框架,可以提高开发效率,以及系统的可维护性

Spring Framework有很多模块可以方便我们的开发。比如Spring支持IOC、还有AOP、还支持对数据库方便的访问,可以方便的支持第三方的组件,支持单元测试,支持RestFul JAVA应用程序开发

Spring 官网:**https://spring.io/**
官方文档**:** https://docs.spring.io/spring-framework/reference/core.html

Github 地址: https://github.com/spring-projects/spring-framework

2.Spring的核心模块有哪些

Spring5.x

Core Container

Spring 框架的核心模块,也可以说是基础模块,主要提供 IoC 依赖注入功能的支持。Spring 其他所有的功能基本都需要依赖于该模块,我们从上面那张 Spring 各个模块的依赖关系图就可以看出来。

  • spring-core :Spring 框架基本的核心工具类。
  • spring-beans :提供对 bean 的创建、配置和管理等功能的支持。
  • spring-context :提供对国际化、事件传播、资源加载等功能的支持。
  • spring-expression :提供对表达式语言(Spring Expression Language) SpEL 的支持,只依赖于 core 模块,不依赖于其他模块,可以单独使用。

AOP

  • spring-aspects :该模块为与 AspectJ 的集成提供支持。
  • spring-aop :提供了面向切面的编程实现。
  • spring-instrument :提供了为 JVM 添加代理(agent)的功能。 具体来讲,它为 Tomcat 提供了一个织入代理,能够为 Tomcat 传递类文 件,就像这些文件是被类加载器加载的一样。没有理解也没关系,这个模块的使用场景非常有限。

Data Access/Integration

  • spring-jdbc :提供了对数据库访问的抽象 JDBC。不同的数据库都有自己独立的 API 用于操作数据库,而 Java 程序只需要和 JDBC API 交互,这样就屏蔽了数据库的影响。
  • spring-tx :提供对事务的支持。
  • spring-orm : 提供对 Hibernate、JPA 、iBatis 等 ORM 框架的支持。
  • spring-oxm :提供一个抽象层支撑 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。
  • spring-jms : 消息服务。自 Spring Framework 4.1 以后,它还提供了对 spring-messaging 模块的继承。

Spring Web

  • spring-web :对 Web 功能的实现提供一些最基础的支持。
  • spring-webmvc : 提供对 Spring MVC 的实现。
  • spring-websocket : 提供了对 WebSocket 的支持,WebSocket 可以让客户端和服务端进行双向通信。
  • spring-webflux :提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步。

Messaging

spring-messaging 是从 Spring4.0 开始新加入的一个模块,主要职责是为 Spring 框架集成一些基础的报文传送应用。

Spring Test

Spring 团队提倡测试驱动开发(TDD)。有了控制反转 (IoC)的帮助,单元测试和集成测试变得更简单。

Spring 的测试模块对 JUnit(单元测试框架)、TestNG(类似 JUnit)、Mockito(主要用来 Mock 对象)、PowerMock(解决 Mockito 的问题比如无法模拟 final, static, private 方法)等等常用的测试框架支持的都比较好。

3.Spring,SpringMVC,SpringBoot之间的关系

Spring一般是指SpringFrameWork,它是一个开源框架提供了很多模块例如Spring-Core提供了IOC、AOP的能力

SpringMVC是Spring框架的一个模块提供了快速构建MVC应用的能力

SpringBoot是一个基于Spring框架的脚手架,可以快速构建基于Spring的应用,简化了xml的文件配置,支持自动配置

IOC

4.谈谈自己对于SpringIOC的理解

IOC 就是控制反转,它思想就是转移对象依赖注入的控制权利,让Spring来管理对象,这样复杂的依赖关系就不需要手动进行维护,可以简化开发。Spring IOC实际上就可以看成一个对象工厂,需要用到对象的时候只需要从工厂中去取。一开始工厂中对象的创建是通过读取xml配置创建的并通过xml维护依赖关系,后来觉得xml配置比较麻烦,就有了SpringBoot的注解配置

5.什么是Spring的Bean

简单来说被SpringIOC容器管理的对象就是Bean

可以通过xml,注解或java配置类来告诉IOC容器我们要管理哪些对象

org.springframework.beans和 org.springframework.context 这两个包是 IoC 实现的基础,如果想要研究 IoC 相关的源码的话,可以去看看

6.将一个类声明成Bean的注解有哪些

  • @Component :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。
  • @Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
  • @Service : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
  • @Controller : 对应 Spring MVC 控制层,主要用于接受用户请求并调用 Service 层返回数据给前端页面。

7.@Component和@Bean的区别是什么

  • @Component注解作用与类,@Bean 作用与方法
  • @Component通过类路径扫描,将Bean装配到容器当中。@Bean 的自定义能力更强,使用第三方类需要装配到Spring容器中时,只能通过@Bean实现

    通过 @ComponentScan 注解来指定类扫描路径

    @Bean注解定义Bean示例
    1
    2
    3
    4
    5
    6
    7
    8
    @Configuration
    public class AppConfig {
    @Bean
    public TransferService transferService() {
    return new TransferServiceImpl();
    }

    }
    相当于xml的
    1
    2
    3
    <beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Bean
    public OneService getService(status) {
    case (status) {
    when 1:
    return new serviceImpl1();
    when 2:
    return new serviceImpl2();
    when 3:
    return new serviceImpl3();
    }
    }

8.注入Bean的注解有哪些

Spring内置的@Autowired,以及JDK内置的@Resource@Inject都可以用来注入Bean

Annotaion Package Source
@Autowired org.springframework.bean.factory Spring 2.5+
@Resource javax.annotation Java JSR-250
@Inject javax.inject Java JSR-330

9.@Autowired@Resource 有什么区别

@Autowired是 Spring的内置注解,默认通过byType 进行匹配,会优先根据接口类型进行匹配。如果有多个Bean实现了相同的接口,通过bytype进行匹配就会出现问题,Spring就不清楚到底使用哪个Bean进行注入。然后Spring会通过byName进行注入,默认是类名首字母小写。

1
2
3
4
5
6
7
8
9
10
11
// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

@Resource是JDK提供的注解,默认注入方式是byname如果不能通过名称进行匹配,注入方式就会变成bytype

@Resource有两个属性name 与 type

如果仅指定 name 属性则注入方式为byName,如果仅指定type属性则注入方式为byType,如果同时指定name 和type属性(不建议这么做)则注入方式为byType+byName

1
2
3
4
public @interface Resource {
String name() default "";
Class<?> type() default Object.class;
}
1
2
3
4
5
6
7
8
9
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

总结:

  • 注解来源不同 @Autowired是Spring的内置注解, @Resource是JDK提供的注解
  • 默认的依赖注入方式不同 @Autowired默认是bytype进行依赖注入,@Resource默认是通过byname进行依赖注入
  • 显示指定依赖注入的方式不同 当一个接口有多个实现类的情况下,@Autowired通过@Qualifier注解指定依赖注入的BeanName,@Resource通过name属性指定依赖注入的BeanName

10.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。

如何配置Bean的作用域

xml

1
<bean id="..." class="..." scope="singleton"></bean>

注解方式:

1
2
3
4
5
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
return new Person();
}

11.单例Bean的线程安全问题

单例Bean存在线程安全问题,当访问共享的成员变量时就会发生竞态条件

有以下解决方法:

  • 尽量使用方法的局部变量,避免定义成员变量。使用局部变量可以避免线程安全问题是因为每个线程都有自己的方法栈,局部变量是存在与每个线程自己的的方法栈当中,所以不会出现多个线程同时操作同一个变量的情况
  • 使用ThreadLocal成员变量,这样每个线程都能拥有自己的局部变量。可以在登陆拦截器当中使用ThreadLocal成员变量存储当前请求登陆的用户信息,在接下来使用当中直接从threadLocal当中取出当前登陆的用户

如果Bean是无状态的,Bean就是线程安全的

12.Bean的生命周期

https://www.cnblogs.com/zrtqsk/p/3735273.html

  • Bean 容器找到配置文件中 Spring Bean 的定义。
  • Bean 容器利用 Java Reflection API 创建一个 Bean 的实例。
  • 如果涉及到一些属性值 利用 set()方法设置一些属性值。
  • 如果 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 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
  • 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。

切面

13.谈一谈你对切面的理解

AOP(Aspect-Oriented Programming)就是面向切面编程,可以将业务无关的代码封装起来,例如日志、事务处理、权限处理的代码封装起来,减少系统重复代码,降低系统耦合度,有利于未来的扩展性和可维护性。

SpringAOP是基于动态代理的,如果实现了接口就会使用JDK Proxy来创建代理对象,如果没有实现接口就是用cglib生成被代理对象的子类来作为代理

术语 含义
目标(Target) 被通知的对象
代理(Proxy) 向目标对象应用通知之后创建的代理对象
连接点(JoinPoint) 目标对象的所属类中,定义的所有方法均为连接点
切入点(Pointcut) 被切面拦截 / 增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice) 增强的逻辑 / 代码,也即拦截到目标对象的连接点之后要做的事情
切面(Aspect) 切入点(Pointcut)+通知(Advice)
Weaving(织入) 将通知应用到目标对象,进而生成代理对象的过程动作

14.Spring AOP 与AspectJ AOP有什么区别

Spring AOP 和 AspectJ AOP 是两种不同的 AOP 实现方式,它们在使用上有一些区别,尽管都是用于在应用程序中处理交叉关注点的。

  1. 织入时机
    • Spring AOP:织入发生在运行时(runtime)。Spring AOP 使用代理对象包装目标对象,以便在方法调用前后添加通知。
    • AspectJ AOP:织入可以发生在编译时(compile-time)、加载时(load-time)或运行时(runtime)。这意味着 AspectJ 提供了更广泛的织入时机选择。
  2. 语法和功能
    • Spring AOP:使用纯粹的 Java 注解或 XML 配置来定义切面和通知。Spring AOP 对 AspectJ 的功能进行了一定程度的限制,主要关注于方法级别的切面。
    • AspectJ AOP:使用自己的专有语法和注解来定义切面和通知。AspectJ 提供了更丰富的功能,允许更细粒度的切入点定义,以及更强大的通知类型(如前置、后置、环绕、异常、最终等)。
  3. 性能
    • Spring AOP:由于其基于动态代理的特性,可能在大规模应用中略有性能开销。
    • AspectJ AOP:由于可以在编译时进行织入,因此可能更接近原生方法调用的性能。
  4. 使用场景
    • Spring AOP:适用于基本的交叉关注点处理,如日志记录、事务管理等。适合于那些希望与 Spring 框架集成并且需要轻量级AOP的应用。
    • AspectJ AOP:适用于更复杂的交叉关注点处理,如性能监控、复杂的权限控制等。适合于那些需要更强大和灵活的AOP功能的应用。

总结:

  • Spring AOP属于运行时增强,AspectJ属于编译时增强。Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

15.AspectJ的通知类型有哪些

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

16.多个切面的顺序如何控制

1.通过使用@Order注解直接定义切面的顺序

1
2
3
4
5
// 值越小优先级越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {

2.实现Order接口,重写getOrder方法

1
2
3
4
5
6
7
8
9
10
11
12
@Component
@Aspect
public class LoggingAspect implements Ordered {

// ....

@Override
public int getOrder() {
// 返回值越小优先级越高
return 1;
}
}

SpringMVC

17.说说对SpringMVC的了解

组织代码的方式,Model是模型、View是视图、Controller是控制器

mvc的发展阶段

Spring MVC 时代

随着 Spring 轻量级开发框架的流行,Spring 生态圈出现了 Spring MVC 框架, Spring MVC 是当前最优秀的 MVC 框架。相比于 Struts2 , Spring MVC 使用更加简单和方便,开发效率更高,并且 Spring MVC 运行速度更快。

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

18.SpringMVC的核心组件有什么

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

流程

  1. 客户端(浏览器)发送请求, DispatcherServlet拦截请求。
  2. DispatcherServlet 根据请求信息调用 HandlerMapping 。HandlerMapping 根据 uri 去匹配查找能处理的 Handler(也就是我们平常说的 Controller 控制器) ,并会将请求涉及到的拦截器和 Handler 一起封装。
  3. DispatcherServlet 调用 HandlerAdapter适配执行 Handler 。
  4. Handler 完成对用户请求的处理后,会返回一个 ModelAndView 对象给DispatcherServletModelAndView 顾名思义,包含了数据模型以及相应的视图的信息。Model 是返回的数据对象,View 是个逻辑上的 View
  5. ViewResolver 会根据逻辑 View 查找实际的 View
  6. DispaterServlet 把返回的 Model 传给 View(视图渲染)。
  7. 把 View 返回给请求者(浏览器)

19.如何统一进行异常处理

使用@ControllerAdvice、@ExceptionHandler注解会给所有或者指定的 Controller 织入异常处理的逻辑(AOP),当 Controller 中的方法抛出异常的时候,由被@ExceptionHandler 注解修饰的方法进行处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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) {
//......
}
}

ExceptionHandlerMethodResolver 中 getMappedMethod 方法决定了异常具体被哪个被 @ExceptionHandler 注解修饰的方法处理异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private final Map<Class<? extends Throwable>, Method> mappedMethods = new HashMap<>(16);
@Nullable
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<>();
//找到可以处理的所有异常信息。mappedMethods 中存放了异常和处理异常的方法的对应关系
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
// 不为空说明有方法处理异常
if (!matches.isEmpty()) {
// 按照匹配程度从小到大排序
matches.sort(new ExceptionDepthComparator(exceptionType));
// 返回处理异常的方法
return this.mappedMethods.get(matches.get(0));
}
else {
return null;
}
}

从源代码看出: getMappedMethod()会首先找到可以匹配处理异常的所有方法信息,然后对其进行从小到大的排序,最后取最小的那一个匹配的方法(即匹配度最高的那个)。

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

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

Spring事务

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

  • 编程式事务 在代码中硬编码使用,通过TransactionTemplate或TransactionManager手动管理事务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Autowired
    private TransactionTemplate transactionTemplate;
    public void testTransaction() {

    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
    @Override
    protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {

    try {

    // .... 业务代码
    } catch (Exception e){
    //回滚
    transactionStatus.setRollbackOnly();
    }

    }
    });
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Autowired
    private PlatformTransactionManager transactionManager;

    public void testTransaction() {

    TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
    // .... 业务代码
    transactionManager.commit(status);
    } catch (Exception e) {
    transactionManager.rollback(status);
    }
    }
  • 声明式事务 在XML中进行配置,或者基于注解使用
    1
    2
    3
    4
    5
    6
    7
    8
    @Transactional(propagation = Propagation.REQUIRED)
    public void aMethod {
    //do something
    B b = new B();
    C c = new C();
    b.bMethod();
    c.cMethod();
    }

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

  1. TransactionDefinition.PROPAGATION_REQUIRED 默认的事务传播行为,如果当前存在事务则加入该事务,否则开启一个事务
  2. TransactionDefinition.PROPAGATION_REQUIRES_NEW 如果当前存在事务则开启一个新的事务,新事务回滚,外部事务也回滚;外部事务回滚新事务不回滚。也就是开启的事务相互独立互不干扰
  3. TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务则开启一个嵌套事务,嵌套事务回滚,不影响外部事务回滚,外部事务回滚则嵌套事务回滚。 如果当前不存在事务,则创建一个事务
  4. TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务则加入该事务,如果不存在事务则抛出异常
  5. TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  6. TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。外部事务挂起,非事务运行的方法在一个隔离环境中,不会影响事务
  7. TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

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

  1. TransactionDefinition.ISOLATION_DEFAULT 默认隔离级别与数据库保持一致,MYSQL默认的事务隔离级别是REPEATABLE_READ,Oracle是READ_COMMITED
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED 最低的隔离级别,一个事务可以读取另外一个事务未提交的数据,会有脏读、幻读、不可重复读的问题
  3. TransactionDefinition.ISOLATION_READ_COMMITTED 可以阻止脏读,但是还是有可能发生幻读和不可重复读
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ 在一个事务中对同一行的数据的多次读取都是一致的,可以避免脏读、不可重复读,但是还是有可能发生幻读
  5. TransactionDefinition.ISOLATION_SERIALIZABLE 最高的隔离级别,事务按顺序依次执行,事务之间完全隔离,可以避免脏读、幻读、不可重复读,但是会影响性能

24.@Transational(rollbackFor = Exception.class)

这个注解就是指定触发回滚的异常,如果这个方法发生了Exception.class及其子类的异常事务就会回滚。

Exception.class异常可以分为已检查异常和未检查异常@Transational默认只会在发生未检查异常的时回滚,如果通过rollbackFor参数指定了异常类型,只有发生该异常类型及其子类的异常时才会触发事务回滚

Spring data JPA

Spring Data JPA是Spring框架的一个子项目,它简化了使用JPA(Java Persistence API)进行数据库访问的开发过程。它提供了一种更简洁和高级的方式来与数据库交互,减少了重复性的代码,并提供了一些常见的数据库操作功能。

以下是Spring Data JPA的主要特点和用法:

  1. 自动实现CRUD操作:Spring Data JPA自动为实体类生成CRUD(Create、Read、Update、Delete)操作的实现,你只需要定义相关的接口方法,无需手动编写SQL语句。
  2. 查询方法的自动生成:除了CRUD操作,Spring Data JPA还可以根据方法的命名规则自动生成查询方法。通过方法名中的关键字(如findBy、deleteBy等),可以快速定义查询条件,减少手动编写查询语句的工作量。
  3. 分页和排序支持:Spring Data JPA提供了对分页和排序的支持,使得分页查询和结果排序变得简单。
  4. 支持自定义查询:除了自动生成的查询方法,你还可以使用@Query注解来编写自定义的JPQL(Java Persistence Query Language)查询语句。
  5. 事务管理:Spring Data JPA与Spring框架无缝集成,可以利用Spring的事务管理机制来管理数据库事务。
  6. 集成各种数据源:Spring Data JPA可以与不同类型的数据源集成,包括关系型数据库(如MySQL、PostgreSQL、Oracle等)和非关系型数据库(如MongoDB、Cassandra等)。
  7. Repository接口:Spring Data JPA的核心是Repository接口,你可以通过继承Repository接口来使用自动实现的CRUD操作和查询方法。
  8. 扩展性:如果需要更高级的数据库操作,你还可以自定义Repository接口,通过继承Repository接口并添加自定义方法来实现。

使用示例:

当使用Spring Data JPA时,首先需要创建一个实体类,然后定义一个继承自JpaRepository的Repository接口,以利用Spring Data JPA的自动实现功能。以下是一个简单的示例,演示如何使用Spring Data JPA来执行基本的CRUD操作。

假设你有一个简单的User实体类,用于表示用户信息:

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;

// 省略构造方法、getter和setter
}

然后,你可以创建一个继承自JpaRepositoryUserRepository接口,它将继承自动实现的CRUD操作:

1
2
3
4
5
public interface UserRepository extends JpaRepository<User, Long> {
// 这里可以添加自定义的查询方法,例如按姓名查询用户
List<User> findByName(String name);
}

现在,可以在Spring应用程序中使用UserRepository来执行数据库操作。例如,在一个Spring Boot应用程序中,可以编写一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@SpringBootApplication
public class JpaExampleApplication implements CommandLineRunner {

@Autowired
private UserRepository userRepository;

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

@Override
public void run(String... args) {
// 创建并保存用户
User user1 = new User("Alice", "alice@example.com");
userRepository.save(user1);

// 查询所有用户
List<User> allUsers = userRepository.findAll();
System.out.println("All Users:");
allUsers.forEach(System.out::println);

// 根据姓名查询用户
List<User> aliceUsers = userRepository.findByName("Alice");
System.out.println("Users named Alice:");
aliceUsers.forEach(System.out::println);

// 删除用户
userRepository.delete(user1);

// 查询所有用户(再次查询,应该为空)
List<User> remainingUsers = userRepository.findAll();
System.out.println("Remaining Users:");
remainingUsers.forEach(System.out::println);
}
}

在这个示例中,我们通过userRepository执行了创建、保存、查询、删除等操作,所有的CRUD操作都由Spring Data JPA自动实现。这只是一个简单的示例,实际中可以根据需求自定义更复杂的查询方法。

需要注意,为了使用Spring Data JPA,你需要在项目的依赖中添加适当的Spring Data JPA库以及数据库驱动库。这个示例假设你已经配置了数据库连接和实体扫描等必要的配置。

25.如何使用JPA在数据库中非持久化一个字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity(name="USER")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
private Long id;

@Column(name="USER_NAME")
private String userName;

@Column(name="PASSWORD")
private String password;

private String secret;

}

如果想让 secret这个字段不被持久化,可以采用下面的方法

1
2
3
4
5
static String transient1; // not persistent because of static
final String transient2 = "Satish"; // not persistent because of final
transient String transient3; // not persistent because of transient
@Transient
String transient4; // not persistent because of @Transient

26.JPA的审计功能

JPA的审计功能是帮助记录数据库的操作的具体行为,比如数据的创建时间、更新时间、创建人、修改人、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Data
@AllArgsConstructor
@NoArgsConstructor
@MappedSuperclass
@EntityListeners(value = AuditingEntityListener.class)
public abstract class AbstractAuditBase {

@CreatedDate
@Column(updatable = false)
@JsonIgnore
private Instant createdAt;

@LastModifiedDate
@JsonIgnore
private Instant updatedAt;

@CreatedBy
@Column(updatable = false)
@JsonIgnore
private String createdBy;

@LastModifiedBy
@JsonIgnore
private String updatedBy;
}
  • @CreatedDate: 表示该字段为创建时间字段,在这个实体被 insert 的时候,会设置值
  • @CreatedBy :表示该字段为创建人,在这个实体被 insert 的时候,会设置值 @LastModifiedDate@LastModifiedBy同理。

27.实体之间的关联关系的注解有哪些

  • @OneToOne  : 一对一。
  • @ManyToMany :多对多。
  • @OneToMany : 一对多。
  • @ManyToOne :多对一。

利用 @ManyToOne 和 @OneToMany 也可以表达多对多的关联关系。

Spring Security

28.如何对密码进行加密

Spring Security 提供了多种加密算法的实现,开箱即用,非常方便。这些加密算法实现类的父类是 PasswordEncoder ,如果你想要自己实现一个加密算法的话,也需要继承 PasswordEncoder

PasswordEncoder 接口一共也就 3 个必须实现的方法。

1
2
3
4
5
6
7
8
9
10
public interface PasswordEncoder {
// 加密也就是对原始密码进行编码
String encode(CharSequence var1);
// 比对原始密码和数据库中保存的密码
boolean matches(CharSequence var1, String var2);
// 判断加密密码是否需要再次进行加密,默认返回 false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}

官方推荐使用基于 bcrypt 强哈希函数的加密算法实现类。