IoC:控制反转
其意义是假如现在有两个类,一个是A,一个是B,但A是B的一个成员,那在实例化B的时候,也需要实例化一个A去加到B中,而IoC就实现了自动执行这个操作的功能。
那么为了实现这个功能,首先需要由用户去标记想要实例的类,本身分为xml与注解,这次用注解实现IoC。
进一步分析IoC:
1.功能:自动初始化指定类中的复杂类成员对象,作为Bean
2.注解分两类:表明该类是Bean,以及表示该成员需要被注入
component注释:
@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
}
该注释用于类,表示该类是Bean。
Autowired注释:
@Retention(RUNTIME)
@Target(FIELD)
public @interface Autowired {
String value() default "";
}
该注释用于成员,表示该成员需要被注入,由value获取值
Bean定义:
public class BeanDefinition {
private Class<?> klass;
private Object object;
private boolean inject;
}
其中类和对象即该Bean,inject表示该类是否已经被注入过
我们使用注释+包扫描的形式完成注入,需要考虑扫描顺序的问题,例如上述例子,若先扫到B,而A还未实例化,注入难以完成,故此处应该使用“懒汉”模式,在扫描包时,Bean内的复杂类先设置为null,当扫描完,用户开始调用该类,再执行注入过程(即将生成好的类对象放入相应的成员位置)。
包扫描:
public static void scanPackage(String packageName) {
try {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) throws Exception {
if (klass.isInterface()
|| klass.isPrimitive()
|| klass.isArray()
|| klass.isAnnotation()
|| !klass.isAnnotationPresent(Component.class)) {
return;
}
Object object = klass.newInstance();
BeanDefinition bd = new BeanDefinition();
bd.setKlass(klass);
bd.setObject(object);
bd.setInject(false);
beanPool.put(klass.getName(), bd);
}
}.packageScan(packageName);
} catch (Exception e) {
e.printStackTrace();
}
}
建一个Map用于储存类名与Bean属性对应关系:
private static final Map<String, BeanDefinition> beanPool;
扫描到包内类被component注释过,利用反射机制生成该类、对象加入Definition,最后将该类名及对应关系加入Map。
用户调用过程:
@SuppressWarnings("unchecked")
public static <T> T getBean(String className) {
BeanDefinition res = beanPool.get(className);
if (res != null) {
Object bean = res.getObject();
try {
doInject(res);
} catch (Exception e) {
e.printStackTrace();
}
return (T) bean;
}
return null;
}
这个方法可以看出来类的具体注入是发生在用户需要该类时再进行。
注入过程:
private static void doInject(BeanDefinition bean) throws Exception {
if (bean.isInject()) {
return;
}
Object object = bean.getObject();
Class<?> klass = bean.getKlass();
Field[] fields = klass.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(Autowired.class)) {
continue;
}
Autowired autowired = field.getAnnotation(Autowired.class);
String value = autowired.value();
Object property = null;
Class<?> fieldType = field.getType();
String typeName = fieldType.getName();
if (value.length() <= 0) {
property = getBean(typeName);
} else {
String valueString = autowired.value();
property = TypeParser.getValue(typeName, valueString);
}
String fieldName = field.getName();
String setterName = "set" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
Method setterMethod = klass.getDeclaredMethod(setterName,
new Class<?>[] {fieldType});
setterMethod.invoke(object, property);
}
bean.setInject(true);
}
注入方式:
扫描该类下的成员,若该成员被Autowired注释修饰,经过由下几步:
1.该成员的值由value获得
2.依据value的值个数可以判断该成员是复杂类(value无值)或是八大基本类型(值直接设置在value中)
3.复杂类处理:根据类名在pool中寻找该类的BeanDefinition(此处注意这种获取方式只有在单例IoC中可以做到,因为多例时,一个类名下将有多个Bean)
基本类型处理:此处只需要进行将值从字符串类型转为基本类型即可,用到TypeParser工具。
4.经过第三步,就将值对象获取到了,接着就在Bean中利用字符串拼凑出set方法,用反射机制调用即可。
5.注入完成后将是否已完成注入的判断设置好(此处将涉及循环依赖,将在之后解释)
经过上述,基本的IoC已经制作好了,但还有如下问题:
1.循环依赖问题
2.我们使用的是注解标记,但当需要的类是jar包里的类,我们无法修改代码该如何解决
3.当前是单例模式,之后将尝试多例模式