Java——Spring 非注解开发:IoC/DI 与 Bean 管理实战(含第三方组件整合)

发布于:2025-06-27 ⋅ 阅读:(12) ⋅ 点赞:(0)

在 Spring 框架的学习中,基于 XML 的非注解开发模式是理解框架底层机制的核心路径。本文结合黑马程序员 SSM 教程内容,系统解析 IoC(控制反转)、DI(依赖注入)的核心原理,深度演示 Bean(含第三方组件)的配置与管理,并通过企业级案例展现实战技巧。

一、IoC(控制反转):从手动创建到容器托管

1. 核心思想与容器本质​

IoC 的核心是将对象的创建与生命周期管理从代码中剥离,交由 Spring 容器统一处理。传统开发中通过new关键字创建对象,而在 IoC 模式下,开发者只需在 XML 中声明 Bean,由容器通过反射、工厂模式等机制生成实例。Spring 提供BeanFactory(基础容器)和ApplicationContext(增强容器,支持国际化、事件机制)两种实现,企业开发中通常使用后者。

2. Bean 定义的三种方式

(1)无参构造函数(默认方式)

<bean id="userService" class="com.service.UserServiceImpl"/>

容器通过反射调用类的无参构造方法实例化对象,适用于大多数简单场景。

(2)静态工厂方法

<bean id="car" class="com.factory.StaticCarFactory" factory-method="createCar"/>

通过factory-method指定静态方法创建对象,适用于需要复用复杂创建逻辑的场景。

(3)实例工厂方法

<bean id="carFactory" class="com.factory.InstanceCarFactory"/><bean id="car" factory-bean="carFactory" factory-method="createCar"/>

需先定义工厂 Bean,再通过factory-bean指定实例工厂,适用于工厂本身需要注入其他依赖的场景。

3. 容器操作核心代码

// 初始化容器(加载XML配置)​
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 获取Bean(两种方式)​
UserService userService1 = (UserService) context.getBean("userService");UserService userService2 = context.getBean("userService", UserService.class);

二、DI(依赖注入):解耦对象依赖关系

1. 两种核心注入方式

(1)构造函数注入(适合依赖关系固定的场景)

<bean id="userService" class="com.service.UserServiceImpl"><constructor-arg name="userDao" ref="userDao"/> <!-- 按顺序注入引用类型 --><constructor-arg name="timeout" value="3000"/> <!-- 按名称注入基本类型 --></bean>

(2)Setter 方法注入(适合依赖关系可变的场景)

<bean id="userService" class="com.service.UserServiceImpl">
    <property name="userDao" ref="userDao"/> <!-- 注入引用类型 -->
    <property name="version" value="1.0"/> <!-- 注入基本类型 -->
</bean>

2. 高级注入技巧

(1)集合类型注入(支持 List/Set/Map/Props)

<bean id="roleService" class="com.service.RoleServiceImpl">
    <property name="adminRoles">
        <list>
            <value>ROLE_ADMIN</value>
            <value>ROLE_SUPER</value>
        </list>
    </property>
    <property name="userProps">
        <props>
            <prop key="timeout">3000</prop>
            <prop key="retries">3</prop>
        </props>
    </property>
</bean>

(2)自动装配(Autowiring)

通过autowire属性简化配置,常用模式:​

  • byName:按属性名匹配 Bean 的id​
  • byType:按属性类型匹配唯一 Bean(需保证容器中该类型唯一)
<bean id="userService" class="com.service.UserServiceImpl" autowire="byType"/>

3. 第三方 Bean 的依赖注入

以数据库连接池 Druid 为例,演示如何注入第三方组件的复杂配置:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/> <!-- 注入JDBC驱动 --><property name="url" value="${jdbc.url}"/> <!-- 注入数据库URL --><property name="maxActive" value="20"/> <!-- 注入连接池参数 --></bean>

关键点: 第三方 Bean 的属性需与类中的 Setter 方法严格对应(如setDriverClassName对应driverClassName属性)。

三、Bean 管理:生命周期、作用域与高级特性

1. 生命周期控制(初始化与销毁回调)​

(1)XML 配置方式(推荐)​

<bean id="dataSource"class="com.alibaba.druid.pool.DruidDataSource"​
      init-method="init" <!-- 初始化方法 -->​
      destroy-method="close"> <!-- 销毁方法(仅单例Bean生效) --><!-- 配置省略 --></bean>

(2)接口实现方式(侵入性强,不推荐)​

public class UserService implements InitializingBean, DisposableBean {@Overridepublic void afterPropertiesSet() throws Exception {// 初始化逻辑​
    }@Overridepublic void destroy() throws Exception {// 销毁逻辑​
    }}

2. 作用域控制(4 大核心作用域)

