Ioc基本原理与原型实现
对于Ioc概念的进一步理解
传统的Java设计中,直接会在对象的内部通过new进行对象的创建,是程序主动创建以来对象;对Ioc来说,有一个专门的容器专门管理这些对象的生命周期,控制对象的创建;所以在Ioc中,是通过Ioc容器控制对象,由Ioc容器控制外部资源的获取。
传统应用中是由我们自己在对象中主动控制去直接获取以来对象;反转则是通过容器来进行对象的依赖和创建,这里,对象只是被动的接收依赖对象,因此称为反转,而反转的则是依赖对象的获取被反转了。
下面我以两张示意图来示意Ioc的机制
从图中我们直观的看出Ioc将对象依赖的获取权利由原来的客户端程序反转到Ioc容器
Ioc不是一种具体的技术,而是一种设计思想,Ioc的目的是为了如何指导我们编写出更加松耦合,更加优雅的程序。传统的应用程序使我们在类的内部显式的创建依赖的对象。从而导致类于类之间耦合度过高。而使用了Ioc的设计思想,将对象的创建,查找依赖,以及生命周期的控制权交给了Ioc容器。对象之间耦合较松,更加灵活。
DI是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中,DI是Ioc的另外一种表达方式,相当于Ioc思想的具体实现
反射实现的一个重要原因是因为每个类在加载的过程中都会生成一个代表这个类的java.lang.Class对象作为方法区数据访问的入口,具体的类加载过程详见Java静态代码执行机制,所谓反射,就是通过获取某个类的class对象后反向的获取某个类或对象的属性及方法信息。
Class<?> dateCls = new Date().getClass(); System.out.println(dateCls.getName());
Class<?> dateCls2 = Date.class; System.out.println(dateCls2.getName());
Class<?> dateCls3 = null; try { dateCls3 = Class.forName("java.util.Date"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(dateCls3.getName());
反射的作用十分广泛。最典型的应用就是工厂设计模式的应用。考虑如下的需求,如果有一个水果类,派生出很多种水果类型,每当给出一种水果的名字,都可以生产出一种对应的水果对象。最常见的思路就是利用工厂设计模式。
1、首先声明Ifruit接口类
public interface IFruit { void eat(); }
2、创建两个实现类Apple和Orange
public class Apple implements IFruit{ @Override public void eat() { System.out.println("苹果"); } }
public class Orange implements IFruit { @Override public void eat() { System.out.println("橙子"); } }
3、创建水果工厂类FruitFactory
public class FruitFactory { public static IFruit getInstance(String fruitName) { IFruit instance = null; if (fruitName.equals("Apple")) { instance = new Apple(); }else if(fruitName.equals("Orage")) { instance = new Orange(); }else { System.out.println("illeagle class name"); } return instance; } }
4、主类
public class TestDemo { public static void main(String[] args) { IFruit obj = null; obj = FruitFactory.getFruit("Apple");//创建了苹果类 obj.eat();//调用接口的eat方法 } }
传统工厂模式的类关系图
对于传统的工厂设计模式,有一个比较严重的问题。如果需要添加新的水果类,就需要改动Factory类的代码,这会给编程带来很大的不便。因此一个比较好的方法时采用反射的方式进行解决。
我们对工厂类进行如下的修改,代码如下:
public static IFruit getInstance(String className) { IFruit fruit = null; try { fruit = (IFruit) Class.forName(className).newInstance();//Class类的forName方法构造类的实例 }catch (Exception e) { e.printStackTrace(); } return fruit; }
对主类的修改:
public static void main(String[] args) { IFruit obj = null; obj = FruitFactoryByReflection.getInstance("com.bryantchang.reflectionTest.Apple"); obj.eat(); }
对于使用反射机制实现的工厂模式,相比传统的工厂设计模式,当增加新类型的时候,只需要将类名传入工厂即可获取对应类型的对象。使得代码变得更加灵活,然而用户无法准确传入完整的包名和类型,因此我们对于这个工厂方法进行了进一步的改造。形成了Ioc思想的雏形。
1、首先创建一个处理bean资源文件的类BeanResourceOps
public class BeanResourceOps { public static Properties getBeanProperties() throws IOException { Properties beanProperty = new Properties(); File beanResourceFile = new File("/Users/changshilu/Desktop/work/meituan/codes/test_codes/personal_growth/testJava/src/main/resources/fruit.properties"); if (beanResourceFile.exists()) { beanProperty.load(new FileInputStream(beanResourceFile)); }else { beanProperty.setProperty("apple", "com.bryantchang.reflectionTest.Apple"); beanProperty.setProperty("orange", "com.bryantchang.reflectionTest.Orange"); beanProperty.store(new FileOutputStream(beanResourceFile), "fruit bean infos"); } return beanProperty; } }
2、修改主类代码
public static void main(String[] args) throws IOException { IFruit obj = null; Properties fruitProperties = BeanResourceOps.getBeanProperties(); obj = FruitFactoryByReflection.getInstance(fruitProperties.getProperty("apple")); obj.eat(); }
这样,我们完成了一个Ioc思想的初步实现,当然上述的机制完成的并不彻底,对于bean的声明周期并没有明确的进行管理,对于bean的注入也过于简单。在后续的博客中,我们将结合Spring中Ioc容器的源代码,并抽取出其中的精华部分,自己实现一个Ioc容器。
谢谢你请我吃糖果