技术交流笔记——SPRING IOC 原理

简介:
前两天组织了一次内部的技术交流,主题是spring ioc框架的原理。时间很短,只有一个小时,所以内容比较简单,主要是几个题目,和围绕题目的一些讨论。

首先考虑这样一个场景:
系统中有如下几个类。
Class A {
  B b;
  C c;
  public void service(){
  Assert b!=null;
  Assert c!= null;
  ....
  }
}

Class B{
  D d;
}

Class C{
  E e;
  F f;
}

Class D{}
Class E{}
Class F{}

在调用A的service时,必须先对它进行初始化,并设置b和c的初始值。初始化B时,必须先初始化d;初始化C时,必须先初始化e和f。

问题1:使用new的方法,写出完整的对A进行初始化、并调用service()服务的步骤。
参考答案:
class A{
public A(){
this.b=new B();
this.c=new C();
}
……
}
Class B{
public B(){
this.d=new D();
}
}
Class C{
public C(){
this.e=new E();
this.f=new F();
}
}
调用代码:
public static void main(String[] args){
A a=new A();
a.service();
}

问题2: 由于需求变更,类A被修改为:

Class A {

//B b;

D d;

C c;

public void service(){

Assert d!=null;

Assert c!= null;

....

}

}

请修改问题1中的代码,使A能够正常初始化。

参考答案:

class A{
public A(){
this.d=new D();
this.c=new C();
}
……
}
……
调用代码:
public static void main(String[] args){
A a=new A();
a.service();
}

问题3、 由于需求变更,类A的服务service()被拆分成两部分service()和service_2(), 并且,系统中先有的调用service()服务的代码,有一些要改用service_2()。类A的 定义已修改如下:

Class A {

D d;

C c;

G g;

public void service(){

Assert d!=null;

Assert c!= null;

....

}

public void service_2(){

Assert g!= null;

……

}

}

请修改问题2中的代码,以适应新的需求。

参考答案:

class A{
public A(D _d, C _c){
this.d=_d;
this.c=_c;
}

public A(G _g){
this.g=_g;
}
……
}
……
调用代码:
public static void main(String[] args){
A a=new A(new D(),new C());
a.service();

A a_2=new A(new G());
a.service_2();
}
问题4、 公司发现类 A 可以使用单例实现。请修改上述代码以实现此需求。
参考答案:
class A{
private static A a=new A(new D(),new C());
private static A a_2=new A(new G());

public static A getA(){
return a;
}

public static A getA_2(){
return a_2;
}

private A(D _d, C _c){
this.d=_d;
this.c=_c;
}

private A(G _g){
this.g=_g;
}
……
}
……
调用代码:
public static void main(String[] args){
A a=A.getA();
a.service();

A a_2=A.getA_2();
a.service_2();
}


直接使用new关键字,是最基础的实例化对象的方式。使用这种方式直接进行实例化,最直接的问题是将服务的调用者和服务的实现类耦合在了一起。如问题1中,main方法中直接new一个A的实例,那么它就和类A耦合起来了。A和B、C,B和D,C和E、F,也是一样。

直接耦合带来的麻烦,在问题3、4中体现得最为明显。由于需求变更而导致的对类A进行的修改,都引发了对调用代码的修改。参考答案中只给出了一处调用。但是设想一下,如果A提供的是基础服务,比如数据库操作,或者一些基础的业务逻辑。那么,系统中会有多少对A的引用?一旦数据库从Oracle迁移到MySQL,或者某服务针对江西提供一种逻辑、针对湖南提供另一种逻辑(这种情况在我参与的项目中真实出现过),那么当修改类A时,系统中会有多少处代码要做变更?

希望这部分内容能帮助理解这样直接耦合会带来什么样的问题。
而解决问题的办法,无疑就是解耦合。问题2和问题4给了我们一种思路。
问题2将类A的创建全部封装在了无参数构造方法A()里面。这样,创建A的过程就和调用服务的过程完全隔离开了:调用A的时候完全不必知道,要创建A需要做些什么工作。可惜这种方式有点脆弱,遇到问题3就不行了,因为A()方法只能定义一个。它无法实现两种创建逻辑。
问题4延续了问题2的思路,把创建A的逻辑封装起来。调用代码中只需要获取实例并调用服务,而不需要考虑A是如何创建的。

这其实就是工厂模式的思路。

问题5、请使用工厂模式,改写问题4中的代码。
参考答案:

Public abstract class Factory {

Public static Factory aFactory(){

Return new FactoryA ();

}


Public static Factory a2Factory(){

Return new FactoryA_2();

}

Create();

}

Public class FactoryA implements Factory {

@Override

Public A Create(){

return new A(new D(),new C());

}

}


Public class FactoryA_2 implements Factory {

@Override

Public A Create(){

return new A_2(new G());

}

}

……

调用代码:

public static void main(String[] args){

A a = Factory.aFactory().Create();

a.service();


A a_2 = Factory.a2Factory().Create();

a_2.service_2();

}


问题6、将service()和service_2()方法提取到接口中,再修改问题5中的代码。

参考答案:略


问题7、用问题6的思路,为其它类(B/C/D/E/F/G)等创建工厂

参考答案:略

问题7的出现带来了一个新的困扰:那么多的类,要写那么多的工厂……尤其是,当我们对现有系统进行改造或者重构时,这种重复工作量会有多少,可想而知。


问题8:为了简化问题7的工作,公司定义了一个通用的工厂类

public Class Factory{

/**

根据入参,调用指定类的默认构造方法(无参数构造方法),生成一个指定的类的实例。如果找不到指定的类,或者指定的类没有可用的构造方法,则返回null

* <p />

例如,调用

* String a = Factory.product(java.lang.String),将返回一个””。等同于调用了 String a = new String();

*

* @param className 用户指定的类名。必须是全路径名,

* @return 用户指定的类的一个实例,或者是null

*/

public static Object product(String className){

}

}

请将①处的代码补充完整。

这里就需要用到java的反射机制了。



综合上述问题,思考一下:为什么会出现Spring IOC框架?它的基本原理是什么?

最简单的说,IOC出现的原因就是为了解耦合。利用一个通用的工厂,来管理各个实现类的创建过程以及在创建过程中引入的类的依赖关系。这样,各个类在代码层级上,是可以做到“兵不知将将不知兵”的,从而减少对类进行修改、扩展时的工作量。

它的基本原理,很明了,就是反射+工厂。



本文转自 斯然在天边 51CTO博客,原文链接:http://blog.51cto.com/winters1224/1327881,如需转载请自行联系原作者

目录
打赏
0
0
0
0
265
分享
相关文章
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
777 113
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
237 26
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
什么是Spring IOC 和DI ?
IOC : 控制翻转 , 它把传统上由程序代码直接操控的对象的调用权交给容 器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转 移,从程序代码本身转移到了外部容器。 DI : 依赖注入,在我们创建对象的过程中,把对象依赖的属性注入到我们的类中。
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
68 0
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
140 69
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
82 21
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
111 12
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
371 14

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等