Spring主要的内容是IoC(控制反转)和DI(依赖注入)。
1.为什么要IoC?
控制反转是一种设计原则,它将对象的创建、管理和依赖关系的处理从应用程序代码中转移到外部容器中。简单来说,它通过将对象的控制权从程序代码反转到外部框架或容器来实现。
传统的控制流
在传统的编程模型中,对象的创建和管理是由应用程序代码直接控制的。例如:
java
public class UserService {
private UserRepository userRepository;
public UserService() {
this.userRepository = new UserRepository();
}
public void saveUser(User user) {
userRepository.save(user);
}
}
在这个例子中,UserService
类直接创建了一个 UserRepository
对象(通过new)。这意味着 UserService
依赖于 UserRepository
,并且 UserService
控制了 UserRepository
的生命周期。
控制反转的实现
在控制反转的情况下,对象的创建和依赖关系的管理由一个框架或容器来处理。例如,在一个依赖注入(Dependency Injection, DI)框架中,通常会有一个配置文件或注解来描述对象之间的关系,框架会自动创建这些对象并注入它们所需的依赖。
java
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void saveUser(User user) {
userRepository.save(user);
}
}
在这个例子中,UserRepository
对象不再是 UserService
直接创建的,而是通过构造函数注入的。这意味着 UserService
不再控制 UserRepository
的创建和管理,而是由外部的 IoC 容器来负责。
为什么要控制反转?
-
解耦:通过将对象的创建和管理从应用程序代码中分离出来,可以减少代码之间的耦合度。这样,当你需要更改某个类的实现时,不必修改依赖它的所有类。
-
可测试性:控制反转使得单元测试更加容易。你可以轻松地使用模拟对象(Mock Object)来替换实际的依赖对象,从而隔离测试目标。
-
灵活性:通过配置方式来管理对象的依赖关系,可以更容易地进行系统配置和调整。例如,你可以在不改变代码的情况下,通过配置文件或注解来切换不同的实现类。
-
代码复用:控制反转通常伴随着面向接口编程,这使得代码更加通用和可复用。你可以编写更抽象的代码,依赖于接口而不是具体的实现。
-
可维护性:由于对象的创建和管理被集中处理,代码的可维护性得到了提高。开发者可以更专注于业务逻辑,而不必担心对象的生命周期和依赖管理
总结
用白话来说,就是我们在创建对象的时候,不希望自己手动new一个,这就需要将这个过程交给别人完成,而这个人,就是IoC容器。
如上图所示,我们把创建对象的工作交给IoC,这样,当我们需要一个对象来调用方法时,我们只需要"声明"这个对象即可。而且,当Service需要依赖于Dao时,也不需要我们自己手动设置关系,可以交由IoC完成。
2.创建Spring工程
2.1导入依赖
首先4个依赖,注意以后每次使用Spring框架都要引入这四个依赖:
XML
<dependencies>
<!-- 4个核心配置-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>6.1.10</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
2.2配置bean
在resources文件夹下创建"applicationContext.xml"文件,具体内容如下:
XML
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>
接下来我们引入,需要管理的类(这里叫做bean)(一定要注意,不是引入接口,而是类)
<bean>
元素的基本格式
在 Spring 配置文件中,<bean>
元素用于定义一个 Java 对象(即 Bean),并将其交给 Spring IoC 容器管理。<bean>
元素的基本格式如下:
XML
<bean id="beanName" class="FullyQualifiedClassName">
<!-- 其他属性或子元素 -->
</bean>
各个属性的含义
-
id
属性:- 用于唯一标识这个 Bean,方便在其他地方引用。
- 例如:
id="bookDao"
-
class
属性:- 指定这个 Bean 对应的 Java 类的全限定名(Fully Qualified Class Name)。
- 例如:
class="com.itheima.dao.impl.BookDaoImpl"
获取IoC容器
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 获取 Spring IoC 容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 继续操作:获取 Bean
}
}
获取到 ApplicationContext
后,可以使用 getBean
方法来获取 Bean。getBean
方法有多个重载形式,可以根据 Bean 的 id
或类型来获取 Bean。
java
// 根据 Bean 的 id 获取 Bean
BookDao bookDao = (BookDao) context.getBean("bookDao");
假设 BookService
依赖于 BookDao
,并且 BookService
的实现类中已经通过构造函数注入了 BookDao
,可以这样获取和使用 Bean:
java
public class MainApp {
public static void main(String[] args) {
// 获取 Spring IoC 容器
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 根据 id 获取 BookService Bean
BookService bookService = (BookService) context.getBean("bookService");
// 使用 BookService 对象
bookService.saveBook(new Book("Spring in Action"));
}
}
DI入门
我们可以思考以下几个问题:
1.基于IoC管理bean
2.Service中使用new形式创建Dao对象是否保留?(否)
3.Service中需要的dao对象如何进入到Service中?(提供方法)
4.Service与Dao间的关系如何描述?(配置)
我们的BookServiceImpl类如下:
java
public class BookService {
private BookDao bookDao;
public void save(){
System.out.println("book service save...");
}
//使用setter方法
public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}
}
注意到这里我们没有使用new()方法!
下面要配置Bean之间的关系
XML
<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="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置service与dao之间的关系,注意是service种引用的dao-->
<property name="bookDao" ref="bookDao"/>
<!--property标签中name指的是,这个类BookServiceImpl中定义的bookDao的名字-->
<!--而ref是,在这个xml文件中每个bean的id-->
</bean>
</beans>