ABSTRACT
Spring的IOC(控制反转)是Spring框架的灵魂,有了它才有了Spring的很多其他的灵活的特性。使用 Spring 开发项目时,控制层、业务层、DAO 层都是通过 IoC 来完成依赖注入的。
IOC其实就是工厂模式+Java的反射机制,所以要理解IOC,我们必须要对工厂模式和反射机制有了解。
什么是IOC
在传统的程序开发中,当需要调用对象时,通常由调用者来创建被调用者的实例,即对象是由调用者主动 new 出来的。
但在 Spring 框架中创建对象的工作不再由调用者来完成,而是交给 IoC 容器来创建,再推送给调用者,整个流程完成反转,因此是控制反转。
反射机制
反射(Reflection)是其Java非常突出的一个动态相关机制。它可以于运行时加载、使用编译期间完全未知的classes(但是要知道类的名字)。也就是说,在运行时,我们还可以获取一个类中所有的方法和属性,可以实例化任何类的对象,还能判断一个对象所属的类。
这样做的好处是,我们在程序运行时才会确定类型绑定对象,而不是在编译时。很多基础框架都用到了Java的这个机制,例如Spring框架、Struct等等,因为这些基础框架往往需要适用于不同的场景,因此对程序的灵活性要求很高,所以动态创建对象、获得类的信息就尤为有用。再比如,一个大型的软件发布后,如果需要更新功能,停止运行然后在重新编译是不现实的,如果利用了java的反射机制,就不需要为了新增加的功能重新编译程序。
反射机制的实现
我们可能之前就有见过反射机制的出现,就是在JDBC中加载数据库驱动时的
Class.forName(“com.mysql.jdbc.Driver”)
如果我们要使用一个类,就要获得这个类的反射类。有三种方法:
1. GETCLASS()方法
所有类的对象都是Class的实例。
String s = “Tomato”;
Class> c = s.getClass();
2. CLASS的静态方法FORNAME
Class> c = Class.forName(“Tomato”);
3. 知道类名,我们可以直接用.CLASS
Class> c = String.class;
实例化一个(构造函数不带参)类的对象:
Object obj = a.newInstance();
实例化一个(构造函数带参)类的对象:
Constructor constructor=c.getConstructor(String.class,int.class);
constructor.newInstance(“Java”,30);
获取对象中的所有方法:
Method[] methods = classType.getDeclaredMethods();
for (int i=0;i
//获取方法名字
String methodName=methods[i].getName();
//获取本方法所有参数类型,存入数组
Class>[] parameterTypes = methods[i].getParameterTypes();
根据方法名用于获取Class类指代对象自己声明的某个方法 getDeclaredMethod();
Method m = c.getDeclaredMethod();
invoke(); 就可以触发方法
m.setAccessible(true);
m.invoke(c);
*不管是public,default,protect还是pricate方法,通过反射类我们都可以自由调用。(但是实际开发中不建议)
获取方法的注解
在Spring中注解扮演着很关键的角色,反射机制也能让我们获取方法的注解
m.getAnnotation(UseCase.class);
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
使用场景:在我们明确地计划不同条件下创建不同实例时使用,而且通常情况下,客户代码不知道或不需要知道创建的对象是来自哪一个类(由服务提供者决定)。主要解决接口选择的问题。
简单的工厂模式
Shape.java 创建一个Shape接口
public interface Shape {
void draw();
}
三个接口的实体类
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println(“Inside Rectangle::draw() method.”);
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println(“Inside Square::draw() method.”);
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println(“Inside Circle::draw() method.”);
}
}
工厂类,决定实例化哪一个类的对象
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase(“CIRCLE”)){
return new Circle();
} else if(shapeType.equalsIgnoreCase(“RECTANGLE”)){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase(“SQUARE”)){
return new Square();
}
return null;
}
}
这个工厂模式有一个问题是,它的工厂类是静态的,如果我们需要增加产品类别,就必须要添加判断条件,这不符合面对对象的开放封闭原则。当然有其他的设计模式可以解决这个问题,例如工厂方法模式,把工厂类变为抽象类,在它的实现类里实例化对象,然后客户代码中,通过配置文件等来实例化工厂类的实体类。如果利用反射,可以轻松解决这个问题。
在工厂类中,我们提前不知道会需要实现什么类,只有在程序运行时我们才会知道。这就符合了反射机制的特性。
在工厂模式中加入反射机制:
public class ShapeFactory {
public Shape getShape(String ClassName){
if(shapeType == null){
return null;
}
Shape s = null;
try{
s = (Shape)Class.forName(ClassName).newInstance();
}catch (Exception e) {
e.printStackTrace();
}
return s;
}
}
现在就算我们添加任意多个子类的时候,工厂类就不需要修改。当然,简单使用上述代码需要传入完整的包名和反射类名,用户可能并不知道,所以我们通过属性文件的形式配置所需要的子类。(属性文件就不说啦)
说回SPRING中的IOC
在Spring中,是在 spring.xml 中配置 bean 标签,IoC 容器通过加载 bean 标签来创建对象的。
BEAN有两种构造方法:有参构造和无参构造。
无参构造:
有参构造:
如何获得IOC创建的对象呢?
Spring提供了两种方法来获取这个对象:通过ID和运行时类
通过ID:
1.加载 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“spring.xml”);
2.通过 id 值获取对象
Student stu = (Student) applicationContext.getBean(“stu”);
通过运行时类
1.加载 spring.xml 配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“spring.xml”);
2.通过运行时类获取对象
Student stu = applicationContext.getBean(Student.class);
System.out.println(stu);
什么是依赖注入?(DI)
依赖注入是将不同对象进行关联的一种方式。
例如创建了一个class对象后,再创建一个student对象,student对象中有一个属性是class,需要绑定刚刚创建的class。此时就需要用到依赖注入。
如有疏漏,欢迎交流!
Reference:
《Java设计模式》Steven John Metsker; William C. Wake
欢迎收藏我的blog: