spring知识点

1 Spring Ioc

IoC的思想就是将原本在程序中⼿动创建对象的控制权,交由Spring框架管理

为什么叫控制反转?

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

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

在Spring中,IoC容器是Spring⽤来实现IoC的载体,IoC容器实际上就是个Map(key,value),Map中存放的是各种对象

2 Spring Bean

简单来说,Bean代指的就是那些被IoC容器所管理的对象

2.1 将⼀个类声明为Bean的注解有哪些?

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

2.2 @Component和@Bean的区别是什么?

  • @Component注解作用于类,而@Bean注解作用于方法
  • @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring容器中(可以使用ComponentScan注解定义要扫描的路径)。@Bean注解通常是我们标有该注解的方法中定义产生这个bean,@Bean告诉了Spring这是某个类的实例
  • @Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如引用第三方库中的类需要装配到Spring容器时,只能通过@Bean来实现

2.3 注入Bean的注解有哪些?(即引入对象的注解)

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

图片

@Autowired和@Resource使用的比较多一些

2.4 @Autowired和@Resource的区别

  • @Autowired属于Spring内置的注解,默认注入方式为byType(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入Bean(接口的实现类)
    • 这会有什么问题呢?当一个接口存在多个实现类, byType这种方式就无法正确注入对象,Spring会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。这种情况下,注入方式会变为byName(根据名称进行匹配),这个名称通常就是类名(首字母小写)。
  • @Resource属于JDK提供的注解,默认注入方式为byName。如果无法通过名称匹配到对应的Bean,注入方式会变为byType
    • @Resource有两个比较重要且日常开发常用的属性:name(名称)、type(类型)
  • 总结
  • @Autowired是Spring提供的注解,@Resource是JDK提供的注解
  • @Autowired默认注入方式为byType,@Resource默认注入方式为byName
  • 当一个接口存在多个实现类的情况下,@Autowired和@Resource都需要通过名称才能正确匹配到对应的Bean。@Autowired通过@Qualifier注解显示指定名称,@Resource通过name属性显示指定名称

2.5 Bean的作用域有哪些?

  • singleton: loC容器中只有唯一的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

2.6 单例Bean的线程安全问题

单例Bean存在线程问题,主要是因为当多个线程操作同一个对象时存在资源竞争

常见的有两种解决办法:

1.在Bean中尽量避免定义可变的成员变量

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

不过,大部分Bean实际都是无状态(没有实例变量)的(比如Dao、Service),这种情况下Bean是线程安全的

2.7 Bean的生命周期

  • Bean容器找到配置文件中Spring Bean的定义
  • Bean容器利用Java Reflection API创建一个Bean的实例
  • 如果涉及到一些属性值,就利用set()方法设置一些属性值
  • 如果Bean实现了BeanNameAware接口,调用setBeanName()方法,传入Bean的名字
  • 如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader()方法,传入ClassLoader对象的实例
  • 如果Bean实现了BeanFactoryAware接口, 调用setBeanFactory()方法,传入BeanFactory对象的实例
  • 与上面的类似,如果实现了其他*.Aware接口,就调用相应的方法
  • 如果有和加载这个Bean的Spring容器相关的BeanPostrocessor对象, 执行postProcessBeforelnitialization()方法
  • 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法
  • 如果Bean在配置文件中的定义包含init-method属性,执行指定的方法
  • 如果有和加载这个Bean的Spring容器相关的BeanPostrocessor对象,执行postProcessAfterInitialization()方法
  • 当要销毁Bean时,如果Bean实现了DisposableBean接口,执行destroy()方法
  • 当要销毁Bean时,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法
    图片

图片

3 Spring AOP

AOP:面向切面编程。能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性

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

Spring AOP基于动态代理,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy去创建代理对象,而对于没有实现接口的对象,Spring AOP会使用Cglib生成一个被代理对象的子类来作为代理。

当然也可以使用AspectJ AOP,Spring AOP已经集成了AspectJ,AspectJ算的上是Java生态系统中最完整的AOP框架

3.1 Spring AOP和AspectJ AOP的区别

  • Spring AOP属于运行时增强,而AspectJ是编译时增强
  • Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。
  • AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单,如果切面比较少,那么两者性能差异不大。但是当切面太多的话,最好选择AspectJ,它比Spring AOP快很多

3.2 AspectJ定义的通知类型有哪些?

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

3.3 多个切面的执行顺序如何控制?

1、通常使用@Order注解直接定义切面顺序

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

2、实现Ordered接口重写getOrder方法

@Component
@Aspect
public class LoggingAspect implements Ordered {
// ....
@Override
public int getOrder() {
// 返回值越小优先级越高
return 1;
}
}

4 Spring MVC

MVC是模型(Model)、视图(View)、控制器(Controller)的简写

4.1 Spring MVC的核心组件

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

4.2 Spring MVC工作原理

图片

  • 客户端(浏览器)发送请求, DispatcherServlet拦截请求
  • DispatcherServlet根据请求信息调用HandlerMapping。HandlerMapping根据uri去匹配查找能处理的Handler,并会将请求涉及到的拦截器和Handler一起封装。
  • DispatcherServlet调用HandlerAdapter适配执行Handler
  • Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet。ModelAndView包含了数据模型以及相应的视图的信息。Model是返回的数据对象,View是逻辑上的View
  • ViewResolver会根据逻辑View查找实际的View
  • DispaterServlet把返回的Model传给View(视图渲染)
  • 把View返回给请求者(浏览器)

4.3 统一异常处理

可以使用注解的方式统一异常处理,具体为@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) {
//......
}
}
Copy to clipboard
Error
Copied

这种异常处理方式下,会给所有或者指定的Controller织入异常处理的逻辑(AOP),当Controller中的方法抛出异常时,由被@ExceptionHandler注解修饰的方法进行处理
ExceptionHandlerMethodResolver中getMappedMethod方法决定了异常具体被哪个被@ExceptionHandler注解修饰的方法处理异常。

@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()会首先找到可以匹配处理异常的所有方法信息,然后对其进行从小到大的排序最后取最小的那一个匹配的方法(即匹配度最高的那个)

5 Spring框架中用到的设计模式

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

6 Spring事务

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

编程式事务:在代码中硬编码(不推荐使用) : 通过TransactionTemplate或者TransactionManager手动管理事务,实际应用中很少使用

声明式事务:在XML配置文件中配置或者直接基于注解(推荐使用) : 实际通过AOP实现(基于@Transactional的全注解方式使用最多)

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

事务传播行为是为了解决业务层方法之间互相调用的事务问题。

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

正确的事务传播行为可能的值如下:

  • TransactionDefinition.PROPAGATION_REQUIRED
    如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。使用的最多的一个事务传播行为,平时经常使用的@Transactional注解默认使用这个事务传播行为。

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW
    创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。

  • TransactionDefinition.PROPAGATION_NESTED
    如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

  • TransactionDefinition.PROPAGATION_MANDATORY
    如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常(mandatory:强制性)。这个使用的很少。

若是错误的配置以下3种事务传播行为,事务将不会发生回滚:

  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常

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

TransactionDefinition接口中定义了五个表示隔离级别的常量,Spring定义了一个枚举类Isolation应用他们

public enum Isolation {
    DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
    READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
    READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
    REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
    SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
    private final int value;
    Isolation(int value) {
        this.value = value;
    }
    public int value() {
        return this.value;
    }
}
  • TransactionDefinition.ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,MySQL默认采用REPEATABLE_READ,Oracle默认采用READ_COMMITTED
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据,会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被事务自己修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别

6.4 @Transactional(rollbackFor = Exception.class)注解了解吗?

当@Transactional注解作用于类上时,该类的所有public方法将都具有该类型的事务属性。同时,当其作用于方法上时,会将类级别的覆盖掉。如果类或者方法加了这个注解,那么当这个类里的方法抛出异常时,就会回滚,数据库里面的数据也会回滚。

Exception分为运行时异常RuntimeException和非运行时异常。

在@Transactional注解中如果不配置rollbackFor属性,那么事务只会在遇到RuntimeException时才会回滚,加上rollbackFor=Exception.class可以让事务在遇到非运行时异常时也回滚

posted @   ycylikestuty  阅读(68)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示