为什么需要控制反转
我们经常在service层需要调用dao层的方法,在controller层调用service层的方法,在学习spring之前,我们使用 new 对象 的方法进行方法调用
java
// dao
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ...");
}
}
// service
public interface UserService {
public void save();
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
public void save() {
System.out.println("user service ...");
userDao.save();
}
}
- 业务层需要调用数据层的方法,就需要在业务层new数据层的对象,代码耦合度偏高
- 为解决这个问题,Spring提出控制反转这一核心概念,使用对象时,在程序中不再使用new产生对象,转换为由"外部"提供对象
控制反转是什么
Spring控制反转(IoC)是一种设计模式,用于将控制权从代码本身转移到外部容器。
- 控制:对象的创建权
- 反转:对象的创建权由程序内部(new 对象) 转移到外部(Spring Ioc 容器)
- 在Spring框架中,我们可以通过配置文件或注解来描述这些对象,并将它们装配到容器中。这样,当我们需要访问某个对象时,我们不再需要显式地创建它,而是可以在容器中查找它并获取对它的引用。
Spring和Ioc的关系
- IoC是Spring框架的核心概念之一,它通过实现依赖注入(DI)来实现对象之间的解耦和管理。
- Spring技术对IOC思想进行了实现,他提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"
- Spring的IoC容器负责创建、管理和维护应用程序中所需要的对象及其之间的关系。
- 创建或被管理的对象在IOC容器中统称为 Bean
依赖注入是什么?
依赖注入 即DI(Dependency Injection),它是一种软件设计模式,用于管理对象之间的关系和依赖,在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。
- 我们可以根据需求建立起依赖关系,如上述代码,业务层需要依赖数据层,UserService就要和UserDao建立依赖关系
依赖注入常见的三种方式
- 构造函数注入(Constructor Injection):通过构造函数将依赖对象传递给Bean。
- 属性注入(Setter Injection):通过属性的setter方法将依赖对象注入到Bean中。
- 注解注入(Annotation Injection):使用注解(如@Autowired、@Resource)注入依赖对象。
修改上述代码,删除service中使用new的方式创建的dao对象
java
// service
public interface UserService {
public void save();
}
public class UserServiceImpl implements UserService {
private UserDao userDao ;
public void save() {
System.out.println("user service ...");
userDao.save();
}
}
构造函数注入
- 在
UserServiceImpl
中添加一个构造函数接收一个UserDao
对象,并将其分配userDao字段
java
// UserServiceImpl 中添加构造函数
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
可以使用XML配置文件或注解来指定依赖项的注入方式。
此处使用XML配置文件进行构造函数注入
xml
<bean id="userService" class="com.example.UserServiceImpl">
<constructor-arg ref="userDao" />
</bean>
<bean id="userDao" class="com.example.UserDao" />
- bean-id :bean的唯一标识名
- class:指定 bean 的类型,配置bean的全路径类名,与具体java类一致
- constructor-arg: 来指定构造函数参数,ref 属性指定了依赖的 dao Bean,ref的值与bean的标识名一致
setter注入
在 UserServiceImpl 中添加一个Setter方法接收 UserDao对象,并将其分配给userDao字段
java
// 添加set方法
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
使用XML配置文件进行set注入
xml
<bean id="userService" class="com.example.UserServiceImpl">
<property name="userDao" ref="userDao" />
</bean>
<bean id="userDao" class="com.example.UserDao" />
- property: 用于来指定set注入的属性依赖项
- name:指定要注入的属性名称,Spring的IOC容器在获取到名称后,将首字母大写,前面加set找对应的
setUserDao()
方法进行对象注入,所以 name的值应与 java类中对应字段的值一致 - ref:属性指定了依赖的 userDao Bean。Spring将会在IOC容器中找到id为
userDao
的Bean对象给userService
进行注入
注解注入
在上述UserDaoImpl类上添加@Component
注解
java
@Component("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("user dao save ..." );
}
}
- @Component:类似注册了一个bean,id为 userDao
在UserServiceImpl 中添加字段注解:@Autowired ,添加类注解@Component
java
@Component
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao ;
public void save() {
System.out.println("user service ...");
userDao.save();
}
}
XML配置文件配置组件扫描
xml
<context:component-scan base-package="com.example" />
- 使用
<context:component-scan>
元素来指定要扫描的包,使Spring容器能够自动检测到带有@Autowired
注解的依赖项。
在实际开发中,我们大多数情况下使用注解的方式进行开发