Spring框架1—Spring的IOC核心技术1

发布于:2025-09-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

Spring 框架的概述

Spring 是一个开源的企业级 Java 应用开发框架,由 Rod Johnson 于 2003 年基于其著作《Expert One-On-One J2EE Development and Design》中阐述的设计理念与原型开发而成。该框架的核心设计目标是解决企业级应用开发的复杂性,尤其聚焦于消除业务逻辑层与数据访问层、表现层等其他层级之间的紧耦合问题 —— 而这一目标的实现,核心在于将面向接口编程的思想贯穿于整个应用的设计与实现过程。

从架构特性来看,Spring 具备三个关键属性:其一,轻量级,即无需依赖重量级的 J2EE 容器即可运行,且核心组件体积小、资源消耗低;其二,分层架构,框架按功能划分为核心容器、AOP 模块、数据访问 / 集成模块等独立组件,使用者可根据需求选择性引入,而非强制依赖全量组件,避免功能冗余;其三,一站式,Spring 并非对现有技术的替代,而是通过整合与封装,为开发者提供一站式解决方案,覆盖了从 JavaSE 基础开发到 JavaEE 企业级应用开发的全流程需求,提供了从依赖注入到事务管理、从 Web 开发到数据持久化的完整解决方案支持。

Spring 框架的核心支柱是控制反转(IoC,Inversion of Control) 与面向切面编程(AOP,Aspect-Oriented Programming):

  • IoC 通过将对象的创建、依赖关系管理等职责从业务逻辑代码中剥离,交由容器统一管控,实现了组件间的解耦,提升了代码的可维护性与可测试性;
  • AOP 则通过将日志记录、事务管理、权限控制等横切关注点模块化,实现了业务逻辑与非业务逻辑的分离,增强了系统的扩展性。

Spring 框架的核心优势:

  • 方便解耦,简化依赖管理:基于 IoC 容器实现对象的生命周期与依赖关系的自动化管理,消除了手动创建对象与维护依赖的代码,降低了组件间的耦合度。
  • 支持 AOP 编程:提供完善的 AOP 实现,可通过声明式方式将横切关注点植入业务逻辑,避免代码侵入,提升系统模块化程度。
  • 声明式事务管理:基于 AOP 实现事务管理,开发者无需通过硬编码控制事务,仅需通过配置即可完成事务的定义、传播行为与隔离级别设置,简化了事务操作。
  • 增强可测试性:原生支持与 JUnit、TestNG 等测试框架的集成,通过注解可便捷地实现依赖注入,降低测试环境搭建的复杂度。
  • 兼容主流框架:设计上保持开放性,内部提供对 MyBatis、Hibernate 等 ORM 框架,以及 Quartz、Redis 等工具的集成支持,避免技术栈锁定。
  • 简化 JavaEE API 使用:对 JDBC、JavaMail、远程调用等复杂 JavaEE API 进行封装(如JdbcTemplate),屏蔽底层细节,降低 API 使用门槛。

Spring 的 IOC 核心技术

Spring 的 IOC(Inversion of Control,控制反转)是面向对象编程的重要设计原则,其核心思想是将对象的创建、依赖关系的管理等控制权从应用程序代码本身转移到 Spring 的 IOC 容器,实现了控制权的反转。这种机制的核心价值在于解耦,有效降低代码之间的直接耦合,从而提升系统的可维护性、可扩展性和可测试性。

Spring 的 IOC 容器是实现控制反转思想的核心载体,IoC 容器负责:

  • Bean 的创建与实例化:容器根据配置信息,自动创建应用程序所需的对象,开发者无需手动通过 new 关键字创建对象,而是由容器根据规则完成实例化;
  • 通过依赖注入配置依赖项:容器会自动分析并处理 Bean 之间的依赖关系;
  • 管理 Bean 的整个生命周期:从对象创建、初始化、使用、到最终的销毁,以及开发者无需关心对象的销毁时机和资源释放;
  • 读取配置元数据:容器会加载并解析所有配置源(XML、注解、Java 配置类等),将 Bean 的定义信息(如类名、作用域、依赖关系等)进行统一管理,作为创建和管理 Bean 的依据。

下面我们一起来编写 IOC 的入门程序

首先是需要创建 maven Java 工程,导入坐标依赖

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

编写接口和实现类,编写具体的实现方法

package com.qcby.service;