作用域 说明 典型场景
singleton 容器中唯一实例(默认),线程安全,适用于无状态 Bean(Service/DAO) 业务逻辑层、数据访问层
prototype 每次获取创建新实例,非线程安全,适用于有状态 Bean(命令对象) 需保存用户状态的组件
request 每次 HTTP 请求创建新实例(仅 Web 环境) Web 层请求参数处理器
session 每个 HTTP 会话共享一个实例(仅 Web 环境) 会话级缓存组件
<bean id="userSession" class="com.SessionBean" scope="session"/> <!-- Web环境专用 -->

3. 第三方 Bean 的继承与别名​

(1)配置继承(复用公共参数)

<!-- 定义基础数据源配置 -->
<bean id="baseDataSource" abstract="true">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
</bean>
<!-- 子类继承并扩展 -->
<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource" parent="baseDataSource">
    <property name="username" value="${jdbc.master.user}"/>
</bean>

(2)别名定义(简化 Bean 引用)

<alias name="userService" alias="userServiceProxy"/> <!-- 为同一Bean定义多个名称 -->

四、企业级实战:SSM 整合与第三方组件深度集成​

1. SSM 框架核心整合步骤(XML 版)​

(1)Spring 与 MyBatis 整合

<!-- 1. 配置数据源(Druid-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2. 配置SqlSessionFactoryMyBatis第三方Bean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/> <!-- 映射文件路径 -->
</bean>
<!-- 3. 扫描Mapper接口(自动生成代理Bean-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.dao"/> <!-- Mapper接口所在包 -->
</bean>

(2)Spring 与 SpringMVC 整合

<!-- web.xml配置DispatcherServlet -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
</servlet>
<!-- springmvc.xml配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

2. 复杂场景:多数据源动态切换(以 Druid 为例)​

(1)定义目标数据源

<bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 主库配置 -->
</bean>
<bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 从库配置 -->
</bean>

(2)配置动态数据源路由(Spring 原生支持)

<bean id="dynamicDataSource" class="org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource">
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <entry key="master" value-ref="masterDataSource"/>
            <entry key="slave" value-ref="slaveDataSource"/>
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="masterDataSource"/> <!-- 默认数据源 -->
</bean>

(3)实现数据源切换逻辑(线程安全)

public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    public static void setDataSourceType(String type) {
        CONTEXT_HOLDER.set(type);
    }
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
}

3. 第三方日志框架(Log4j)整合

<!-- 配置ConsoleAppender(输出到控制台) -->
<bean id="consoleAppender" class="org.apache.log4j.ConsoleAppender">
    <property name="layout" ref="patternLayout"/> <!-- 注入日志格式 -->
</bean>
<bean id="patternLayout" class="org.apache.log4j.PatternLayout">
    <property name="conversionPattern" value="%d [%t] %-5p %c - %m%n"/> <!-- 日志模板 -->
</bean>
<!-- 配置根Logger(关联Appender-->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetClass" value="org.apache.log4j.Logger"/>
    <property name="targetMethod" value="getRootLogger"/>
    <property name="arguments">
        <list>
            <value>INFO</value> <!-- 日志级别 -->
            <ref bean="consoleAppender"/> <!-- 关联Appender -->
        </list>
    </property>
</bean>

五、非注解模式的优缺点与适用场景​

1. 核心优势​

  • 配置可见性:所有 Bean 定义集中在 XML,依赖关系一目了然,适合复杂依赖场景。​
    兼容性:可无缝整合不支持注解的老旧系统或第三方库(如遗留数据库驱动、传统日志框架)。​
  • 学习价值:深入理解 Spring 底层机制(反射、工厂模式、容器生命周期)的最佳实践。​

2. 局限性​

  • 配置冗余:大量 XML 标签导致文件臃肿,修改时需频繁切换上下文。​
    类型安全问题:编译期无法检查配置错误(如class路径错误、属性名拼写错误),需运行时验证。​
  • 维护成本:复杂继承关系可能导致配置混乱,团队协作时需严格规范命名与结构。​

3. 适用场景​

  • 遗留系统迁移:逐步将传统 JavaEE 项目纳入 Spring 生态,避免大规模代码改动。​
  • 基础设施管理:数据源、事务管理器、缓存框架等底层组件的配置,适合在 XML 中统一管理。​
  • 教学与底层研究:理解 Spring IoC/DI 核心原理,必须掌握 XML 配置模式。​

六、总结:从 XML 到注解的演进与融合​

非注解开发模式是 Spring 的 “基石”,其核心是通过 XML 文件显式定义 Bean 的行为与依赖关系。尽管现代开发以注解(@Component/@Autowired)为主流,但 XML 在以下场景仍不可替代:​

  • 第三方组件整合:当第三方库不支持注解或需要复杂初始化参数时,XML 是最直接的配置方式。​
  • 全局配置管理:如多数据源、事务​

网站公告

今日签到

点亮在社区的每一天
去签到