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

Spring框架1

  • [Spring 框架的概述](#Spring 框架的概述)
  • [Spring 的 IOC 核心技术](#Spring 的 IOC 核心技术)
    • [Bean 管理的配置文件方式](#Bean 管理的配置文件方式)
    • [实例化 Bean 的三种方式](#实例化 Bean 的三种方式)
    • [DI 依赖注入](#DI 依赖注入)
    • 多配置文件方式

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 工程,导入坐标依赖

xml 复制代码
    <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>

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

java 复制代码
package com.qcby.service;

public interface UserService {
    public void hello();
}
java 复制代码
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 复制代码
<?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 属性指定的方法

xml 复制代码
    <bean id="userService" class="com.qcby.service.impl.UserServiceImpl" init-method="init"/>
java 复制代码
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 配置文件

java 复制代码
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 配置方式为:

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

实例化 Bean 的三种方式

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

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

  1. 静态工厂实例化方式
java 复制代码
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();
    }
}
xml 复制代码
    <!--静态工厂实例化-->
    <bean id="us" class="com.qcby.factory.StaticFactory" factory-method="createUs" />

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

  1. 动态工厂实例化方式
java 复制代码
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();
    }
}
xml 复制代码
    <!--动态工厂实例化-->
    <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 方法,编写配置文件完成属性值的注入
java 复制代码
package com.qcby.dao.impl;

import com.qcby.dao.OrderDao;

public class OrderDaoImpl implements OrderDao {

    public void saveOrder() {
        System.out.println("持久层:保存订单...");
    }
}
java 复制代码
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();
    }
}
xml 复制代码
    <!-- 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. 属性构造方法方式注入值
    对于类成员变量,有参构造函数注入
java 复制代码
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 +
                '}';
    }
}
xml 复制代码
    <!-- 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等的注入方式

java 复制代码
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 +
                '}';
    }
}
xml 复制代码
    <!--给集合属性注入值-->
    <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 的目录下又多创建了一个配置文件,现在是两个核心的配置文件,那么加载这两个配置文件的方式有两种

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

xml 复制代码
<import resource="applicationContext2.xml"/>

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

java 复制代码
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext2.xml");
相关推荐
葵野寺2 小时前
【RelayMQ】基于 Java 实现轻量级消息队列(七)
java·开发语言·网络·rabbitmq·java-rabbitmq
书院门前细致的苹果2 小时前
JVM 全面详解:深入理解 Java 的核心运行机制
java·jvm
上官浩仁3 小时前
springboot excel 表格入门与实战
java·spring boot·excel
Livingbody3 小时前
10分钟完成 ERNIE-4.5-21B-A3B-Thinking深度思考模型部署
后端
Hello.Reader3 小时前
从零到一上手 Protocol Buffers用 C# 打造可演进的通讯录
java·linux·c#
hzzzzzo04 小时前
微服务核心组件实战:Nacos 与 Ribbon 的应用
spring cloud·微服务·ribbon·nacos·架构
树码小子4 小时前
Java网络初识(4):网络数据通信的基本流程 -- 封装
java·网络
java1234_小锋4 小时前
RocketMQ的集群架构是怎样的?
架构·rocketmq·java-rocketmq
稻草人想看远方4 小时前
GC垃圾回收
java·开发语言·jvm