Spring 框架IOC和AOP

Spring 框架作为 Java 领域的 "武林盟主",自 2003 年由 Rod Johnson 创建以来,凭借其轻量级、一站式、松耦合 的设计理念,彻底改变了企业级应用开发的模式。本文将围绕 Spring 的两大核心 ------控制反转(IOC)面向切面编程(AOP),结合配置文件、半注解、全注解三种开发方式,带你从入门到精通 Spring 核心技术。

一、Spring 框架概述:为什么它能成为 Java 开发的 "标配"?

1. Spring 是什么?

Spring 是一个分层的 JavaSE/EE 全栈轻量级开源框架 ,核心解决业务逻辑层与其他层的耦合问题 ,将面向接口的编程思想贯穿整个系统。它不是要替代现有技术,而是通过整合各类优秀框架(如 MyBatis、Struts2 等),为开发者提供一套统一的开发规范。

简单来说,Spring 就像一个 "超级工厂",帮你管理所有对象的创建和依赖关系;又像一把 "手术刀",通过 AOP 实现对业务逻辑的精准切入 ------ 这一切都源于它的两大灵魂:IOC(控制反转)AOP(面向切面编程)

2. Spring 的六大核心优势

  • 解耦神器:通过 IOC 将对象创建权交给 Spring,彻底摆脱 "new 对象" 的强依赖,让代码更灵活;
  • AOP 赋能:无需修改业务代码,就能实现权限拦截、日志监控等横向功能;
  • 声明式事务 :只需简单配置,就能完成事务管理,再也不用手动写try-catch-commit-rollback
  • 测试友好:对 JUnit 深度支持,通过注解可一键测试 Spring 组件;
  • 框架整合王:无缝集成 MyBatis、Hibernate、Quartz 等主流框架,避免重复造轮子;
  • 简化 API 使用:对 JDBC、JavaMail 等 "难用" 的 JavaEE API 进行封装,降低开发门槛。

二、IOC 核心技术:把对象创建权 "反转" 给 Spring

1. 什么是 IOC?

IOC(Inverse of Control,控制反转)是 Spring 的核心设计思想 ------将对象的创建权从开发者手中 "反转" 给 Spring 框架

举个例子:以前你需要手动new UserService(),现在只需告诉 Spring"我要一个 UserService",Spring 就会自动帮你创建好对象。这种模式彻底解决了程序耦合性高的问题,让代码维护性飙升。

2. IOC 入门实战:从 0 到 1 搭建 Spring 工程

步骤 1:导入依赖(Maven 坐标)
XML 复制代码
<dependencies>
    <!-- Spring核心容器 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- 日志相关 -->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
步骤 2:编写业务接口与实现类
java 复制代码
// 接口:UserService.java
package com.spring.demo.service;
public interface UserService {
    void hello();
}

// 实现类:UserServiceImpl.java
package com.spring.demo.service;
public class UserServiceImpl implements UserService {
    @Override
    public void hello() {
        System.out.println("Hello, Spring IOC!");
    }
}
步骤 3:编写 Spring 配置文件(applicationContext.xml)

src/main/resources下创建applicationContext.xml,配置需要 Spring 管理的 Bean:

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">

    <!-- 配置UserService Bean,id是Bean的唯一标识,class是实现类全路径 -->
    <bean id="userService" class="com.spring.demo.service.UserServiceImpl"/>
</beans>
步骤 4:编写测试类
java 复制代码
package com.spring.demo.test;
import com.spring.demo.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IocDemo {
    @Test
    public void testIoc() {
        // 加载Spring配置文件,创建工厂
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从工厂中获取Bean
        UserService userService = (UserService) context.getBean("userService");
        // 调用方法
        userService.hello();
    }
}

运行测试方法,控制台将输出Hello, Spring IOC!------ 这意味着 Spring 已经成功帮你创建并管理了UserService对象!

3. IOC 的 Bean 管理:配置文件方式详解

(1)Bean 的基础属性
  • id:Bean 的唯一标识,命名需以字母开头,可包含字母、数字、下划线等(不能有特殊字符);
  • class:Bean 的实现类全路径,Spring 通过反射创建对象;
  • scope :Bean 的作用范围:
    • singleton(默认):单例,整个 Spring 容器中只有一个实例;
    • prototype:多例,每次getBean都会创建新实例;
    • request/session:仅在 Web 环境下生效,分别对应一次请求或一个会话的作用域。
