注:该篇文章会与我的个人博客同步更新。欢迎移步https://cqh-i.github.io/体验更好的阅读效果。
什么是 IOC
全称 Inversion of Control(控制反转)。将对象的创建权反转给了Spring。
与传统方式对比
传统方式的调用中,接口和实现类之间的耦合度太高,不满足OCP原则(对修改关闭,对扩展开放)。如有一个接口和它的两个实现类:
/*
* 某功能接口
*/
public interface UserDao{
public void save();
}
/**
* 该功能接口的实现类一
*/
public class UserDaoImpl1 implements UserDao{
@Override
public void save() {
System.out.println("UserDaoImpl1执行了。。。");
}
}
/**
*该功能接口的实现类二
*/
public class UserDaoImpl2 implements UserDao{
@Override
public void save() {
System.out.println("UserDaoImpl2执行了。。。");
}
}
当我们需要切换接口的实现类时,传统方式弊端就出来了,UserService userService = new UserDaoImpl1();
改为UserService userService = new UserDaoImpl2();
一个好的程序设计应尽量满足OCP原则,在尽量不修改程序源码的基础上对程序进行扩展。
上述接口和实现类之间的耦合可以通过工厂模式来解决。
public class BeanFactory {
public static UserDao getUserDao() {
return new UserDaoImpl1();
}
}
现在接口和实现类之间没有耦合,但是工厂和接口之间有耦合。如我们要切换接口实现类还要要去修改工厂类里面的方法,如改为return new UserDaoImpl2();
。
要解决上述问题,需要通过工厂模式+反射+配置文件来实现程序解耦合。
<bean id="userDao" class="xxx.UserDaoImpl1"></bean>
class BeanFactory {
public static Object getBean(String id){
//解析XML
//反射
Class clazz = Class.forName("xxxImpl");
return clazz.newInstance();
}
}
IOC的底层实现原理
上述的工厂模式+反射+配置文件方式就是IOC的底层实现原理。
将实现类交给Spring管理
- 创建applicationContext.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">
<bean id="UserDao" class="com.itheima.spring.demo1.UserDaoImpl1"></bean>
</beans>
- 编写测试类
@Test
public void test() {
// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
userDao.save();
}
运行结果:UserDaoImpl1执行了。。。
通过这种方式,当我们需要切换底层实现类时,只需要修改配置文件,而不用去修改源代码。
IOC 和 DI
DI:依赖注入,前提必须有IOC的环境,Spring管理这个类的时候将类的依赖的属性注入(设置)进来。
假如UserDaoImpl1中有一个属性name(String),
public class UserDaoImpl1 implements UserDao{
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void save() {
System.out.println("UserDaoImpl1执行了。。。"+name);
}
}
那么可以在配置文件中,注入属性的值
<bean id="UserDao" class="com.itheima.spring.demo1.UserDaoImpl1">
<property name="name" value="WS"/>
</bean>
执行测试方法
@Test
public void test() {
// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
userDao.save();
}
运行结果:UserDaoImpl1执行了。。。WS
Spring IOC 注解开发方式
- 引入约束:引入context约束。
- 开启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"
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">
<!-- 使用IOC的注解开发,第一步需要配置扫描,哪些包下的类使用IOC的注解 -->
<context:component-scan base-package="com.itheima.spring.demo1"></context:component-scan>
</beans>
- 在类上添加注解
@Component("UserDao")//相当于在xml里配置<bean id="userDao" class="com.lee.spring.demo1.UserDaoImpl"/>
public class UserDaoImpl1 implements UserDao{
@Override
public void save() {
System.out.println("UserDaoImpl1执行了。。。");
}
}
4.编写测试类
@Test
public void test() {
// 创建Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
userDao.save();
}
运行结果:UserDaoImpl1执行了。。。
注解方式设置属性的值
注解方式设置属性的值,可以没有set方法的。
- 属性如果有set方法,需要将属性注入的注解添加到set方法上
- 属性如果没有set方法,需要将属性注入的注解添加到属性上
@Component("UserDao") // 相当于在xml里配置<bean id="userDao" class="com.lee.spring.demo1.UserDaoImpl"/>
public class UserDaoImpl1 implements UserDao {
@Value("李四")
private String name;
/* @Value("李四")
public void setName(String name) {
this.name = name;
}*/
@Override
public void save() {
System.out.println("UserDaoImpl1执行了。。。"+name);
}
}
运行结果:UserDaoImpl1执行了。。。李四
Sping的IOC的注解详解
- @Component:组件 修饰一个类,将这个类交给Spring管理。
(这个组件可以修饰任何一层的类,包括Dao,Service,web层。因为分层结构不明显,后来提供了三个衍生类,但目前来讲三个注解和Component功能一样,只是建议使用三个衍生注解,后面的版本可能会进行扩展)
三个衍生注解(功能类似)
@Controller:web层
@Service:service层
@Repository:dao层
属性注入的注解
- 普通属性:
@Value:设置普通属性的值 - 对象类型属性:
@Autowired:设置对象属性的值,但是按照类型完成属性注入
替代注解@Resurce:设置对象属性的值,按照名字完成注入