【Java实战㉛】解锁Spring框架实战:深入IOC容器的奇妙之旅
目录
- 一、Spring 概述
- 1.1 Spring 核心思想
- 1.2 Spring 优势
- 1.3 Spring 体系结构
- 二、IOC 容器基础
- 2.1 IOC 概念
- 2.2 Spring Bean
- 2.3 Bean 的创建方式
- 2.4 Bean 的依赖注入
- 三、Spring 配置方式实战
- 3.1 XML 配置文件
- 3.2 注解配置
- 3.3 Java 配置类
- 3.4 IOC 容器实战案例
一、Spring 概述
1.1 Spring 核心思想
Spring 框架的核心思想主要体现在两个方面:IOC(Inversion of Control,控制反转)和 AOP(Aspect-Oriented Programming,面向切面编程)。
IOC,即控制反转,是一种设计原则,它将对象的创建和依赖关系的管理从对象本身转移到外部容器,也就是 Spring 容器。在传统的编程方式中,如果一个对象 A 需要使用对象 B,那么对象 A 通常会在内部直接创建对象 B 的实例。例如:
public class UserService {private UserDao userDao = new UserDaoImpl();public void saveUser() {userDao.save();}
}
在这个例子中,UserService 类直接依赖并创建了 UserDaoImpl 类的实例,这就导致了 UserService 与 UserDaoImpl 之间的紧密耦合。如果后续需要更换 UserDao 的实现类,就必须修改 UserService 的代码。
而在 IOC 中,对象的创建和依赖关系的管理被反转,由 Spring 容器来负责。对象 A 不再直接创建对象 B,而是通过容器或框架来获取对象 B 的实例。比如使用 Spring 框架后,代码可以改为:
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public void saveUser() {userDao.save();}
}
然后在 Spring 的配置文件或使用注解的方式来告诉 Spring 容器如何创建 UserDao 的实例,并将其注入到 UserService 中。这样,UserService 就不再关心 UserDao 的具体创建过程和实现类,实现了对象之间的解耦。
AOP,即面向切面编程,它的核心思想是将横切关注点(如日志记录、事务管理、权限校验等)与业务逻辑分离,通过切面(Aspect)来统一管理这些横切逻辑。在传统的开发中,这些横切逻辑往往会分散在各个业务方法中,导致代码的重复和臃肿。例如,在多个业务方法中都需要进行日志记录,就会出现大量重复的日志记录代码:
public class UserService {public void saveUser() {System.out.println("开始记录日志");// 业务逻辑System.out.println("保存用户信息");System.out.println("结束记录日志");}public void updateUser() {System.out.println("开始记录日志");// 业务逻辑System.out.println("更新用户信息");System.out.println("结束记录日志");}
}
使用 AOP 后,可以将日志记录等横切逻辑封装成一个切面,然后通过配置或注解的方式将其应用到需要的业务方法上。例如:
@Aspect
@Component
public class LogAspect {@Before("execution(* com.example.service.*.*(..))")public void logBefore(JoinPoint joinPoint) {System.out.println("方法[" + joinPoint.getSignature().getName() + "]开始执行");}@AfterReturning("execution(* com.example.service.*.*(..))")public void logAfter(JoinPoint joinPoint) {System.out.println("方法[" + joinPoint.getSignature().getName() + "]执行完成");}
}
在这个例子中,LogAspect 是一个切面类,通过 @Before 和 @AfterReturning 注解定义了在目标方法执行前和执行后的通知逻辑。execution(* com.example.service..(…)) 是切入点表达式,用于指定哪些方法需要应用这些通知。这样,业务方法中就不再需要重复的日志记录代码,实现了业务逻辑与横切逻辑的分离,提高了代码的可维护性和可重用性。
1.2 Spring 优势
Spring 框架具有众多优势,在实际项目开发中发挥着重要作用。
- 解耦:通过 IOC 思想,Spring 将对象的创建和依赖关系的管理交给容器,使得对象之间的耦合度大大降低。例如在一个电商项目中,订单服务(OrderService)依赖于商品服务(ProductService)和用户服务(UserService)。如果不使用 Spring 的 IOC,订单服务中可能会直接创建商品服务和用户服务的实例,这就导致订单服务与商品服务、用户服务紧密耦合。而使用 Spring 的 IOC 后,订单服务只需声明对商品服务和用户服务的依赖,由 Spring 容器在运行时将相应的实例注入进来。这样,当商品服务或用户服务的实现发生变化时,订单服务无需修改代码,只需要修改 Spring 的配置文件或注解即可,提高了代码的灵活性和可维护性。
- 简化开发:Spring 提供了丰富的功能和模块,如声明式事务管理、数据访问抽象层等,大大简化了开发过程。以声明式事务管理为例,在传统的 JDBC 开发中,需要手动编写大量的事务处理代码,包括事务的开始、提交和回滚等操作。而使用 Spring 的声明式事务管理,只需要在配置文件或方法上添加相应的注解,就可以轻松实现事务的管理。例如:
@Service
@Transactional
public class UserService {// 业务方法,无需手动处理事务public void saveUser(User user) {// 数据库操作}
}
在这个例子中,@Transactional 注解表示该方法需要进行事务管理,Spring 会在方法执行时自动处理事务的相关操作,开发者无需关心具体的事务实现细节,提高了开发效率。
- 支持多种框架整合:Spring 可以与各种优秀的开源框架进行无缝整合,如 Hibernate、MyBatis、Struts 等。在一个企业级项目中,可能会使用 Spring 作为核心框架,同时结合 Hibernate 进行数据库持久化操作,结合 Struts 进行 Web 层的开发。Spring 提供了统一的配置和管理方式,使得这些框架能够协同工作,发挥各自的优势。例如,通过 Spring 的配置文件,可以方便地配置 Hibernate 的数据源、事务管理器等,同时将 Hibernate 的 SessionFactory 注入到需要的服务层中,实现数据的持久化操作。这种框架整合的能力,使得开发者可以根据项目的需求选择最合适的技术栈,提高项目的开发效率和质量。
1.3 Spring 体系结构
Spring 框架是一个庞大且功能丰富的体系结构,它由多个模块组成,每个模块都有其特定的功能和职责,这些模块相互协作,共同为开发者提供了强大的开发支持。
- Core 模块:提供了框架的基本组成部分,包含 IOC 和依赖注入功能,是整个 Spring 框架的基础。它负责创建、配置和管理对象,通过反射机制实现对象的实例化和依赖关系的注入。例如,在一个简单的 Spring 项目中,通过 Core 模块可以将一个普通的 Java 对象(POJO)配置成 Spring 容器管理的 Bean,实现对象的生命周期管理和依赖注入。
- Context 模块:建立在 Core 和 Beans 模块提供的坚实基础上,它是访问定义和配置的任何对象的媒介,是 Spring 的 IOC 容器的具体实现。ApplicationContext 接口是 Context 模块的焦点,它提供了一个框架式的对象访问方式,类似于一个 JNDI 注册表。Context 模块不仅继承了 Core 和 Beans 模块的功能,还添加了支持国际化、事件传播、资源加载等功能。在一个 Web 应用中,可以使用 Context 模块来加载配置文件、管理应用程序的上下文信息,并且可以方便地获取和使用 Spring 容器中的 Bean。
- AOP 模块:提供了面向切面的编程实现,允许开发者定义方法拦截器和切入点,以便将那些与业务逻辑无关,但又需要在多个业务模块中重复使用的功能(如日志记录、事务管理、权限校验等)进行分离和统一管理。通过 AOP,开发者可以在不修改原有业务代码的情况下,为业务方法添加额外的功能。例如,通过 AOP 模块可以定义一个日志切面,对所有业务方法的执行进行日志记录,而无需在每个业务方法中添加日志代码。
- DAO 模块:即数据访问对象(Data Access Object)模块,它提供了对各种数据访问技术的抽象和封装,包括 JDBC、Hibernate、JPA 等,使得开发者可以使用统一的方式来访问不同类型的数据库,而无需关心具体的数据访问实现细节。在一个数据库操作频繁的项目中,DAO 模块可以帮助开发者将数据访问层的代码进行封装和抽象,提高代码的可维护性和可复用性。例如,通过 DAO 模块可以定义一个 UserDAO 接口,然后使用不同的实现类(如基于 JDBC 的 UserDAOImpl 或基于 Hibernate 的 UserHibernateDAO)来实现对用户数据的访问,上层业务代码只需要依赖 UserDAO 接口即可,而不需要关心具体的实现类。
除了上述模块外,Spring 还包括 Web 模块(用于 Web 应用开发)、ORM 模块(支持对象关系映射)、Messaging 模块(提供消息处理功能)等,这些模块相互配合,共同构成了 Spring 强大的功能体系,满足了不同类型项目的开发需求。
二、IOC 容器基础
2.1 IOC 概念
IOC(Inversion of Control)即控制反转,是一种设计原则,其核心思想是将对象的创建和依赖关系的管理从应用程序代码本身转移到外部的 Spring 容器中。在传统的编程方式中,对象之间的依赖关系通常由对象本身负责管理。例如,假设有一个 UserService 类,它依赖于 UserDao 类来进行数据库操作,传统的实现方式如下:
public class UserService {private UserDao userDao = new UserDaoImpl();public void saveUser() {userDao.save();}
}
在这段代码中,UserService 类不仅负责业务逻辑,还负责创建和管理 UserDao 的实例,这就导致了 UserService 与 UserDaoImpl 之间存在紧密的耦合关系。如果后续需要更换 UserDao 的实现类,比如换成 UserDaoNewImpl,就必须修改 UserService 类的代码,这对于大型项目来说,维护成本极高。
而在 Spring 的 IOC 模式下,对象的创建和依赖关系的管理被反转给了 Spring 容器。UserService 类不再需要自己创建 UserDao 的实例,而是由 Spring 容器负责创建并注入。代码可以改为:
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}public void saveUser() {userDao.save();}
}
然后在 Spring 的配置文件(如 applicationContext.xml)或使用注解的方式来告诉 Spring 容器如何创建 UserDao 的实例,并将其注入到 UserService 中。这样,UserService 类只关注业务逻辑,而不关心 UserDao 的具体创建过程和实现类,实现了对象之间的解耦,提高了代码的可维护性和可扩展性。
2.2 Spring Bean
在 Spring 框架中,Bean 是指那些被 Spring IoC 容器实例化、配置和管理的对象。简单来说,Spring Bean 就是应用程序中的各个组件,它们可以是业务逻辑组件(如 Service 层的类)、数据访问组件(如 DAO 层的类)或其他任何需要由 Spring 容器管理的类。
Bean 的定义
可以通过多种方式定义一个 Spring Bean,常见的有基于 XML 配置和基于注解配置。
- 基于 XML 配置:在 applicationContext.xml 文件中,使用 <bean> 标签来定义一个 Bean,例如:
<bean id="userService" class="com.example.service.UserService"><property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
在这个例子中,id 属性为 Bean 指定了一个唯一的标识符,class 属性指定了 Bean 的实现类。<property> 标签用于注入依赖,name 属性指定了目标 Bean 的属性名称,ref 属性引用了另一个 Bean 作为依赖。
- 基于注解配置:在类上使用 @Component、@Service、@Repository、@Controller 等注解来定义一个 Bean。例如:
@Service
public class UserService {@Autowiredprivate UserDao userDao;// 业务方法
}
@Repository
public class UserDaoImpl implements UserDao {// 数据访问方法
}
@Service 注解用于标记业务逻辑层的类,@Repository 注解用于标记数据访问层的类,Spring 容器在启动时会自动扫描这些注解,并将对应的类注册为 Bean。
Bean 的作用域
Spring 框架提供了多种作用域来管理 Bean 的生命周期,常见的作用域有:
- 单例(Singleton):这是 Spring Bean 的默认作用域,在整个 Spring IoC 容器中,只会存在一个该 Bean 的实例。对于无状态的工具类、服务类(如 Service、DAO)等,通常使用单例作用域,以节省资源。例如,UserService 类如果使用单例作用域,在整个应用程序运行期间,Spring 容器只会创建一个 UserService 的实例,所有对 UserService 的引用都指向这个唯一的实例。
- 原型(Prototype):每次请求该 Bean 时,Spring 容器都会创建一个新的实例。适用于需要保持状态的 Bean,比如用户购物车,每个用户的购物车都是独立的,所以可以将购物车对应的 Bean 设置为原型作用域。
- 请求(Request):在一次 HTTP 请求中,会创建一个 Bean 实例,该作用域仅在基于 Web 的 Spring ApplicationContext 情形下有效。例如,在处理一个 HTTP 请求时,如果某个 Bean 的作用域是 Request,那么在这个请求的处理过程中,Spring 容器会创建一个该 Bean 的实例,请求处理完成后,这个实例就会被销毁。
- 会话(Session):在一个 HTTP Session 中,会创建一个 Bean 实例,同样仅在基于 Web 的 Spring ApplicationContext 情形下有效。比如,在用户登录后,将用户的会话信息封装成一个 Bean,设置为 Session 作用域,那么在用户的整个会话期间,这个 Bean 实例会一直存在,直到会话结束。
Bean 的生命周期
Spring Bean 的生命周期可以概括为以下几个阶段:
- 实例化:Spring 通过反射机制调用 Bean 的构造方法,创建一个 “原始对象”,此时对象还未填充属性。例如,对于 UserService 类,如果它有一个无参构造函数,Spring 会调用这个无参构造函数来创建 UserService 的实例。
- 属性赋值:Spring 通过依赖注入(DI)为 Bean 的属性赋值,例如使用 @Autowired、@Value 等注解进行属性注入。在 UserService 类中,如果它依赖于 UserDao,并且使用 @Autowired 注解进行注入,那么在这个阶段,Spring 会将 UserDao 的实例注入到 UserService 中。
- 初始化:Spring 提供了多种扩展点来进行初始化操作。
如果 Bean 实现了 InitializingBean 接口,Spring 会调用其 afterPropertiesSet 方法。
如果使用了 @PostConstruct 注解,标记的方法会在初始化完成后执行。
还可以通过在配置文件中使用 init-method 属性指定初始化方法。
例如,UserService 类实现了 InitializingBean 接口,那么在属性赋值完成后,Spring 会调用 UserService 的 afterPropertiesSet 方法,开发者可以在这个方法中进行一些初始化操作,如加载配置文件、初始化连接池等。 - 使用:经过初始化后,Bean 就可以被应用程序使用了,开发者可以通过 Spring 容器获取 Bean 实例,并调用其方法来完成业务逻辑。
- 销毁:当 Spring 容器关闭时,如果 Bean 实现了 DisposableBean 接口,Spring 会调用其 destroy 方法;如果使用了 @PreDestroy 注解,标记的方法会在销毁前执行;还可以通过在配置文件中使用 destroy-method 属性指定销毁方法。例如,对于一些需要释放资源的 Bean,如数据库连接池、文件句柄等,可以在销毁方法中进行资源的释放操作。
2.3 Bean 的创建方式
在 Spring 中,Bean 的创建方式主要有以下三种:
构造方法创建
这是最常用的一种创建 Bean 的方式,Spring 容器通过调用 Bean 类的构造方法来创建实例。在配置文件中,只需指定 Bean 的 class 属性即可。例如:
<bean id="userService" class="com.example.service.UserService"/>
Spring 会自动调用 UserService 类的无参构造函数(如果存在)来创建实例。如果 UserService 类只有带参数的构造函数,那么可以使用 <constructor-arg> 标签来传递参数,例如:
<bean id="userService" class="com.example.service.UserService"><constructor-arg name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
在这个例子中,UserService 的构造函数需要一个 UserDao 类型的参数,通过 <constructor-arg> 标签的 name 属性指定参数名,ref 属性引用了 userDao Bean 作为参数值。
静态工厂创建
使用静态工厂方法来创建 Bean 实例,Bean 配置中的 class 属性不再是 Bean 实例的实现类,而是静态工厂类,同时还需要使用 factory-method 属性来指定所创建的静态工厂方法。例如:
<bean id="userService" class="com.example.factory.UserServiceFactory" factory-method="createUserService"/>
对应的静态工厂类 UserServiceFactory 如下:
public class UserServiceFactory {public static UserService createUserService() {return new UserService();}
}
在这个例子中,Spring 容器会调用 UserServiceFactory 的静态方法 createUserService 来创建 UserService 的实例。
实例工厂创建
先创建工厂类的实例,然后通过该实例的方法来创建 Bean 实例。在配置文件中,需要使用 factory-bean 属性指定工厂 Bean 的 id,factory-method 属性指定实例工厂的工厂方法。例如:
<bean id="userServiceFactory" class="com.example.factory.UserServiceFactory"/>
<bean id="userService" factory-bean="userServiceFactory" factory-method="createUserService"/>
对应的实例工厂类 UserServiceFactory 如下:
public class UserServiceFactory {public UserService createUserService() {return new UserService();}
}
在这个例子中,首先创建 userServiceFactory Bean 的实例,然后通过该实例调用 createUserService 方法来创建 UserService 的实例。
2.4 Bean 的依赖注入
依赖注入(Dependency Injection,简称 DI)是 IOC 的具体实现方式,它通过在类中注入依赖对象的方式来解决对象间的依赖关系。Spring 支持多种依赖注入方式,包括:
setter 注入
通过调用 Bean 的 setter 方法来注入依赖对象。在配置文件中,使用 <property> 标签来进行配置。例如:
<bean id="userService" class="com.example.service.UserService"><property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
在 UserService 类中,需要为 userDao 属性提供对应的 setter 方法:
public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}// 业务方法
}
Spring 容器在创建 userService Bean 时,会调用 setUserDao 方法,将 userDao Bean 注入到 UserService 中。
构造器注入
通过调用 Bean 的构造函数来注入依赖对象。在配置文件中,使用 <constructor-arg> 标签来传递参数。例如:
<bean id="userService" class="com.example.service.UserService"><constructor-arg name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
在 UserService 类中,需要定义一个带有 UserDao 参数的构造函数:
public class UserService {private UserDao userDao;public UserService(UserDao userDao) {this.userDao = userDao;}// 业务方法
}
Spring 容器在创建 userService Bean 时,会调用这个构造函数,将 userDao Bean 作为参数传入。
自动注入
Spring 可以根据类型或名称自动解析和注入依赖关系,通过在需要注入的字段、构造函数或 Setter 方法上使用 @Autowired 注解来实现。例如:
@Service
public class UserService {@Autowiredprivate UserDao userDao;// 业务方法
}
@Autowired 注解默认是按照类型进行注入的,如果容器中存在多个相同类型的 Bean,可以使用 @Qualifier 注解来指定具体要注入的 Bean 的名称。例如:
@Service
public class UserService {@Autowired@Qualifier("userDaoImpl")private UserDao userDao;// 业务方法
}
除了 @Autowired,还可以使用 @Resource 注解进行自动注入,@Resource 注解默认是按照名称进行注入的,如果没有指定名称,则按照类型进行注入。
三、Spring 配置方式实战
3.1 XML 配置文件
在 Spring 项目中,XML 配置文件是一种传统且常用的配置方式,applicationContext.xml 是最常见的配置文件名。通过该文件,可以定义 Bean 及其依赖关系,实现依赖注入等功能。
假设我们有一个简单的业务场景,有一个 UserService 类,它依赖于 UserDao 类来进行数据库操作。首先定义 UserDao 接口及其实现类 UserDaoImpl:
public interface UserDao {void save();
}
public class UserDaoImpl implements UserDao {@Overridepublic void save() {System.out.println("保存用户数据到数据库");}
}
然后定义 UserService 类:
public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void saveUser() {userDao.save();}
}
接下来在 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 定义UserDao的Bean --><bean id="userDao" class="com.example.dao.UserDaoImpl"/><!-- 定义UserService的Bean,并注入UserDao依赖 --><bean id="userService" class="com.example.service.UserService"><property name="userDao" ref="userDao"/></bean>
</beans>
在上述配置中,<bean> 标签用于定义 Bean,id 属性为 Bean 指定唯一标识,class 属性指定 Bean 的实现类。<property> 标签用于注入依赖,name 属性指定目标 Bean 的属性名称,ref 属性引用另一个 Bean 作为依赖。通过这样的配置,Spring 容器在启动时会创建 userDao 和 userService 的实例,并将 userDao 注入到 userService 中。
3.2 注解配置
注解配置是 Spring 提供的一种更加简洁、灵活的配置方式,它可以减少 XML 配置文件的编写,提高开发效率。以下介绍几个常用注解的使用方法:
- @Component:这是一个通用的组件注解,用于将一个普通的 Java 类标记为 Spring 容器管理的组件,即 Bean。它可以作用于任何需要被 Spring 容器管理的类上,例如:
@Component
public class UserService {// 业务逻辑
}
如果需要为组件指定一个自定义的名称,可以通过 @Component(“customName”) 的方式来指定。
- @Autowired:用于自动装配 Bean,它可以应用在字段、方法和构造函数上。默认情况下,@Autowired 是按照类型进行装配的。例如:
@Component
public class UserService {@Autowiredprivate UserDao userDao;// 业务逻辑
}
在上述代码中,Spring 容器会在容器中查找类型为 UserDao 的 Bean,并将其注入到 userService 的 userDao 字段中。如果容器中存在多个相同类型的 Bean,可以结合 @Qualifier 注解来指定具体要注入的 Bean 的名称,例如:
@Component
public class UserService {@Autowired@Qualifier("userDaoImpl")private UserDao userDao;// 业务逻辑
}
- @Resource:也是用于依赖注入的注解,它由 JDK 提供,Spring 也对其提供了支持。@Resource 默认是按照名称进行注入的,如果没有指定名称,则按照类型进行注入。例如:
@Component
public class UserService {@Resource(name = "userDao")private UserDao userDao;// 业务逻辑
}
在这个例子中,@Resource 注解通过 name 属性指定要注入的 Bean 的名称为 userDao。
- @Value:用于注入简单类型的属性值,例如字符串、数字等。它可以应用在字段或方法上。例如:
@Component
public class DatabaseConfig {@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;// 其他配置方法
}
在上述代码中,@Value 注解从配置文件(如 application.properties 或 application.yml)中读取对应属性的值,并注入到相应的字段中。
3.3 Java 配置类
Java 配置类是 Spring 3.0 引入的一种基于 Java 代码的配置方式,它使用 @Configuration 和 @Bean 等注解来替代 XML 配置文件。这种方式更加类型安全,并且可以利用 Java 代码的灵活性和强大功能。
- @Configuration:用于标记一个类为配置类,它表示该类是一个 Spring 配置的入口点,相当于一个 XML 配置文件。例如:
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {// 配置方法
}
- @Bean:用于在配置类中定义一个 Bean,它可以应用在方法上,方法的返回值就是要创建的 Bean 实例。方法名默认作为 Bean 的名称,也可以通过 @Bean(“customName”) 的方式指定自定义名称。例如:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {@Beanpublic UserDao userDao() {return new UserDaoImpl();}@Beanpublic UserService userService() {UserService userService = new UserService();userService.setUserDao(userDao());return userService;}
}
在上述代码中,AppConfig 是一个配置类,userDao 方法定义了一个 UserDao 类型的 Bean,userService 方法定义了一个 UserService 类型的 Bean,并通过调用 userDao() 方法将 UserDao 注入到 UserService 中。
3.4 IOC 容器实战案例
下面通过一个完整的 Service 与 DAO 层依赖注入案例,展示 IOC 容器在实际项目中的应用。
假设我们正在开发一个简单的用户管理系统,有以下几个主要的类:
- User 类:表示用户实体
public class User {private Long id;private String name;private int age;// 省略getter和setter方法
}
- UserDao 接口及其实现类 UserDaoImpl:负责用户数据的持久化操作
public interface UserDao {void save(User user);
}
public class UserDaoImpl implements UserDao {@Overridepublic void save(User user) {System.out.println("保存用户:" + user.getName() + " 到数据库");}
}
- UserService 类:负责用户业务逻辑的处理,依赖于 UserDao
public class UserService {private UserDao userDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void registerUser(User user) {System.out.println("开始注册用户");userDao.save(user);System.out.println("用户注册成功");}
}
- 配置类 AppConfig:使用 Java 配置类的方式进行配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {@Beanpublic UserDao userDao() {return new UserDaoImpl();}@Beanpublic UserService userService() {UserService userService = new UserService();userService.setUserDao(userDao());return userService;}
}
- 测试类 TestUserService:用于测试 UserService 的功能
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestUserService {public static void main(String[] args) {// 创建Spring应用上下文AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 获取UserService实例UserService userService = context.getBean(UserService.class);// 创建用户对象User user = new User();user.setName("张三");user.setAge(20);// 调用UserService的方法进行用户注册userService.registerUser(user);// 关闭应用上下文context.close();}
}
运行 TestUserService 的 main 方法,输出结果如下:
开始注册用户
保存用户:张三 到数据库
用户注册成功
通过这个案例,可以清晰地看到 IOC 容器如何通过配置类创建 Bean,并实现 Service 与 DAO 层之间的依赖注入,从而完成业务逻辑的处理。这种方式使得代码的结构更加清晰,耦合度降低,便于维护和扩展。