Skip to content

Spring Framework

1. 框架概念

1.1 生活中的框架

框架是一个集成了基本结构、规范、设计模式、编程语言和程序库等基础组件的软件系统,用于构建更高级别的应用程序。

框架思维:为解决特定问题而提供的一整套解决方案。

1.2 程序中的框架

主流框架:SSM

  • Spring:为所有 bean(组件)提供管理解决方案
  • SpringMVC:解决表述层(控制层)常见问题
  • Mybatis:解决数据访问层(Dao 层)问题

框架优点

  • 提高开发效率
  • 降低开发成本
  • 提高应用程序稳定性
  • 提供标准化解决方案

2. Spring Framework 简介

2.1 Spring 广义与狭义

广义 Spring:Spring 技术栈(全家桶)

  • Spring Framework、Spring MVC、SpringBoot、Spring Cloud 等

狭义 Spring:Spring Framework(基础框架)

2.2 Spring Framework 概述

Spring 是基于 IoC 和 AOP 的容器框架

核心概念

  • IoC 容器:负责实例化、配置和组装 bean
  • IoC(控制反转):控制权由应用程序转移到 IoC 容器
  • DI(依赖注入):在容器内部处理组件间的依赖关系
  • AOP:面向切面编程,动态统一添加额外功能

主要功能模块

模块功能
Core Container核心容器,控制反转和依赖注入
AOP&Aspects面向切面编程
TX声明式事务管理
Testing快速整合测试环境
Data Access/Integration数据访问/集成功能
Spring MVCWeb 应用程序集成功能

2.3 Spring Framework 特点

  • 丰富的生态系统
  • 模块化设计
  • 简化 Java 开发
  • 不断创新和发展

3. Spring IoC/DI 基本实现

3.1 基于 XML 方式装配组件

环境准备

xml
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.0.6</version>
    </dependency>
</dependencies>

实现步骤

  1. POJO 类
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private Integer stuId;
    private String stuName;
    private Integer stuAge;
}
  1. 配置文件(spring.xml):
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="zs" class="com.at.bean.Student">
        <property name="stuId" value="1001"/>
        <property name="stuName" value="zs"/>
        <property name="stuAge" value="18"/>
    </bean>
</beans>
  1. 测试类
java
@Test
public void testStudent(){
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring.xml");
    Student bean = ioc.getBean(Student.class);
    System.out.println("bean = " + bean);
}

3.2 基于配置类装配组件

配置类

java
@Configuration
public class SpringConfig {

    @Bean
    public Student student(){
        return new Student(1001, "zhangsan", 18);
    }
}

测试类

java
@Test
public void testSpring(){
    ApplicationContext ioc = new AnnotationConfigApplicationContext(SpringConfig.class);
    Student bean = ioc.getBean(Student.class);
    System.out.println("bean = " + bean);
}

3.3 @Bean 注解详解

@Bean 源码属性

  • name:指定 bean 名称
  • value:与 name 属性是别名关系
  • initMethod:指定初始化方法
  • destroyMethod:指定销毁方法
  • autowireCandidate:是否作为自动装配候选者

装配 DruidDataSource

java
@Configuration
public class DruidConfig {

    @Value("${jdbc.driverClassName}")
    private String driverClassName;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driverClassName);
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        return ds;
    }
}

3.4 Bean 生命周期

六个阶段

  1. 加载 Bean 定义
  2. 实例化 Bean 组件
  3. 设置 Bean 属性
  4. 调用 Bean 初始化方法
  5. Bean 可以使用
  6. 调用 Bean 销毁方法

示例

java
public class Student {
    public Student() {
        System.out.println("Student==>构造器!!!");
    }

    public void setStuId(Integer stuId) {
        System.out.println("==>Student->setId()!!!");
        this.stuId = stuId;
    }

    public void initStudent() {
        System.out.println("==>Student->init-method()!!!");
    }

