【反射】(03)反射之工厂设计模式(浅谈Spring实现IOC)
(1)工厂模式简要分析
(1.1)先来认识Spring中的IOC功能(以下内容纯属我自己的猜测)
(1.1.1)我的猜测
在spring框架中实现了IOC的容器,作用就是把所有的类对象看做是Bean,然后放进IOC容器里进行管理,当你想用到这个对象时,不再需要new来实例化对象,而是直接从IOC容器中取出已经实例化的对象。
既然IOC容器帮我们完成了实例化对象,肯定不是它自己偷偷摸摸的用new关键字实例化了对象,我们的目标就是尽量少用new关键字来降低耦合度,那么它是怎么帮我们完成对象实例化的工作呢?
经过反射的深入学习后,我知道可以根据一个类的路径(包名.类名)反向获取到这个类,那么我们把这个路径交给IOC容器,此时IOC容器已经反射得到这个类了。经过反射的深入学习,我们又知道反射的一项重要功能就是可以用Class类调用构造方法实例化对象。
那么我们就明白了,对象Bean交个IOC容器后,IOC使用反射,显示创建Class对象,然后用Class类对象调用构造方法实例化对象,完全不用new关键字。实例化的对象就在容器中管理,想用你就直接来拿吧。
(1.1.2)总结一下:Spring的IOC用的技术有两点
1)你把类的路径给我,我能根据这个路径反向获取你这个类
2)有了这个类,我再用Class类来实例化这个类的对象,并把对象返回给你
(1.1.3)来看看实际应用中IOC容器是怎么实现DI依赖注入的:
1)在类的上方加注解,这个注解就把这个类的路径传给IOC容器了
IOC获取到路径以后就要开始一系列的骚操作了,原理代码如下:
Class<?> clazz=null;
String path="注解@Mapper传进来的类路径";
//Class对象实例化,反射声明RealSubject类对象
try {
//实例化Class类对象最常用的形式
clazz=Class.forName(path);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
现在还没有实例化对象,我们接着往下走
2)当你要用到这个对象时,你会先声明对象,并在这个对象上加上注解
加上注解后,就等于通知IOC容器,老哥,我现在急用对象,赶紧实例化一个对象给我用用。于是IOC开始如下的骚操作,原理代码如下:
//声明RealSubject类对象
CategoryMapper categoryMapper= null;
//Class类对象调用newInstance方法,等于调用RealSubject构造方法来对象实例化
try {
//Java9之后,newInstance方法已经被弃用,改用:getDeclaredConstructor().newInstance()
categoryMapper= (CategoryMapper ) clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
此时你声明的对象已经被Class类用“clazz.getDeclaredConstructor().newInstance();”这串代码调用构造器实例化好了,一眨眼的功夫,你的对象categoryMapper 已经被实例化了。你可以开始愉快的调用了
(1.2)实现一个简单的工厂设计模式
(1.2.1)第一步:实现一个接口Fruit,统一管理子类
public interface Fruit {
public void eat();
}
(1.2.2)第二步:实现两个接口的子类
public class Orange implements Fruit {
@Override
public void eat() {
System.out.println("吃橘子");
}
}
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("吃苹果");
}
}
(1.2.3)实现一个工厂(类似于IOC容器)
上面说的,Spring中是使用注解@Mapper把路径传给容器的,我们这里手动传路径,把路径当做参数className传给方法getInsance,这个方法专门用反射实例化对象,有了路径一切就都好说了,瞧好吧您
public class Factory {
public static Fruit getInsance(String className){
Fruit fruit = null;
try {
fruit = (Fruit) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return fruit;
}
}
(1.2.3)测试类
上面我们说了,getInstance方法就是专门用反射根据路径实例化对象的,现在我们声明对象,然后把子类的路径当做参数className传给方法,方法内部就会实例化对象,并且返回一个已经实例化的对象,我们再把这个对象向上转型成Fruit类型
public class FactoryDemo {
public static void main(String[] args) {
Fruit apple = Factory.getInsance("part03_工厂模式.p05_反射之工厂设计模式.Apple");
apple.eat();
Fruit orange = Factory.getInsance("part03_工厂模式.p05_反射之工厂设计模式.Orange");
orange.eat();
}
}
我们用上面方法返回的对象调用类的方法,看看它是不是真的已经实例化了,运行结果如下
果然,这个对象已经完成实例化了,仔细看,我们从头到尾都没有使用new,这就是我们追求的效果。