(2)Bean 的生命周期配置

Spring 允许在 Bean创建时销毁时执行自定义方法:

XML 复制代码
<bean id="userService" class="com.spring.demo.service.UserServiceImpl"
      init-method="init" destroy-method="destroy"/>
java 复制代码
public class UserServiceImpl implements UserService {
    public void init() {
        System.out.println("Bean初始化时执行...");
    }
    public void destroy() {
        System.out.println("Bean销毁时执行...");
    }
    // 其他方法...
}
  • 单例 Bean 的destroy方法在 Spring 容器关闭时执行;
  • 多例 Bean 的destroy方法由 JVM 垃圾回收机制管理,Spring 不主动调用。
(3)Bean 的三种实例化方式

Spring 支持三种创建 Bean 的方式:

  • 默认构造方法(最常用)

    复制代码
    <bean id="userService" class="com.spring.demo.service.UserServiceImpl"/>
  • 静态工厂方式

    java 复制代码
    // 静态工厂类
    public class StaticFactory {
        public static UserService createUserService() {
            return new UserServiceImpl();
        }
    }
    XML 复制代码
    <bean id="userService" class="com.spring.demo.factory.StaticFactory" 
          factory-method="createUserService"/>
  • 实例工厂方式

    java 复制代码
    // 实例工厂类
    public class InstanceFactory {
        public UserService createUserService() {
            return new UserServiceImpl();
        }
    }
    XML 复制代码
    <bean id="instanceFactory" class="com.spring.demo.factory.InstanceFactory"/>
    <bean id="userService" factory-bean="instanceFactory" 
          factory-method="createUserService"/>

三、DI 依赖注入:让 Spring 自动解决对象依赖

DI(Dependency Injection,依赖注入)是 IOC 的具体实现方式------Spring 在创建 Bean 时,会自动将其依赖的对象 "注入" 进来。

1. Set 方法注入(最常用)

通过 Bean 的 setter 方法注入依赖,步骤如下:

步骤 1:在 Bean 中定义依赖属性并提供 set 方法
java 复制代码
public class OrderServiceImpl implements OrderService {
    // 依赖的Dao层对象
    private OrderDao orderDao;
    // 普通属性
    private String msg;
    private int age;

    // 提供set方法,Spring通过set方法注入值
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void saveOrder() {
        System.out.println("保存订单:" + msg + ",年龄:" + age);
        orderDao.saveOrder();
    }
}

public interface OrderDao {
    void saveOrder();
}

public class OrderDaoImpl implements OrderDao {
    @Override
    public void saveOrder() {
        System.out.println("DAO层:执行保存订单SQL");
    }
}
步骤 2:在配置文件中配置依赖注入
XML 复制代码
<bean id="orderDao" class="com.spring.demo.dao.OrderDaoImpl"/>
<bean id="orderService" class="com.spring.demo.service.OrderServiceImpl">
    <!-- 注入Dao对象,ref引用其他Bean的id -->
    <property name="orderDao" ref="orderDao"/>
    <!-- 注入普通字符串,value直接赋值 -->
    <property name="msg" value="Spring依赖注入演示"/>
    <property name="age" value="20"/>
</bean>
步骤 3:测试依赖注入
java 复制代码
@Test
public void testDI() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    OrderService orderService = (OrderService) context.getBean("orderService");
    orderService.saveOrder();
}

运行后控制台将输出:

复制代码
保存订单:Spring依赖注入演示,年龄:20
DAO层:执行保存订单SQL

2. 构造方法注入

通过构造方法注入依赖,适合必须在对象创建时就初始化依赖的场景:

java 复制代码
public class Car {
    private String name;
    private Double price;

    public Car(String name, Double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{name='" + name + "', price=" + price + "}";
    }
}
复制代码
<bean id="car" class="com.spring.demo.entity.Car">
    <!-- constructor-arg指定构造方法参数,name对应参数名 -->
    <constructor-arg name="name" value="奔驰"/>
    <constructor-arg name="price" value="400000"/>
</bean>

3. 集合类型注入(数组、List、Map、Properties)

Spring 支持对数组、集合等复杂类型的注入:

java 复制代码
public class CollectionBean {
    private String[] array;
    private List<String> list;
    private Map<String, String> map;
    private Properties properties;

    // 提供set方法
    public void setArray(String[] array) { this.array = array; }
    public void setList(List<String> list) { this.list = list; }
    public void setMap(Map<String, String> map) { this.map = map; }
    public void setProperties(Properties properties) { this.properties = properties; }

    @Override
    public String toString() {
        return "CollectionBean{" +
                "array=" + Arrays.toString(array) +
                ", list=" + list +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}
XML 复制代码
<bean id="collectionBean" class="com.spring.demo.entity.CollectionBean">
    <!-- 数组注入 -->
    <property name="array">
        <array>
            <value>元素1</value>
            <value>元素2</value>
        </array>
    </property>
    <!-- List注入 -->
    <property name="list">
        <list>
            <value>列表项1</value>
            <value>列表项2</value>
        </list>
    </property>
    <!-- Map注入 -->
    <property name="map">
        <map>
            <entry key="key1" value="值1"/>
            <entry key="key2" value="值2"/>
        </map>
    </property>
    <!-- Properties注入 -->
    <property name="properties">
        <props>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>

四、多配置文件管理:大型项目的解耦方案

在大型项目中,单个applicationContext.xml会变得非常臃肿。Spring 支持多配置文件拆分与合并,让配置更清晰:

方式 1:主配置文件导入子配置文件

复制代码
<!-- applicationContext.xml(主配置) -->
<import resource="applicationContext-dao.xml"/>
<import resource="applicationContext-service.xml"/>

方式 2:加载时指定多个配置文件

复制代码
// 同时加载多个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext(
    "applicationContext-dao.xml", 
    "applicationContext-service.xml"
);

五、半注解与全注解开发:简化配置的终极方案

Spring 从 3.x 开始支持注解开发 ,大幅减少 XML 配置的工作量。我们可以根据场景选择半注解(XML + 注解)全注解模式。

1. 半注解开发:XML 配置 + 注解注入

步骤 1:开启组件扫描

applicationContext.xml中配置注解扫描,指定需要扫描的包:

复制代码
<context:component-scan base-package="com.spring.demo"/>
步骤 2:在类上添加注解
  • @Component:通用组件注解,可指定 Bean 的 id(不指定则默认类名首字母小写);
  • @Service:业务层组件(本质是@Component的别名);
  • @Repository:持久层组件(本质是@Component的别名);
  • @Controller:控制层组件(Web 环境下使用,本质是@Component的别名)。
java 复制代码
// 业务层:@Service注解,id默认为userService
@Service
public class UserServiceImpl implements UserService {
    @Override
    public void hello() {
        System.out.println("Hello, 注解开发!");
    }
}

// 持久层:@Repository注解,id默认为orderDao
@Repository
public class OrderDaoImpl implements OrderDao {
    @Override
    public void saveOrder() {
        System.out.println("DAO层保存订单");
    }
}
步骤 3:依赖注入注解
  • @Autowired:自动按类型注入,若类型冲突则按名称注入(需配合@Qualifier("beanId"));
  • @Resource:先按名称注入,再按类型注入(JDK 原生注解,需 JDK8 + 支持);
  • @Value:注入普通字符串或配置文件值。
java 复制代码
@Service
public class OrderServiceImpl implements OrderService {
    // 自动注入OrderDao(按类型匹配)
    @Autowired
    private OrderDao orderDao;

    // 注入普通字符串
    @Value("Spring半注解演示")
    private String msg;

    @Override
    public void saveOrder() {
        System.out.println("保存订单:" + msg);
        orderDao.saveOrder();
    }
}

2. 全注解开发:零 XML 配置

通过@Configuration@ComponentScan注解,完全替代 XML 配置:

java 复制代码
// 配置类,替代applicationContext.xml
@Configuration
// 组件扫描,等同于<context:component-scan>
@ComponentScan(basePackages = "com.spring.demo")
public class SpringConfig {
    // 若有其他配置(如数据源),可在此类中定义Bean
}
java 复制代码
// 测试类:加载配置类
public class AnnotationDemo {
    @Test
    public void testFullAnnotation() {
        // 加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean(UserService.class);
        userService.hello();
    }
}

六、AOP 面向切面编程:不修改业务代码实现功能增强

AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 的另一大杀器 ------ 它允许你在不修改业务代码的前提下,对方法进行功能增强(如日志、权限、事务等)。

1. AOP 核心概念

  • 切面(Aspect):封装的增强功能(如日志切面、事务切面);
  • 连接点(JoinPoint):可以被增强的方法(所有方法理论上都是连接点);
  • 切入点(Pointcut):实际被增强的方法(通过表达式指定);
  • 通知(Advice):增强的具体逻辑(如前置通知、后置通知等);
  • 织入(Weaving):将切面应用到目标对象的过程。

2. AOP 入门实战:日志切面

步骤 1:导入 AOP 依赖
XML 复制代码
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
步骤 2:编写业务接口与实现类
java 复制代码
public interface UserService {
    void addUser();
    void deleteUser();
}

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void addUser() {
        System.out.println("执行添加用户业务...");
    }
    @Override
    public void deleteUser() {
        System.out.println("执行删除用户业务...");
    }
}
步骤 3:编写切面类
java 复制代码
@Component  // 让Spring管理切面类
@Aspect     // 声明这是一个切面
public class LogAspect {
    // 切入点表达式:execution(返回值 包名.类名.方法名(参数))
    // * com.spring.demo.service.*.*(..) 表示:service包下所有类的所有方法,参数任意
    @Pointcut("execution(* com.spring.demo.service.*.*(..))")
    public void pointcut() {}

    // 前置通知:方法执行前增强
    @Before("pointcut()")
    public void beforeAdvice() {
        System.out.println("前置通知:记录方法开始执行时间...");
    }

    // 后置通知:方法正常执行后增强(异常时不执行)
    @AfterReturning("pointcut()")
    public void afterReturningAdvice() {
        System.out.println("后置通知:记录方法执行耗时...");
    }

    // 环绕通知:方法执行前后都增强(最灵活)
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前:记录日志开始...");
        // 执行目标方法
        Object result = joinPoint.proceed();
        System.out.println("环绕通知后:记录日志结束...");
        return result;
    }
}
步骤 4:开启 AOP 注解支持

在配置文件中开启 AOP 自动代理:

复制代码
<aop:aspectj-autoproxy/>
步骤 5:测试 AOP
java 复制代码
@Test
public void testAOP() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = context.getBean(UserService.class);
    userService.addUser();
}

运行后控制台输出:

复制代码
环绕通知前:记录日志开始...
前置通知:记录方法开始执行时间...
执行添加用户业务...
环绕通知后:记录日志结束...
后置通知:记录方法执行耗时...

可以看到,业务方法addUser()的执行被切面 "无侵入" 地增强了------ 这就是 AOP 的魅力!

七、总结:Spring 核心技术学习路径

Spring 的核心是IOC(控制反转)AOP(面向切面编程) ,掌握它们的关键在于理解 "将控制权交给 Spring,让框架帮你做更多事" 的设计理念。

  • 入门阶段:从配置文件方式的 IOC 和 DI 入手,理解 Spring 如何管理对象和依赖;
  • 进阶阶段:学习注解开发(半注解 / 全注解),减少 XML 配置的冗余;
  • 高阶阶段:深入 AOP 原理,结合事务、缓存等场景,实现业务功能的无侵入增强。

Spring 框架的生态极其庞大,但只要抓住 IOC 和 AOP 这两个核心,就能在 Java 开发的海洋中乘风破浪!

相关推荐
@爱学习的小趴菜6 小时前
Redis服务器配置
服务器·数据库·redis
初圣魔门首席弟子6 小时前
vscode多文件编程bug记录
java·vscode·bug
华仔啊6 小时前
提升 Java 开发效率的 5 个神级技巧,超过 90% 的人没用全!
java·后端
沐浴露z6 小时前
【JVM】详解 线程与协程
java·jvm
前路不黑暗@6 小时前
Java:继承与多态
java·开发语言·windows·经验分享·笔记·学习·学习方法
ZhengEnCi6 小时前
J1A-Java版本选择踩坑指南-为什么99%的人选错JDK版本?大厂Java开发者的版本选择内幕大公开
java
再难也得平6 小时前
微服务拆分之SpringCloud
java·spring cloud·微服务
Java水解6 小时前
Spring AI Alibaba 入门教程:快速集成大模型到Spring Boot应用
后端·spring
ypf52086 小时前
springboot DevTools热部署
java