2.IOC理论
2.1 IOC理论推导
- 在之前的业务中,用户需求的变化会导致原有的业务代码,需要根据用户的需求来修改源代码,程序代码量庞大,修改一次成本太昂贵。
UserDao/UserDaoImpl/UserOhterSqlDaoImpl
UserService/UserServiceImpl
UserServiceImpl{
UserDao userDao = new UserDaoImpl(); //这种对象实例方式会用户的调佣不同而经常修改
UserDao = new UserOhterSqlDaoImpl();
}
//所以创建对象不该由这里的service去决定,而是从外界传入,让调用方去实例实现对象,传入本方法中,从而保证代码不用修改也能做到适应
- 用一个set方法去适应,使用set注入后程序不具有主动性,变成了被动的接受对象
在调用service方法是注意:有强转现现象,子类自己的方法,不能通过父类变量调用,即多态父类使用子类独有的方法要强转
UserDao userDao = null;
public void setUserDao(UserDao userDao){
this.userDao = userDao
}
TiP:在传统的 Java 应用中,一个类想要调用另一个类中的属性或方法,通常会先在其代码中通过 new Object() 的方式将后者的对象创建出来,然后才能实现属性或方法的调用。为了方便理解和描述,我们可以将前者称为“调用者”,将后者称为“被调用者”。也就是说,调用者掌握着被调用者对象创建的控制权。
但在 Spring 应用中,Java 对象创建的控制权是掌握在 IoC 容器手里的,其大致步骤如下。
- 开发人员通过 XML 配置文件、注解、Java 配置类等方式,对 Java 对象进行定义,例如在 XML 配置文件中使用 标签、在 Java 类上使用 @Component 注解等。
- Spring 启动时,IoC 容器会自动根据对象定义,将这些对象创建并管理起来。这些被 IoC 容器创建并管理的对象被称为 Spring Bean。
- 当我们想要使用某个 Bean 时,可以直接从 IoC 容器中获取(例如通过 ApplicationContext 的 getBean() 方法),而不需要手动通过代码(例如 new Obejct() 的方式)创建。
- IoC 带来的最大改变不是代码层面的,而是从思想层面上发生了“主从换位”的改变。原本调用者是主动的一方,它想要使用什么资源就会主动出击,自己创建;但在 Spring 应用中,IoC 容器掌握着主动权,调用者则变成了被动的一方,被动的等待 IoC 容器创建它所需要的对象(Bean),使系统耦合性降低,让开发更加专注业务的实现,这就是IOC的原型
- 这个过程在职责层面发生了控制权的反转,把原本调用者通过代码实现的对象的创建,反转给 IoC 容器来帮忙实现,因此我们将这个过程称为 Spring 的“控制反转”。
设计模式的本质不是为了消除变化,而是为了将变化集中在一个地方
- IOC解耦的过程
2.2 IOC本质
- Spring IoC的全称是Inversion of Control,称为控制反转,是一种思想,DI(依赖注入)是实现Ioc的一种方法。
- IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
- 传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
控制反转:核心思想就是由 Spring 负责对象的创建。在对象创建过程中,Spring 会自动根据依赖关系,将它依赖的对象注入到当前对象中,这就是所谓的“依赖注入”。
- IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
- Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
- 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
- 控制反转是通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转是IOC容器,实现方法是依赖注入DI(Dependency Injection)
2.3 举例理解
- 实体类
public class Hello {
private String message;
public Hello() {
}
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {return false;}
Hello hello = (Hello) o;
return message != null ? message.equals(hello.message) : hello.message == null;
}
@Override
public int hashCode() {
return message != null ? message.hashCode() : 0;
}
@Override
public String toString() {
return "Hello{" +
"message='" + message + '\'' +
'}';
}
}
- 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用spring来创建对象,在spring这些都称为bean
相当于new对象
id ==对象名
class== 对象类型
property:set属性
message:set方法中的参数名
-->
<bean id="hello" class="com.zk.pojo.Hello">
<!--value:具体的值,基本数据类型
ref:引用spring容器中已经创建好的值 -->
<property name="message" value="hello zk!"/>
</bean>
</beans>
- 调用
@Test
public void TestDemo01(){
//获取spring的ApplicationContext上下文对象,即spring容器
//参数可以传多个
//解析bean.xml文件,生成管理相应的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("Beans.xml");
//getBean:参数为spring配置文件中bean的id
//这里不强转,也可以在第二个参数传入类型,类似mybatis
//**id前添加&获取工厂
Hello hello = applicationContext.getBean("hello", Hello.class);
System.out.println(hello.getMessage());
}
- 结果
hello zk!
注意存在的问题:
xml依赖注入arg是通过构造函数,property是通过seter方法,name属性要和实体类的属性名一致
spring容器调用无参构造器创建队像,然后用set注入,所以实体类需要无参构造器。若没有无参构造,容器通过反射创建对象(Class对象的newInstance()方法默认调用的是无参构造)会失败
没spring托管叶子:XML顶部的警告:配置上下文
- 类单继承,接口多继承,但是由于jdk8中接口可以使用default关键字具体实现方法,所以实际上实现多个接口可能会出现类的多继承的效果
public interface Trst {
default void test() {
System.out.println("方法体");
}
}
- 区别就是需求变更后只需要在配置文件中改一次,不需要到处在别的类的代码中修改,改源代码需要重新发布项目,后续这些配置会放到统一的配置中心去,类似一个后台。在线修改后,运行中的项目会直接热加载,即时生效。
改配置文件需要需要知道加载机制是热加载还是冷加载
- 依赖注入:property利用set方法进行注入,所以实体类必须有这个属性的构造方法
<property name="message" value="hello zk!"/>
- 若想进一步了解这个容器创建bean的实现可以浏览下ClassPathXmlApplicationContext类的源码
本专栏下一篇:Spring IoC 使用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123914.html