Spring 框架作为 Java 后端开发的基石,核心围绕控制反转(IOC) 和面向切面编程(AOP) 两大思想,旨在简化开发、降低模块耦合。本文基于 Spring 5.0.2 版本,从框架介绍、IOC/DI 核心用法、注解开发到 JUnit 整合,全方位拆解 Spring 上半部分核心知识点,附带完整实战代码,适合入门者系统学习。
一、Spring 框架核心认知
1. 什么是 Spring?
Spring 是 2003 年兴起的轻量级 Java EE 一站式框架,由 Rod Johnson 创建。它以 IOC 和 AOP 为核心,解决了业务逻辑层与其他层的耦合问题,支持分层开发(表现层、业务层、持久层),且能无缝集成 MyBatis、Struts2 等主流框架。
2. Spring 的核心优势
- 解耦简化开发:IOC 容器统一管理对象创建和依赖关系,无需手动 new 对象。
- AOP 编程支持:轻松实现权限拦截、日志记录等横切逻辑,不侵入业务代码。
- 声明式事务:通过配置而非硬编码管理事务,降低开发复杂度。
- 便捷测试:与 JUnit 深度整合,简化测试代码编写。
- 兼容优秀框架:内置对 MyBatis、Hibernate、Quartz 等框架的支持。
- 封装复杂 API:简化 JDBC、JavaMail 等难用的 Java EE API。
二、核心技术:IOC 控制反转
1. IOC 核心概念
IOC(Inverse of Control)即控制反转 ,指将对象的创建权从开发者手中 "反转" 给 Spring 框架。开发者无需手动通过new关键字创建对象,而是由 Spring 容器根据配置文件或注解自动实例化对象,彻底解决代码耦合问题。
2. IOC 入门实战(XML 配置方式)
(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
// 接口
public interface UserService {
void hello();
}
// 实现类
public class UserServiceImpl implements UserService {
@Override
public void hello() {
System.out.println("Hello IOC!!");
}
}
(3)编写 Spring 配置文件(applicationContext.xml)
在resources目录下创建配置文件,声明 Bean 交给 Spring 管理:
<?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">
<!-- 配置Bean:id为Bean名称,class为全类名 -->
<bean id="userService" class="com.qcbyjy.service.UserServiceImpl" />
</beans>
(4)编写测试类
通过 Spring 容器获取 Bean 并调用方法:
java
public class Demo1 {
@Test
public void run1() {
// 加载Spring配置文件,初始化容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取Bean(按id查找)
UserService userService = (UserService) ac.getBean("userService");
// 调用方法
userService.hello(); // 输出:Hello IOC!!
}
}
3. Bean 的核心配置详解
(1)Bean 的作用范围(scope 属性)
singleton:单例(默认值),容器中只创建一个 Bean 实例,全局共享。prototype:多例,每次调用getBean()都会创建新实例。request:Web 环境专用,每次 HTTP 请求创建新 Bean。session:Web 环境专用,同一个 Session 共享一个 Bean。
配置示例:
<bean id="userService" class="com.qcbyjy.service.UserServiceImpl" scope="prototype" />
(2)Bean 的生命周期配置
init-method:Bean 初始化后执行的方法。destroy-method:Bean 销毁前执行的方法(单例 Bean 随容器关闭销毁,多例 Bean 由垃圾回收销毁)。
配置示例:
java
// 实现类中添加初始化和销毁方法
public class UserServiceImpl implements UserService {
public void init() {
System.out.println("Bean初始化...");
}
public void destroy() {
System.out.println("Bean销毁...");
}
// 业务方法...
}
<bean id="userService" class="com.qcbyjy.service.UserServiceImpl"
init-method="init" destroy-method="destroy" />
(3)Bean 的三种实例化方式
-
默认无参构造(推荐):最常用,要求类必须有默认无参构造方法。
<bean id="userService" class="com.qcbyjy.service.UserServiceImpl" /> -
静态工厂实例化:通过静态方法创建 Bean。
java// 静态工厂类 public class StaticFactory { public static UserService createUserService() { return new UserServiceImpl(); } }<bean id="userService" class="com.qcbyjy.demo1.StaticFactory" factory-method="createUserService" /> -
实例工厂实例化:通过工厂实例的非静态方法创建 Bean。
java// 实例工厂类 public class InstanceFactory { public UserService createUserService() { return new UserServiceImpl(); } }<!-- 先配置工厂Bean --> <bean id="factory" class="com.qcbyjy.demo1.InstanceFactory" /> <!-- 通过工厂Bean创建目标Bean --> <bean id="userService" factory-bean="factory" factory-method="createUserService" />
三、核心技术:DI 依赖注入
1. DI 核心概念
DI(Dependency Injection)即依赖注入,是 IOC 的具体实现。Spring 容器在创建 Bean 时,会自动将依赖的其他 Bean 或属性值注入到当前 Bean 中,无需开发者手动设置。
2. 依赖注入的三种常用方式
(1)Set 方法注入(最常用)
通过 Bean 的setXxx()方法注入依赖,适用于大多数场景。
示例:注入引用类型(Service 依赖 Dao)+ 基本类型
java
// Service实现类
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();
}
}
// Dao实现类
public class OrderDaoImpl implements OrderDao {
@Override
public void saveOrder() {
System.out.println("持久层:保存订单...");
}
}
配置文件:
<!-- 配置Dao Bean -->
<bean id="orderDao" class="com.qcbyjy.dao.OrderDaoImpl" />
<!-- 配置Service Bean,注入依赖 -->
<bean id="orderService" class="com.qcbyjy.service.OrderServiceImpl">
<!-- 注入引用类型:ref指向目标Bean的id -->
<property name="orderDao" ref="orderDao" />
<!-- 注入基本类型:value直接赋值 -->
<property name="msg" value="Spring DI" />
<property name="age" value="20" />
</bean>
(2)构造方法注入
通过 Bean 的带参构造方法注入依赖,适用于依赖必须存在的场景。
示例:
java
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
<bean id="car" class="com.qcbyjy.demo2.Car">
<constructor-arg name="cname" value="大奔" />
<constructor-arg name="money" value="400000" />
</bean>
(3)集合类型注入
支持数组、List、Set、Map、Properties 等集合类型的注入。
示例:
java
public class CollectionBean {
private String[] strs; // 数组
private List<String> list; // List
private Map<String, String> map; // Map
private Properties properties; // Properties
// 提供set方法
public void setStrs(String[] strs) { this.strs = strs; }
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; }
// toString方法...
}
配置文件:
XML
<bean id="collectionBean" class="com.qcbyjy.demo3.CollectionBean">
<!-- 数组注入 -->
<property name="strs">
<array>
<value>张三</value>
<value>李四</value>
</array>
</property>
<!-- List注入 -->
<property name="list">
<list>
<value>王五</value>
<value>赵六</value>
</list>
</property>
<!-- Map注入 -->
<property name="map">
<map>
<entry key="user1" value="小明" />
<entry key="user2" value="小红" />
</map>
</property>
<!-- Properties注入 -->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
四、IOC 注解开发(简化配置)
XML 配置繁琐,Spring 提供注解方式替代 XML,更适合现代开发。
1. 注解开发入门
(1)核心依赖
与 XML 方式一致,无需额外导入依赖。
(2)开启注解扫描
在 XML 配置文件中添加注解扫描,Spring 会自动扫描指定包下的注解:
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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启注解扫描:扫描com.qcbyjy包下所有类 -->
<context:component-scan base-package="com.qcbyjy" />
</beans>
(3)Bean 注册注解
使用以下注解将类交给 Spring 管理(作用相同,分层标识不同):
@Component:通用注解,适用于任意类。@Controller:表现层(Web 层)专用。@Service:业务层专用。@Repository:持久层专用。
示例:
java
// 业务层类,指定Bean名称为"userService"
@Service(value = "userService")
public class UserServiceImpl implements UserService {
@Override
public void hello() {
System.out.println("Hello IOC注解...");
}
}
(4)依赖注入注解
@Value:注入基本类型(String、int 等)。@Autowired:默认按类型注入引用类型,需容器中存在唯一对应类型的 Bean。@Qualifier:与@Autowired搭配,按名称注入(解决同类型多个 Bean 的冲突)。@Resource:Java 原生注解,按名称 注入(name属性指定 Bean 名称)。
示例:
java
@Component(value = "car")
public class Car {
// 注入基本类型(无需set方法)
@Value("大奔")
private String cname;
@Value("400000")
private Double money;
// 注入引用类型(按类型+名称)
@Autowired
@Qualifier(value = "person")
private Person person;
// 初始化方法(替代init-method)
@PostConstruct
public void init() {
System.out.println("Car初始化...");
}
// toString方法...
}
@Component(value = "person")
public class Person {
@Value("张三")
private String pname;
// toString方法...
}
(5)Bean 作用范围注解
@Scope:指定 Bean 作用范围,取值singleton(默认)或prototype。
示例:
@Service
@Scope(value = "prototype") // 多例
public class UserServiceImpl implements UserService {
// 业务方法...
}
2. 纯注解开发(无 XML)
纯注解开发是 Spring Boot 的基础,通过配置类替代 XML 文件。
(1)核心注解
@Configuration:声明当前类为 Spring 配置类。@ComponentScan:指定注解扫描包(替代<context:component-scan>)。@Import:导入其他配置类(拆分配置时使用)。@Bean:在方法上使用,手动创建 Bean 并交给 Spring 管理(适用于第三方类,如连接池)。
(2)纯注解开发实战
① 编写配置类
java
@Configuration // 声明为配置类
@ComponentScan(basePackages = "com.qcbyjy.demo4") // 扫描包
@Import(SpringConfig2.class) // 导入其他配置类
public class SpringConfig {
// 手动创建第三方Bean(如Druid连接池)
@Bean(name = "dataSource") // Bean名称为dataSource
public DataSource createDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
// 其他配置类
@Configuration
public class SpringConfig2 {
// 可配置其他Bean...
}
② 编写业务类
java
运行
@Component
public class Order {
@Value("北京")
private String address;
@Override
public String toString() {
return "Order{" + "address='" + address + '\'' + '}';
}
}
③ 编写测试类
java
public class Demo4 {
@Test
public void run1() {
// 加载配置类,初始化容器
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
// 获取Bean
Order order = (Order) ac.getBean("order");
DataSource dataSource = (DataSource) ac.getBean("dataSource");
System.out.println(order); // 输出:Order{address='北京'}
System.out.println(dataSource); // 输出Druid连接池对象
}
}
五、Spring 整合 JUnit 单元测试
传统测试需要手动加载 Spring 容器,整合 JUnit 后可自动注入 Bean,简化测试代码。
1. 导入依
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
<scope>test</scope>
</dependency>
2. 整合方式一:XML 配置
java
// 指定JUnit运行器为Spring提供的运行器
@RunWith(SpringJUnit4ClassRunner.class)
// 加载XML配置文件
@ContextConfiguration(locations = "classpath:applicationContext_test.xml")
public class Demo5 {
// 自动注入Bean
@Autowired
private User user;
@Test
public void run1() {
user.sayHello(); // 直接调用方法,无需手动创建容器
}
}
3. 整合方式二:纯注解配置
java
@RunWith(SpringJUnit4ClassRunner.class)
// 加载配置类
@ContextConfiguration(classes = SpringConfig6.class)
public class Demo6 {
@Autowired
private Customer customer;
@Test
public void run1() {
customer.save(); // 输出:保存客户...
}
}
// 配置类
@Configuration
@ComponentScan("com.qcbyjy.demo6")
public class SpringConfig6 {
}
六、总结
Spring 上半部分核心围绕 IOC/DI 展开,关键在于理解 "对象由容器管理,依赖由容器注入" 的思想。开发中可根据场景选择 XML 或注解方式:
- 小型项目:XML 配置简单直观。
- 中大型项目 / 微服务:纯注解开发更高效,是 Spring Boot 的基础。
后续将深入学习 Spring AOP、声明式事务等高级特性,掌握这些技术后,能大幅提升开发效率和代码质量。