Spring框架(一)
Spring框架是Java开发中最为流行的框架之一,它以其强大的功能和灵活的设计,极大地简化了企业级应用的开发。本文将详细介绍Spring框架的核心概念、核心技术、依赖注入、多配置文件方式、开发程序的方式、IOC注解方式以及Spring与JUnit的整合。
目录
一、Spring框架概述
1.1 Spring框架的概述
1.2 Spring框架的优点
二、Spring的IOC核心技术
2.1 什么是IOC
2.2 IOC的程序入门
2.3 IOC技术总结
2.4 实例化Bean对象的三种方式
三、DI依赖注入
3.1 依赖注入的概述
3.2 DI的两种方式
3.3 复杂类型注入(数组,集合,Properties)
四、多配置文件管理
五、Spring框架开发程序的方式
六、IOC注解开发
6.1 半注解方式(XML + 注解)
6.2 纯注解方式(零XML)
6.3 对比分析
七、Spring 框架整合 JUnit 单元测试
7.1 基于XML配置的Spring整合JUnit测试
7.2 纯注解方式的Spring整合JUnit测试
7.3 对比分析
总结
一、Spring框架概述
1.1 Spring框架的概述
Spring是一个开源的Java平台,提供了一个轻量级的框架来解决企业应用开发的复杂性。Spring框架的核心优势在于其分层架构,允许开发者选择使用特定的组件,同时为J2EE应用程序开发提供集成的解决方案。
Spring框架的主要特点包括:
-
控制反转(IoC):将对象的创建和依赖关系的管理交给Spring容器。
-
面向切面编程(AOP):通过AOP实现权限拦截、运行监控等功能。
-
声明式事务管理:通过配置文件实现事务管理,无需手动编写事务代码。
-
方便程序测试:支持JUnit4,便于对Spring程序进行单元测试。
-
集成优秀框架:如Struts2、Hibernate、MyBatis、Quartz等。
-
降低JavaEE API的使用难度:对JDBC、JavaMail等复杂API进行了封装。
1.2 Spring框架的优点
-
方便解耦,简化开发:Spring通过IoC容器管理对象的创建和依赖关系,开发者无需手动管理对象的生命周期和依赖关系。
-
AOP编程的支持:Spring提供了AOP编程的支持,可以方便地实现权限拦截、运行监控等功能。
-
声明式事务的支持:通过配置文件即可完成事务管理,无需手动编写事务管理代码。
-
方便程序的测试:Spring对JUnit4提供了良好的支持,可以通过注解方便地测试Spring程序。
-
方便集成各种优秀框架:Spring内部提供了对Struts2、Hibernate、MyBatis、Quartz等框架的直接支持。
-
降低JavaEE API的使用难度:Spring对JDBC、JavaMail、远程调用等复杂API进行了封装,降低了使用难度。
二、Spring的IOC核心技术
2.1 什么是IOC
IOC(Inverse of Control,控制反转)是一种设计原则,通过将对象的创建权反转给Spring框架,降低了代码之间的耦合度。Spring的工厂通过读取配置文件来管理对象的创建和依赖关系。
2.2 IOC的程序入门
以下是一个简单的Spring IOC入门示例:
1. 创建mavenJava项目,导入坐标依赖(pom.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>
2. 编写接口和实现类
package com.qcby.service;public interface UserService {public void hello();
}
package com.qcby.service;public class UserServiceImpl implements UserService{public void hello() {System.out.println("Hello IOC!");}
}
3. 编写 Spring 核心的配置文件
在src/main/resources文件夹下创建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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--IOC 管理 bean--><bean id="userService" class="com.qcby.service.UserServiceImpl"/></beans>
4. 编写测试方法
import com.qcby.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Demo1 {/*** 入门程序*/@Testpublic void run1(){// 使用 Spring 的工厂ApplicationContext applicationContext = newClassPathXmlApplicationContext("spring.xml");// 通过工厂获得类:UserService userService = (UserService)applicationContext.getBean("userService");userService.hello();}}
2.3 IOC技术总结
-
ApplicationContext接口:工厂的接口,用于获取具体的Bean对象。常用的实现类有
ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
。 -
Bean管理:通过
id
、class
、scope
等属性配置Bean对象。 -
Bean对象的创建和销毁:通过
init-method
和destroy-method
属性配置Bean的生命周期方法。
2.4 实例化Bean对象的三种方式
1. 默认无参数构造方法
<bean id="us" class="com.qcby.service.UserServiceImpl"/>
2. 静态工厂实例化方式
public class StaticFactory {public static UserService createUs() {System.out.println("通过静态工厂的方式创建 UserServiceImpl 对
象...");return new UserServiceImpl();}
}
<bean id="us" class="com.qcby.demo1.StaticFactory" factory-method="createUs"/>
3. 实例工厂实例化方式
public class Dfactory {public UserService createUs() {System.out.println("实例化工厂的方式...");return new UserServiceImpl();}
}
<bean id="dfactory" class="com.qcby.demo1.Dfactory" />
<bean id="us" factory-bean="dfactory" factory-method="createUs" />
三、DI依赖注入
3.1 依赖注入的概述
DI(Dependency Injection,依赖注入)是Spring框架负责创建Bean对象时,动态地将依赖对象注入到Bean组件中的过程。
3.2 DI的两种方式
1. Setter方法注入
//OrderDao
package com.qcby.dao;public interface OrderDao {public void saveOrder();
}//OrderDaoImpl
package com.qcby.dao;public class OrderDaoImpl implements OrderDao{public void saveOrder() {System.out.println("持久层:保存订单...");}
}//OrderService
package com.qcby.service;public interface OrderService {public void saveOrder();
}//OrderServiceImpl
package com.qcby.service;import com.qcby.dao.OrderDao;public class OrderServiceImpl implements OrderService{//编写成员属性,一定需要提供该属性的 set 方法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:依赖注入--><bean id="os" class="com.qcby.service.OrderServiceImpl"><property name="orderDao" ref="od" /><property name="msg" value="你好" /><property name="age" value="30" /></bean><bean id="od" class="com.qcby.dao.OrderDaoImpl"></bean>
import com.qcby.service.OrderService;
import com.qcby.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Demo1 {@Testpublic void run3(){ApplicationContext applicationContext = newClassPathXmlApplicationContext("spring.xml");OrderService orderService = (OrderService)applicationContext.getBean("os");orderService.saveOrder(); //业务层:保存订单...你好 - 30 持久层:保存订单...}}
2. 构造器注入
package com.qcby.demo2;public class Car {// 名称private String cname;// 金额private Double money;public Car(String cname, Double money) {this.cname = cname;this.money = money;}@Overridepublic String toString() {return "Car{" +"cname='" + cname + '\'' +", money=" + money +'}';}
}
<!-- 属性构造方法方式注入值 --><bean id="car" class="com.qcby.demo2.Car"><constructor-arg name="cname" value="大奔" /><constructor-arg name="money" value="400000" /></bean>
@Testpublic void run4(){ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");Car car = (Car) context.getBean("car");System.out.println(car); //Car{cname='大奔', money=400000.0}}
3.3 复杂类型注入(数组,集合,Properties)
package com.qcby.demo3;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;}@Overridepublic String toString() {return "CollectionBean{" +"strs=" + Arrays.toString(strs) +", list=" + list +", map=" + map +", properties=" + properties +'}';}
}
<!--给集合属性注入值--><bean id="collectionBean" class="com.qcby.demo3.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>
@Testpublic void run5(){ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");CollectionBean collectionBean = (CollectionBean) context.getBean("collectionBean");System.out.println(collectionBean);//CollectionBean{strs=[美美, 小凤], list=[熊大, 熊二], map={aaa=老王, bbb=小王}, properties={password=123456, username=root}}}
四、多配置文件管理
通过<import>
标签或加载多个配置文件实现模块化配置:
<!-- 在主配置文件中配置 -->
<import resource="applicationContext2.xml" />
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml", "applicationContext2.xml"
);
五、Spring框架开发程序的方式
Spring框架开发方式:
以下是一个使用Spring框架开发程序的完整示例:
1. 创建Maven工程,导入开发的jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qcby</groupId><artifactId>spring02</artifactId><version>1.0-SNAPSHOT</version><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><!--连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!--mysql 驱动包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency></dependencies></project>
2. 创建数据库,创建表结构
create database spring_db;
use spring_db;
create table account(
id int primary key auto_increment,
name varchar(40),
money double
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
3. 编写JavaBean的类
package com.qcby.domain;import java.io.Serializable;public class Account implements Serializable{private static final long serialVersionUID = 7355810572012650248L;private Integer id;private String name;private Double money;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}@Overridepublic String toString() {return "Account{" +"id=" + id +", name='" + name + '\'' +", money=" + money +'}';}
}
4. 编写AccountDao的接口和实现类
package com.qcby.dao;import com.qcby.domain.Account;import java.util.List;public interface AccountDao {public List<Account> findAll();
}
package com.qcby.dao;import com.alibaba.druid.pool.DruidDataSource;
import com.qcby.domain.Account;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;public class AccountDaoImpl implements AccountDao{// 注入连接池对象private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}/*** 查询所有的数据* @return*/public List<Account> findAll() {/*DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring_db");dataSource.setUsername("root");dataSource.setPassword("12345");*/List<Account> list=new ArrayList<Account>();Connection connection = null;PreparedStatement stmt = null;ResultSet rs = null;try {// 获取连接connection = dataSource.getConnection();// 编写 sql 语句String sql = "select * from account";// 预编译stmt = connection.prepareStatement(sql);// 查询rs = stmt.executeQuery();// 遍历,封装数据while (rs.next()){Account account = new Account();account.setId(rs.getInt("id"));account.setName(rs.getString("name"));account.setMoney(rs.getDouble("money"));list.add(account);}} catch (SQLException e) {e.printStackTrace();}finally {try {connection.close();} catch (SQLException e) {e.printStackTrace();}try {stmt.close();} catch (SQLException e) {e.printStackTrace();}try {rs.close();} catch (SQLException e) {e.printStackTrace();}}return list;}
}
5. 编写AccountService的接口和实现类
package com.qcby.service;import com.qcby.domain.Account;import java.util.List;public interface AccountService {public List<Account> findAll();
}
package com.qcby.service;import com.qcby.dao.AccountDao;
import com.qcby.domain.Account;import java.util.List;public class AccountServiceImpl implements AccountService{// 依赖注入private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** 查询所有的数据* @return*/public List<Account> findAll() {return accountDao.findAll();}
}
6. 编写配置文件
<?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"><!--配置连接池--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql:///spring_db" /><property name="username" value="root" /><property name="password" value="12345" /></bean><!--管理 bean--><bean id="accountService" class="com.qcby.service.AccountServiceImpl"><property name="accountDao" ref="accountDao" /></bean><bean id="accountDao" class="com.qcby.dao.AccountDaoImpl"><property name="dataSource" ref="dataSource" /></bean></beans>
7. 编程测试程序
import com.qcby.domain.Account;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.List;public class Demo1 {@Testpublic void run1(){ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");AccountService accountService = (AccountService) ac.getBean("accountService");// 调用方法List<Account> list = accountService.findAll();for (Account account : list) {System.out.println(account);}}
}
六、IOC注解开发
6.1 半注解方式(XML + 注解)
1. 核心思想
在XML配置文件中开启注解扫描,结合代码中的注解(如@Component
、@Autowired
)管理Bean,部分配置仍依赖XML文件。
2. 实现步骤
-
开启注解扫描:在XML中配置
<context:component-scan>
,指定扫描的包路径。<beans xmlns:context="http://www.springframework.org/schema/context"><context:component-scan base-package="com.qcby"/> </beans>
-
定义Bean:在类上使用
@Component
及其衍生注解(@Service
,@Repository
,@Controller
)。@Service("userService") public class UserServiceImpl implements UserService {// ... }
-
依赖注入:
-
普通类型:使用
@Value
注入。@Value("Hello") private String message;
-
引用类型:使用
@Autowired
按类型注入,或@Qualifier
按名称注入。@Autowired @Qualifier("userDao") private UserDao userDao;
-
3. 常用注解
注解 | 作用 |
---|---|
@Component | 通用Bean注解,标识类为Spring管理的组件。 |
@Service | 标识业务层组件。 |
@Repository | 标识持久层组件(自动处理DAO层异常)。 |
@Autowired | 按类型自动注入依赖(默认按类型,可配合@Qualifier 按名称)。 |
@Scope | 定义Bean作用域(如singleton 、prototype )。 |
@PostConstruct | 初始化方法注解,等同于init-method 。 |
@PreDestroy | 销毁方法注解,等同于destroy-method 。 |
4. 示例
@Service("accountService")
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;@Value("1000")private int initialBalance;
}
6.2 纯注解方式(零XML)
1. 核心思想
完全通过Java配置类(@Configuration
)和注解替代XML文件,实现全注解驱动的开发。
2. 实现步骤
-
定义配置类:使用
@Configuration
标识配置类,@ComponentScan
指定扫描包路径。@Configuration @ComponentScan("com.qcby") public class SpringConfig {}
-
注册Bean:
-
自动扫描:通过
@ComponentScan
自动注册标记了@Component
的类。 -
手动注册:在配置类中使用
@Bean
方法显式定义Bean。@Bean public DataSource dataSource() {DruidDataSource ds = new DruidDataSource();ds.setUrl("jdbc:mysql:///spring_db");return ds; }
-
-
加载配置类:通过
AnnotationConfigApplicationContext
加载配置类。ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
3. 常用注解
注解 | 作用 |
---|---|
| 标识当前类为配置类,替代XML文件。 |
| 指定扫描包路径,自动注册Bean。 |
| 在方法上定义Bean,方法返回值作为Bean实例(常用于第三方库的集成)。 |
| 引入其他配置类,实现模块化配置。 |
| 加载外部配置文件(如 |
4. 示例
@Configuration
@ComponentScan("com.qcby.demo4")
@Import(DataSourceConfig.class)
public class AppConfig {@Beanpublic DataSource dataSource() {DruidDataSource ds = new DruidDataSource();ds.setDriverClassName("com.mysql.jdbc.Driver");ds.setUrl("jdbc:mysql:///spring_db");return ds;}
}
6.3 对比分析
特性 | 半注解方式 | 纯注解方式 |
---|---|---|
配置文件 | 需要XML文件(开启扫描) | 完全无需XML,使用Java配置类 |
灵活性 | 适合遗留项目迁移或部分注解改造 | 适合新项目,完全面向注解开发 |
代码侵入性 | 较低(仅需少量注解) | 较高(需编写配置类) |
典型场景 | 逐步替换XML中的Bean定义 | 微服务、Spring Boot项目 |
依赖注入 | 支持 | 完全通过注解实现 |
七、Spring 框架整合 JUnit 单元测试
在传统的单元测试中,每次测试都需要手动创建Spring容器、加载配置文件,这不仅增加了代码量,还降低了测试效率。Spring框架针对这一问题提供了整合JUnit的解决方案,使得单元测试更加简洁高效。
7.1 基于XML配置的Spring整合JUnit测试
1. 环境准备
首先确保项目中已导入JUnit和Spring-test依赖:
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope>
</dependency>
<dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version><scope>test</scope>
</dependency>
2. 编写类和方法,把该类交给 IOC 容器进行管理
package com.qcby.demo5;public class User {public void sayHello() {System.out.println("Hello....");}
}
3. 编写配置文件
<?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"><!--整合单元测试--><bean id="user" class="com.qcby.demo5.User"/>
</beans>
4. 编写测试类
package com.qcby.test;import com.qcby.demo5.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** Spring整合JUnit单元测试(XML配置方式)*/
@RunWith(SpringJUnit4ClassRunner.class) // 使用Spring的测试运行器
@ContextConfiguration(value = "classpath:spring.xml") // 加载Spring配置文件
public class Demo5 {@Autowired // 自动注入User对象private User user;@Testpublic void run1() {user.sayHello(); // 调用被测试方法}
}
关键点说明:
-
@RunWith(SpringJUnit4ClassRunner.class)
:指定使用Spring的测试运行器 -
@ContextConfiguration
:指定Spring配置文件位置 -
@Autowired
:自动注入被测试对象
7.2 纯注解方式的Spring整合JUnit测试
1. 编写类和方法
package com.qcby.demo6;import org.springframework.stereotype.Component;@Component
public class Customer {public void save() {System.out.println("保存客户...");}
}
2. 编写配置类
package com.qcby.demo6;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;/*** Spring配置类(替代XML配置)*/
//声明
@Configuration
@ComponentScan(value = "com.qcby.demo6") // 扫描指定包下的组件
public class SpringConfig6 {
}
3. 编写测试类
package com.qcby.test;import com.qcby.demo6.Customer;
import com.qcby.demo6.SpringConfig6;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;/*** Spring整合JUnit单元测试(纯注解方式)*/
@RunWith(SpringJUnit4ClassRunner.class)
//加载配置类
@ContextConfiguration(classes = SpringConfig6.class) // 指定配置类
public class Demo6 {//按类型注入@Autowiredprivate Customer customer;@Testpublic void run1() {customer.save(); // 调用被测试方法}
}
关键点说明:
-
使用
@Configuration
和@ComponentScan
替代XML配置 -
@ContextConfiguration(classes = ...)
指定配置类 -
其余部分与XML方式类似
7.3 对比分析
特性 | XML配置方式 | 纯注解方式 |
---|---|---|
配置方式 | 使用XML文件 | 使用Java配置类 |
灵活性 | 较低,修改需修改XML文件 | 较高,可直接修改Java代码 |
可读性 | 结构清晰,适合复杂配置 | 简洁,适合简单配置 |
维护性 | 配置分散,维护成本较高 | 配置集中,维护成本较低 |
适用场景 | 大型项目,需要精细控制Bean创建 | 中小型项目,快速开发和迭代 |
总结
Spring框架通过IOC和AOP机制,彻底改变了Java企业级开发的方式。无论是基础的Bean管理,还是复杂的多数据源配置,Spring都提供了优雅的解决方案。掌握这些核心技术与高级特性,不仅能提升代码质量,还能大幅提高开发效率。在实际项目中,建议结合Spring Boot进一步简化配置,快速构建微服务应用。