    public void destroyStudent() {
        System.out.println("==>Student->destroy-method()!!!");
    }
}
java
@Configuration
public class SpringConfig {
    @Bean(value = "student", initMethod = "initStudent", destroyMethod = "destroyStudent")
    public Student student(){
        Student student = new Student();
        student.setStuId(101);
        return student;
    }
}

3.5 Bean 作用域

常用作用域

作用域含义创建时机默认值
singletonIOC 容器中单实例IOC 容器初始化时
prototype多个实例获取 bean 时

使用

java
@Bean
@Scope(value = "singleton") // 或 "prototype"
public Student student(){
    return new Student();
}

4. Spring IoC/DI 深入学习

4.1 开启组件扫描

java
@Configuration
@ComponentScan(basePackages = "com.at")
public class SpringConfig {}

4.2 分层管理组件注解

注解说明
@Component通用组件
@Repository数据访问层(Dao 层)
@Service业务层(Service 层)
@Controller控制层

4.3 属性自动注入注解

@Value:为字面量实现自动装配

java
@Value("${jdbc.url}")
private String url;

@Autowired:为非字面量类型实现自动装配

  • 先按类型匹配,再按 id 筛选

@Qualifier:配合 @Autowired 指定 bean id

java
@Autowired
@Qualifier("userDao")
private UserDao userDao;

@Resource:Java EE 注解,默认按名称匹配

4.4 @Import 注解

导入配置类

java
@Configuration
@Import({DatabaseConfig.class, WebConfig.class})
public class AppConfig {}

导入普通类

java
@Import(MyService.class)
public class AppConfig {}

4.5 @Conditional 注解

条件化装配 Bean

java
@Configuration
public class AppConfig {

    @Bean
    @Conditional(MyCondition.class)
    public MyService myService() {
        return new MyServiceImpl();
    }
}

衍生条件注解

  • @ConditionalOnProperty
  • @ConditionalOnClass
  • @ConditionalOnBean

4.6 FactoryBean 与 BeanFactory 区别

特性FactoryBeanBeanFactory
角色定义如何创建特定类型 bean管理 bean 生命周期
主要用途控制 bean 创建逻辑提供 bean 获取和管理
接口实现实现者提供具体创建逻辑Spring 容器基础接口

5. Spring AOP 详解

5.1 AOP 前奏

问题:核心业务代码与非核心业务代码耦合

解决方案

  • 提取工具类解决代码分散
  • 使用代理模式解决代码混乱

5.2 代理模式

静态代理

java
public class CalcImplStaticProxy implements Calc {
    private CalcImpl calcImpl;

    public int add(int i, int j) {
        MyLogging.methodBefore("add", i, j);
        int rs = calcImpl.add(i, j);
        MyLogging.methodAfter("add", rs);
        return rs;
    }
}

动态代理

java
public class CalcImplDynamicProxy {
    private Object target;

    public Object getProxy(){
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy, method, args) -> {
                String methodName = method.getName();
                MyLogging.methodBefore(methodName, args);
                Object rs = method.invoke(target, args);
                MyLogging.methodAfter(methodName, rs);
                return rs;
            }
        );
    }
}

5.3 Spring 支持的动态代理

JDK 动态代理

  • 基于接口
  • proxy-target-class="false"

CGLIB 动态代理

  • 基于继承
  • proxy-target-class="true"(SpringBoot 默认)

5.4 基于注解实现 AOP

环境准备

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

切面类

java
@Component
@Aspect
public class MyLogging {

    @Before("execution(public int com.at.aop.CalcImpl.add(int,int))")
    public void methodBefore(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("==>"+methodName+"()正在执行,参数:"+ Arrays.toString(args));
    }
}

开启支持

java
@Configuration
@EnableAspectJAutoProxy
public class SpringConfigAop {}

5.5 AOP 相关术语

  • 横切关注点:非核心业务代码
  • 通知:非核心业务代码提取到类中
  • 连接点:非核心业务代码织入位置(通知之前)
  • 切入点:非核心业务代码织入位置(通知之后)
  • 切面类:包含通知的类
  • 目标对象:被代理的对象
  • 代理对象:通过代理类获取的对象

