Spring
图
Spring简化企业级应用程序的开发,提供了包括依赖注入、面向切面编程、声明式事务管理、数据访问和集成等功能,同时还支持各种不同的应用程序服务器和Web服务器,并提供与其他第三方框架的无缝集成

Spring介绍

Spring是一款主流的Java EE轻量级开源框架,目的是用于简化Java企业级应用的开发难度和开发周期

Spring简化企业级应用程序的开发,提供了包括依赖注入、面向切面编程、声明式事务管理、数据访问和集成等功能,同时还支持各种不同的应用程序服务器和Web服务器,并提供与其他第三方框架的无缝集成

Spring划分

  • 广义上:值以Spring Framework为核心的Spring技术栈,由很多子项目组成,如:SpringBoot、Spring MVC、Spring Cloud、Spring Data等
  • 狭义上:专指Spring Framework,两大核心:IOC和AOP

注意:Spring6需要JDK17

什么是IOC?

控制反转是指将对象的创建和依赖关系的管理交给IoC容器完成,不再进行new创建对象,而是通过DI动态的进行注入,避免大量的代码耦合,提高了程序的可维护性和可扩展性

什么是DI?

依赖注入,是指由容器在程序运行时动态地将需要的依赖对象传递给应用程序的各个组件,而不是由这些组件自己去创建或查找依赖,通过依赖注入,组件之间的耦合度大大降低,而且使得代码更加清晰、可维护和可扩展

DI的具体实现是由反射技术运行时动态生成

管理IOC容器中的bean的方式?

  • XML配置文件方式
  • 注解方式

获取bean的方式?

在Spring框架中,获取bean的方式有以下几种:

方法一:通过IOC容器获取:在XML配置文件或注解中定义bean后,可以通过ApplicationContext或BeanFactory接口从IOC容器中获取bean实例。例如:

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyBean myBean = (MyBean) context.getBean("myBean");

方法二:通过自动装配获取:在Java类中使用@Autowired注解对bean进行自动装配,Spring会自动将其注入到相应的属性中。例如:

@Service
public class MyService {
    @Autowired
    private MyBean myBean;

    // ...
}

方法三:通过构造方法注入获取:在Java类中使用构造方法注入的方式获取bean实例。例如:

@Service
public class MyService {
    private MyBean myBean;

    public MyService(MyBean myBean) {
        this.myBean = myBean;
    }

    // ...
}

IOC注入方式?

  1. Setter注入:使用setter方法进行注入,容器会调用相应的setter方法将依赖注入到对象中
  2. 构造函数注入:使用构造函数进行注入,在创建对象时直接将依赖传递给构造函数
  3. 接口注入:使用接口进行注入,目标对象实现相应的接口,容器会根据接口类型将依赖注入到相应的属性中
  4. 注解注入:使用注解进行注入,目标对象上标注相应的注解,容器会自动将依赖注入到标注了注解的属性或参数中

注解注入?

  • @Autowired:用于自动装配Bean依赖,可以标注在属性、构造函数、Setter方法等位置
  • @Resource:用于根据名称注入Bean依赖,可以标注在属性、Setter方法等位置
  • @Inject:与@Autowired类似,但是是JSR-330中定义的注解

Bean作用域?

  • 单例(Singleton):默认,创建时机为IOC容器初始化时
  • 原型(Prototype):多实例,创建时机在获取Bean时
  • 会话(Session):在Web应用程序中,为每个会话创建一个Bean实例
  • 请求(Request):在Web应用程序中,为每个请求创建一个Bean实例

Bean的生命周期?

  1. 实例化:容器根据配置文件或注解创建一个Bean的实例
  2. 属性赋值:容器将Bean的属性值注入到Bean实例中
  3. 初始化前回调方法:在Bean实例初始化之前,容器会调用一些回调方法
  4. 初始化
  5. 初始化后回调方法:在Bean实例初始化完成后,容器会再次调用一些回调方法
  6. 就绪
  7. 销毁:当Bean不再被使用时,容器会销毁该Bean实例,并调用其生命周期中的destroy()方法

定义Bean的注解

  1. @Component:通用的组件注解,可标注任何类作为Spring组件
  2. @Repository:持久层对象注解,用于标注DAO组件类
  3. @Service:服务层对象注解,用于标注Service组件类
  4. @Controller:控制器注解,用于标注Controller组件类
  5. @Configuration:配置类注解,用于标注该类为Spring配置类
  6. @Bean:用于在@Configuration类中的方法上声明一个Bean

