关于Spring IOC(一)
首先我们先谈谈对Spring IOC(控制反转)、DI(依赖注入)的理解,IOC是英文(Inversion of Control)的缩写,IOC控制反转不是具体的技术,而是一个编程思想,它可以指导我们写出低耦合高性能的程序,之前的程序都是在编写过程中便完成了对对象的初始化并将其注入到程序中,但是IOC思想将你设计好的程序中的对象交由“容器”完成依赖查找和注入而不是将其在程序中直接控制。
关于DI(依赖注入):组件之间的依赖关系由程序在运行期决定,由“容器”动态的将依赖关系注入到某个组件当中,通过依赖注入我们只需要一些配置就可以实现无需代码指定目标需要的资源,不需要管资源来自于哪就完成剩下的逻辑。
控制反转和依赖注入是同一概念不同角度的描述。关于Spring IOC的理解可以通俗点讲为:在我们现在的书写程序过程中,当我们需要一个对象时一般都是用new方法,但是在使用控制反转后,我们可以通过一些配置或者注解的方式完成对该对象的初始化,在后面的使用中我们也不需要考虑它到底来自于哪,怎样生成的。
一个小例子
先举一个小例子:
//
public class TwoClass {
public TwoClass() {
}
@Override
public String toString() {
return "这是一个TwoClass的对象";
}
}
//
public class OneClass {
private TwoClass two;
public OneClass() {
}
public void doOneThing() {
System.out.println(two);
}
}
//
public class Demo {
public static void main(String[] args) {
OneClass one = new OneClass();
one.doOneThing();
}
}
上面两个简单类已经测试函数对其的调用,运行结果是null;因为OneClass类中的TwoClass类并没有初始化。
我们再看下面的代码片:
//
Class<?> klass = OneClass.class;
try {
Field field = klass.getDeclaredField("two");
field.setAccessible(true);
field.set(one, new TwoClass());
one.doOneThing();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
除了上面说的对TwoClass调用new的方法外,若是在主函数中利用反射机制完成OneClass中two成员的初始化和注入也可以。
Compent注解和AutoWired注解
上面就是一个非常非常简单的DI过程,不过太过于简单,下面我们可以这样考虑:
将某应用所设计的类及对象,都集中存储到一个集合中,凡是在这个集合中的类,尤其是这些类的成员类型也在这个集合中。则这些成员的初始化都从集合中的对象还给与;
举一个简单的例子:A类、B类、C类都在集合中,另外。A类中有B类类型的成员,有C类类型的成员,B类有C类类的成员,南无A类的B类类型成员,C类类型成员,以及B类中的C类成员,都由一代代码自动完成“初始化”,以后使用这些类的对象的时候,一律在从这个集合中取。
从另一个角度思考上述的问题:
构建一个容器,在这个容器中存储类及其对象;在使用这些类的对象时,基本上都是从这个容器中获取的;这些类的成员。若其类型也在容器中,则它们将被自动初始化且容器中的对象完成初始化。这里需要说明的一点是类中的成员初始化选择应该由用户决定。
选择的两种具体方法:
①通过XML配置映射关系
②通过注解配置映射关系
在此处我们选择用注解配置的方法完成;
在正式开始之前我们需要考虑我们需要建立哪些注解和该注解是该被用在类上还是方法、成员上。
关于Compent注解
首先我们建立@Component注解,该注解应用在方法上面,并且默认被注解的类为单例模式,若有方法被@Component注解注释则表示该方法是需要被放到容器中。
//
@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
boolean singleton() default true;
}
关于AutoWired注解
建立使用在成员以及方法上的注解@AutoWired,若某类被@Compent标注,其某成员被@AutoWired标注,则表示该类的该成员需要“注入”,若该注解用在方法上,使用者应保证其实使用在setter方法上;
setter方法一般有如下特征:
①只有一个参数
②无返回值,当然也可以是返回本类型对象
//
@Documented
@Retention(RUNTIME)
@Target({ FIELD, METHOD })
public @interface AutoWired {
}
扫描路径中文件并将其放入池子中
//
public static void scanPackage(String packageName) {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
if(klass.isPrimitive()
|| klass == String.class
|| klass.isAnnotation()
|| klass.isArray()
|| !klass.isAnnotationPresent(Component.class)
|| klass.isInterface()) {
return;
}
//将这个类实例化,并放到beanpool中
try {
Object object = null;
Component component = klass.getAnnotation(Component.class);
boolean singleton = component.singleton();
BeanDefination beanDefination = new BeanDefination();
if(singleton) {
object = klass.newInstance();
}
beanDefination.setSingleton(singleton);
beanDefination.setKlass(klass);
beanDefination.setObject(object);
beanPool.put(klass.getName(), beanDefination);
MethodDependence.checkDependence(klass);
dealBean(klass, object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}.scanPackage(packageName);
MethodDependence.invokeDependceMethod();
}
上述的操作完成了所有对@Componet注解的实例化,并将其放到池子中的操作,但是还没有实现对这时实例中由@Autowired成员的注入工作,除了用注解方式实现依赖关系的描述外,还可以用XML方式配置依赖关系;这种情况下,对于Bean的发现,可能需要多次“包扫描”或者“XML”配置解析,那么在没有完全扫描完之前,是没有办法确定依赖关系的完整性的,因此在包扫描或解析XML配置时,不能给予处理依赖关系(不能急于“注入”工作),完成注入的最佳时机应该延迟到GetBean()时,即“懒汉模式”。
关于bean注解
下面我们思考一个问题,若@Compent注解被用到的一个类中需要被注入的一个成员为jar包中的一个类的对象,而jar包中的类不能在给其添加@Compent注解,那么上面的思考过程就不能满足这种场景,那么该怎样解决这个问题?
bean注解就是为了解决这样的问题存在的,对一个方法使用bean注解,就将该方法的返回值类型对象和返回值类型形成键值对放入池子中。
bean注解还有第二种使用场景:
对于有些类的无参构造对象不是简单无参构造就能使用的,意思有些是有无参构造的,但是无参构造出来的对象不能使用,这时就可以使用bean注解,完成该数据所必需的的基础数据,从而使得该对象可使用。
bean注解第三种使用场景:
适用于相关类没有提供可使用的构造方法,相关构造方法是private的,或者构造方法不能直接调用,或者,构造方法不能直接生成对象,在这种情况下,由于@Compent注解的处理是通过调用相关类的无参构造产生的,那么对于上述情况,就不能产生这个类的对象,取得这个类的对象并加入到beanpool中。
bean注解
//
@Retention(RUNTIME)
@Target(METHOD)
public @interface Bean {
}
下一篇我们讲讲解IOC中有关循环依赖等问题。