public interface UserService {
    public void hello();
}
package com.qcby.service.impl;

import com.qcby.service.UserService;

public class UserServiceImpl implements UserService {
    public void hello() {
        System.out.println("hello Ioc !!!");
    }
}

Spring 负责管理对象的创建过程,因此需要处理可实例化的类。由于 UserService 接口本身无法被实例化,交由 Spring 管理没有实际意义,因此 Spring 实际管理的是该接口的具体实现类 UserServiceImpl。

Bean 管理的配置文件方式

编写 Spring 核心配置文件,在 src 的 resources 目录下创建 applicationContext.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">

    <!--IOC 管理 bean-->
    <bean id="userService" class="com.qcby.service.impl.UserServiceImpl" />
</beans>

容器管理的对象称为 bean

  • id 属性即为 bean 起个名字,在约束中采用 ID 的约束,唯一,必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号
  • class 属性即为 bean 对象的全路径
  • scope 属性代表 Bean 的作用范围:
    singleton 单例
    prototype 多例
    request 应用在 Web 项目中,每次 HTTP 请求都会创建一个新的 Bean
    session 应用在 Web 项目中,同一个 HTTP Session 共享一个 Bean
  • Bean 对象的创建和销毁的两个属性配置
    Spring 初始化 bean 或销毁 bean 时,有时需要作一些处理工作,因此 spring 可以在创建和拆卸 bean 的时候调用 bean 的两个生命周期方法
    init-method,当 bean 被载入到容器的时候调用 init-method 属性指定的方法
    destroy-method,当 bean 从容器中删除的时候调用 destroy-method 属性指定的方法

把 log4j.properties 的配置文件拷贝到 resources 目录下,做为 log4j 的日志配置文件

在这里插入图片描述

配置当 bean 被载入到容器的时候调用 init-method 属性指定的方法

    <bean id="userService" class="com.qcby.service.impl.UserServiceImpl" init-method="init"/>
public class UserServiceImpl implements UserService {
    public void hello() {
        System.out.println("hello Ioc !!!");
    }
    public void init(){
        System.out.println("对象创建完成,进行初始化操作...");
    }
}

Spring 提供了两种类型的容器:

  • BeanFactory 容器
    是提供依赖注入支持的基本容器,在这种情况下只有在显式请求时才会实例化 bean,重量轻适用于资源受限的环境
  • ApplicationContext 容器
    是建立在 BeanFactory 之上的高级容器,它包括 BeanFactory 的所有功能,并添加了额外的功能,在这种情况下 Bean 是在启动时创建和配置的

ApplicationContext 是 Spring 的核心工厂接口,,主要用于获取容器中的 Bean 实例。该接口下有两个具体的实现类:
ClassPathXmlApplicationContext,加载类路径下的 Spring 配置文件
FileSystemXmlApplicationContext,加载本地磁盘下的 Spring 配置文件

package com.qcby;

import com.qcby.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo1 {

    @Test
    public void run1(){
        // 使用 Spring 的工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 通过工厂获得类
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.hello();
    }
}

需要注意的是 Spring 容器中 bean 的默认作用域是 singleton(单例),当一个 bean 被定义为单例时,Spring 容器在整个应用生命周期内只会创建该 bean 的一个实例,所有获取该 bean 的请求(getBean())都会返回同一个实例

在这里插入图片描述

Spring 的单例作用域是容器级别的,每个 applicationContext 容器会独立维护自己的单例 bean 实例,不同容器之间的 bean 是相互独立的

在这里插入图片描述

bean 的作用域 scope 定义为 prototype(原型),当每次调用 getBean() 时,都会创建一个新的实例
注解配置方式为:@Scope("prototype"),XML 配置方式为:

    <bean id="userService" class="com.qcby.service.impl.UserServiceImpl" scope="prototype" init-method="init"/>

在这里插入图片描述

实例化 Bean 的三种方式

  1. 默认是无参数的构造方法
 <bean id="us" class="com.qcby.service.impl.UserServiceImpl"/>

在 XML 配置中,通过 factory-method 指定方法创建实例

  1. 静态工厂实例化方式
package com.qcby.factory;

import com.qcby.service.UserService;
import com.qcby.service.impl.UserServiceImpl;

public class StaticFactory {
    // 静态工厂方式
    public static UserService createUs(){
        System.out.println("通过静态工厂的方式创建 UserServiceImpl 对象...");
        return new UserServiceImpl();
    }
}
    <!--静态工厂实例化-->
    <bean id="us" class="com.qcby.factory.StaticFactory" factory-method="createUs" />