Bean加载流程?

  1. 通过配置文件或Java代码定义Bean:开发人员可以使用XML文件或Java代码来定义需要创建的Bean,并配置它们的属性和依赖关系等信息
  2. 加载配置文件:Spring容器在启动时会加载指定的配置文件,根据配置文件中的Bean定义创建Bean实例
  3. 创建Bean实例:当容器加载完成配置文件后,会按照Bean定义逐一创建Bean实例。对于每一个Bean,容器会先判断是否需要延迟加载、是否需要使用单例模式、是否需要AOP代理等
  4. Bean依赖注入:在Bean实例化后,容器会自动将所需的依赖对象通过构造函数注入、Setter方法注入或者字段注入等方式注入到Bean中
  5. Bean生命周期管理:在容器管理下的Bean会经历多个不同的生命周期阶段,包括Bean定义解析、Bean实例化、Bean属性设置、初始化回调、销毁回调等
  6. 提供Bean:容器完成Bean实例化、依赖注入和生命周期管理后,就可以将Bean提供给应用程序使用了

AOP

面向切面,是对面向对象的一种补充,主要目的是解决系统中各个模块之间的横切关注点问题,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,从而减少系统中的重复代码,降低了模块间的耦合度,提高系统的可维护性

AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

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

AOP相关术语

  • 横切关注点:具有相同代码的地方
  • 通知(增强):具体要增强的功能
  • 切面:封装通知方法的类
  • 目标:被代理的目标对象
  • 代理:想目标对象应用通知之后创建的代理对象
  • 连接点:允许使用通知的地方
  • 切入点:实际要增强的方法

Spring通知类型

  • 前置通知:在连接点之前执行的通知(@Before)
  • 后置通知:当连接点退出的时候执行的通知(@After)
  • 返回通知:在连接点正常完成后执行的通知(@AfterReturning)
  • 异常通知:在方法抛出异常退出时执行的通知(@AfterThrowing)
  • 环绕通知:包围一个连接点的通知,包含上面4个(@Around)

AOP实现方式

使用的是动态代理:有接口使用JDK动态代理,没有接口使用CGLIB动态代理

AOP使用实例

图

@Aspect
@Component
public class LoggingAspect {
 
    @Before("execution(* com.myapp.service.*.*(..))")
    public void beforeServiceMethodExecution() {
        System.out.println("Before method execution...");
    }
 
    @AfterReturning("execution(* com.myapp.service.*.*(..))")
    public void afterServiceMethodExecution() {
        System.out.println("After method execution...");
    }
 
    @AfterThrowing(pointcut = "execution(* com.myapp.service.*.*(..))", throwing = "ex")
    public void afterServiceMethodException(Throwable ex) {
        System.out.println("After method throws exception... " + ex.getMessage());
    }
 
    @Around("execution(* com.myapp.service.*.*(..))")
    public Object aroundServiceMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Before method execution...");
        try {
            Object result = joinPoint.proceed();
            System.out.println("After method execution...");
            return result;
        } catch (Throwable e) {
            System.out.println("Exception thrown: " + e.getMessage());
            throw e;
        }
    }
}

获取通知的方法信息

图

获取方法返回值示例

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @AfterReturning(value = "execution(* com.example.service.UserService.getUserById(..))", returning = "user")
    public void logUser(User user) {
        logger.info("User {} has been retrieved.", user.getName());
    }
}

获取异常示例

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @AfterThrowing(value = "execution(* com.example.service.UserService.deleteUserById(..))", throwing = "exception")
    public void logDeleteUserException(UserNotFoundException exception) {
        logger.error("Failed to delete user. Error message: {}", exception.getMessage());
    }
}

环绕通知示例

@Aspect
@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Around("execution(* com.example.service.UserService.updateUser(..))")
    public Object logUpdateUser(ProceedingJoinPoint joinPoint) {
        // 目标方法名
        joinPoint.getSignature().getName()
        // 目标参数
        joinPoint.getArgs()
        // 返回值
        Object result = null;
        try {
            // 调用目标方法
            result = joinPoint.proceed();
        } catch (Throwable e) {
            System.out.println("出现异常(与异常通知作用相似)")
        } finally {
            System.out.println("最后执行(与后置通知作用相似)")
        }

        long endTime = System.currentTimeMillis();
        logger.info("Finish updating user. Execution time: {} ms.", endTime - startTime);

        return result;
    }
}

重用切入点

图

图

切面优先级

图

Spring涉及的设计模式?

  • 工厂模式:通过BeanFactory和ApplicationContext来创建对象
  • 单例模式:Bean默认为单例模式
  • 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
  • 策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
  • 模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题,比如RestTemplate
  • 适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller
  • 观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用
  • 桥接模式:可以根据客户的需求能够动态切换不同的数据源