5.6 切入点表达式

语法

java
@Before("execution(* com.at.aop.*.*(..))")

重用表达式

java
@Pointcut("execution(* com.at.aop.*.*(..))")
public void myJoinPoint(){}

@Before("myJoinPoint()")
public void methodBefore(JoinPoint joinPoint){}

5.7 AOP 五大通知

前置通知(@Before):

java
@Before("myJoinPoint()")
public void methodBefore(JoinPoint joinPoint){}

后置通知(@After):

java
@After("myJoinPoint()")
public void methodAfter(JoinPoint joinPoint){}

返回通知(@AfterReturning):

java
@AfterReturning(value = "myJoinPoint()", returning = "rs")
public void methodAfterRetuning(JoinPoint joinPoint, Object rs){}

异常通知(@AfterThrowing):

java
@AfterThrowing(value = "myJoinPoint()", throwing = "ex")
public void methodAfterThrowing(JoinPoint joinPoint, Exception ex){}

环绕通知(@Around):

java
@Around("myJoinPoint()")
public Object methodAround(ProceedingJoinPoint pjp){
    try {
        // 前置通知
        Object rs = pjp.proceed();
        // 返回通知
        return rs;
    } catch (Throwable e) {
        // 异常通知
    } finally {
        // 后置通知
    }
}

5.8 切面优先级

java
@Aspect
@Order(1)
public class LoggingAspect {}

6. Spring 事务管理

6.1 JdbcTemplate 基本用法

环境准备

properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring_tx
spring.datasource.username=root
spring.datasource.password=root

常用 API

java
// 增删改
jdbcTemplate.update(String sql, Object... args);

// 查询单个对象
jdbcTemplate.queryForObject(String sql, RowMapper, Object... args);

// 查询多个对象
jdbcTemplate.query(String sql, RowMapper);

6.2 事务概念回顾

ACID 属性

  • 原子性(Atomicity)
  • 一致性(Consistency)
  • 隔离性(Isolation)
  • 持久性(Durability)

隔离级别

  • 读未提交(READ UNCOMMITTED)
  • 读已提交(READ COMMITTED)
  • 可重复读(REPEATABLE READ)- MySQL 默认
  • 串行化(SERIALIZABLE)

6.3 Spring 声明式事务

编程式事务

java
Connection conn = ...;
try {
    conn.setAutoCommit(false);
    // 业务代码
    conn.commit();
} catch(Exception e){
    conn.rollBack();
} finally{
    conn.close();
}

声明式事务(推荐):

java
@Service
@Transactional
public class UserService {
    // 方法会自动在事务中执行
}

事务管理器配置

java
@Configuration
@EnableTransactionManagement
public class TxConfig {

    @Bean
    public TransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}

6.4 @Transactional 注解属性

只读事务

java
@Transactional(readOnly = true)

事务超时

java
@Transactional(timeout = 3) // 3秒超时

回滚规则

java
@Transactional(
    rollbackFor = Exception.class,
    noRollbackFor = FileNotFoundException.class
)

隔离级别

java
@Transactional(isolation = Isolation.REPEATABLE_READ)

传播行为

java
@Transactional(propagation = Propagation.REQUIRED) // 默认
@Transactional(propagation = Propagation.REQUIRES_NEW)

6.5 事务传播行为

常用传播行为

  • REQUIRED(默认):如果当前存在事务,则加入该事务;如果不存在,则创建新事务
  • REQUIRES_NEW:创建新事务,如果当前存在事务,则挂起当前事务

示例

java
@Service
public class UserServiceAll {

    @Autowired
    private UserService userService;

    @Transactional
    public void updateUserAll(){
        userService.updatePwdByUsername("zhangsan", "666666");
        userService.updateNicknameByUsername("普通员工", "zhangsan");
    }
}