环境
- JDK 1.8
- Maven 3.6.3
- IntelliJ IDEA 2020
知识
- Java基础
- Maven
- JUnit
1.1、什么是Spring
Spring框架是由于软件开发的复杂性而创建的。
- 使用基本的JavaBean来代替复杂的 EJB (Enterprise Java Beans);
- 不仅可用于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益;
- 理念:使现有的技术更容易使用,整合了很多现有的技术框架。
有关起源:
- 2002年,首次推出了Spring框架的雏形:interface 21框架
- 2004年3月24日,Spring框架以interface 21框架为基础重新设计,发布了1.0正式版;
- Rod Johnson:Spring Framework创始人,在悉尼大学获得了计算机学位和音乐学位,更是拿到了音乐学的博士学位。
1.2、为什么要使用Spring
Spring是一个轻量级的控制反转、面向切面编程的容器。
- 优点:开源、免费、轻量级、非入侵式;
- 特性:控制反转,面向切面编程
- 支持事务处理、整合框架
1.3、组成
1.4、搭建工程
- 创建一个不带模板的Maven空项目
- 删除src目录,作为项目的父工程;
- 导入Maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency>
- 创建模块,完成开发准备工作。
2.1、问题引入
在传统的web开发中,要实现一个业务,需要编写以下程序:
- XxxDao:接口
- XxxDaoImpl:接口实现类
- XxxService:业务接口
- XxxServiceImpl:业务实现类
2.1.1、需求:输出用户信息
- UserDao
public interface UserDao { /** * 获取用户信息 */ void getUserInfo(); }
- UserDaoImpl
public class UserDaoImpl implements UserDao{ @Override public void getUserInfo() { System.out.println("这里是用户信息"); } }
- UserService
public interface UserService { /** * 获取默认用户信息 */ void getUserInfo(); }
- UserServiceImpl
public class UserServiceImpl implements UserService { /** * Service层:调用Dao层 */ private UserDao userDao = new UserDaoImpl(); @Override public void getUserInfo() { userDao.getUserInfo(); } }
- Test
@Test public void test() { UserService userService = new UserServiceImpl(); userService.getUserInfo(); }
业务流程:Test相当于测试用户,用户访问Service层
,Service层
再去调用DAO层
的方法。即用户不会直接访问到DAO层
。
2.1.2、新需求:增加新用户
- 新的接口实现类
public class UserDaoMySqlImpl implements UserDao{ @Override public void getUserInfo() { System.out.println("这里是MySQL用户信息"); } }
- 修改原本的代码
private UserDao userDao = new UserDaoImpl(); // 需要改为 private UserDao userDao = new UserDaoMySqlImpl();
分析:
-
UserServiceImpl
中的userDao
是固定的,相当于“锁定”了具体一类用户的使用; - 若增加新用户,即新建一个
接口实现类
。
- 需要修改
UserServiceImpl
中userDao
的值,才能供新用户的使用; - 代码强耦合,复用性低,维护成本高。
- 在当前情况下,用户的需求可能会影响原来的代码:客户提出需求,程序员需要根据不同需求来修改原先的代码;
- 需要找到一种方案,让程序能够自行应对客户的不同需求。
原因:究其根本,是因为UserServiceImpl
中的userDao
是固定值,“锁定”了具体一类用户的使用。如果能够使userDao
可以根据不同用户来动态实现值的注入,就能解决这个难题。
2.2、解决问题
2.2.1、方案
将userServiceImpl
做如下修改:
- 去掉声明
userDao
时的赋值操作; - 通过
setter()
实现动态赋值。/** * Service层:调用Dao层 */ private UserDao userDao; /** * 根据用户,为userDao动态赋值 * * @param userDao 具体用户 */ public void setUserDao(UserDao userDao) { this.userDao = userDao; }
测试
public void test() {
// 多态:父类引用指向子类对象
UserService userService = new UserServiceImpl();
// 由于父类不能调子类方法,所以需要先强转成子类才可以调用方法
((UserServiceImpl) userService).setUserDao(new UserDaoImpl());
userService.getUserInfo();
}
public void test() {
// 多态:父类引用指向子类对象
UserService userService = new UserServiceImpl();
// 由于父类不能调子类方法,所以需要先强转成子类才可以调用方法
((UserServiceImpl) userService).setUserDao(new UserDaoMySqlImpl());
userService.getUserInfo();
}
分析:
-
UserServiceImpl
中的userDao
是动态注入的,由具体用户选择使用; - 若增加新用户,即新建一个
接口实现类
。
- 不再需要修改
UserServiceImpl
中userDao
的值; - 解耦,提高复用性,降低维护成本。
- 在这种情况下,用户的需求不影响原来的代码,客户提出需求,程序员不再需要修改原先的代码,而是由程序自行应对不同需求;
2.2.2、对比
- 原来的业务:主动权在程序上。
- 程序主动创建对象;
- 需求有变动的话,程序员需要修改对象的创建;
- 现在的业务:主动权在用户。
- 使用setter注入对象,程序不再具有主动性,变成被动的接收对象;
- 接收对象后,由第三方动态地为对象注入值;
- 需求有变动的话,由用户自行选择设置。
举一个生活中的例子:你请客人吃饭。
- 原来的方式:你决定菜品,亲自下厨。
- 你主动决定要请客人吃的菜品,去市场先买好菜;
- 来了新客人,要吃不同的菜,你就需要重新去市场买菜。
- 现在的方式:客人决定菜品,叫饭店做。
- 你不事先备菜,而是听客人要吃的菜品;
- 知道客人要吃的菜品,你打电话给饭店点菜;
- 来了新客人,要吃不同的菜,你就再打给饭店加菜。
- 分析
- 你:程序员、程序;
- 客人:用户;
- 饭店:第三方;
- 菜品:业务需求;
- 原来的方式:主动权在你手上,但是客人想吃不同菜品,你就要麻烦自己再去备菜;
- 现在的方式:主动权在客人手上,客人想吃不同菜品,你就打个电话给饭店点菜就好了。
这种思想,从本质上解决了问题,程序员不再需要管理对象的创建,而是由用户自行根据需求选择。
这就是IOC思想的原型!
2.3、IOC的本质
控制反转(Inversion of Control)是一种设计思想,通过描述(XML或注解)并通过第三方去获取或生产特定对象。
- 没有IOC的程序中 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序本身控制;
- 控制反转后,将对象的创建转移给第三方,即获得依赖对象的方式反转了;
IOC是Spring框架的核心内容,IOC有多种实现方式:XML配置、注解,甚至零配置实现。
- 采用XML配置Bean:Bean的定义和实现分离;
- 采用注解配置Bean:把二者合为一体,Bean的定义信息以注解的形式定义在实现类中,达到了零配置的目的。
容器工作流程:
- Spring容器在初始化时先读取配置文件;
- Spring容器根据元数据创建并组装Bean对象;
- 配置好的系统程序,使用时从容器中获取需要的Bean对象;
在Spring中事项控制反转的是IOC容器,依赖注入(Dependency Injection)是IOC的一种实现方法。