工厂类中的创建方法是静态方法,不需要创建工厂类的实例,Spring 容器会调用 UserFactory.createUser() 来生成 user 实例

  1. 动态工厂实例化方式
package com.qcby.factory;

import com.qcby.service.UserService;
import com.qcby.service.impl.UserServiceImpl;

public class DynamicFactoty {
    public UserService createUs(){
        System.out.println("动态工厂实例化方式...");
        return new UserServiceImpl();
    }
}
    <!--动态工厂实例化-->
    <bean id="dynamicFactoty" class="com.qcby.factory.DynamicFactoty" />
    <bean id="us1" factory-bean="dynamicFactoty" factory-method="createUs" />

工厂类中的创建方法是非静态方法,必须先实例化工厂类,再通过工厂实例调用方法

DI 依赖注入

DI(Dependency Injection,依赖注入)是实现 IOC(控制反转)思想的技术,在 Spring 框架负责创建 Bean 对象时,动态的将依赖对象 / 属性 注入到 Bean 组件中

  1. 属性的 set 方法注入值
    编写属性提供该属性对应的 set 方法,编写配置文件完成属性值的注入
package com.qcby.dao.impl;

import com.qcby.dao.OrderDao;

public class OrderDaoImpl implements OrderDao {

    public void saveOrder() {
        System.out.println("持久层:保存订单...");
    }
}
package com.qcby.service.impl;

import com.qcby.dao.OrderDao;
import com.qcby.service.OrderService;

public class OrderServiceImpl implements OrderService {
    // 编写成员属性
    private OrderDao orderDao;
    // 一定需要提供该属性的 set 方法,IOC 容器底层就通过属性的 set 方法方式注入值
    public void setOrderDao(OrderDao orderDao) {
        this.orderDao = orderDao;
    }

    private String msg;
    private int age;

    public void setMsg(String msg) {
        this.msg = msg;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void saveOrder() {
        System.out.println("业务层:保存订单..."+msg+" - "+age);
        // 调用
        orderDao.saveOrder();
    }
}
    <!-- DI 依赖注入,通过属性的 set 方法注入值-->
    <bean id="os" class="com.qcby.service.impl.OrderServiceImpl">
        <property name="orderDao" ref="od" />
        <property name="msg" value="你好" />
        <property name="age" value="30" />
    </bean>
    <bean id="od" class="com.qcby.dao.impl.OrderDaoImpl"></bean>
  1. 属性构造方法方式注入值
    对于类成员变量,有参构造函数注入
package com.qcby.model;

public class Car {

    private String cname;
    private Double money;

    public Car(String cname, Double money) {
        this.cname = cname;
        this.money = money;
    }
    @Override
    public String toString() {
        return "Car{" +
                "cname='" + cname + '\'' +
                ", money=" + money +
                '}';
    }
}
    <!-- DI 依赖注入,通过有参构造器注入值-->
    <bean id="car" class="com.qcby.model.Car">
        <constructor-arg name="cname" value="奔驰" />
        <constructor-arg name="money" value="400000" />
    </bean>

数组,集合(List,Set,Map),Properties等的注入方式

package com.qcby.model;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionBean {

    private String [] strs;
    public void setStrs(String[] strs) {
        this.strs = strs;
    }
    private List<String> list;
    public void setList(List<String> list) {
        this.list = list;
    }
    private Map<String,String> map;
    public void setMap(Map<String, String> map) {
        this.map = map;
    }
    private Properties properties;
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    @Override
    public String toString() {
        return "CollectionBean{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}
    <!--给集合属性注入值-->
    <bean id="collectionBean"
          class="com.qcby.model.CollectionBean">
        <property name="strs">
            <array>
                <value>美美</value>
                <value>小凤</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>熊大</value>
                <value>熊二</value>
            </list>
        </property>
        <property name="map">
            <map>
                <entry key="aaa" value="老王"/>
                <entry key="bbb" value="小王"/>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>

多配置文件方式

在 src 的目录下又多创建了一个配置文件,现在是两个核心的配置文件,那么加载这两个配置文件的方式有两种

主配置文件中包含其他的配置文件

<import resource="applicationContext2.xml"/>

工厂创建的时候直接加载多个配置文件

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");