IoC容器
是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),
对象在被创建的时候,是从IoC池子中获取对应的类对象。
模拟Ioc
实现的最终目的:对类中的加有@Autowired成员,可以根据其类类型自动从IoC中获取完成注入。
1. 把bean放入到IoC
bean的意思就是把类封装成一个bean类,其中包含类、对象、是否被注入、是否为单例模式这四个属性。
把bean放入到IoC的俩种方法
- 扫描包中带有Component注解的类。通过newInstance();方法为该类创建一个对象并封装该类为一个bean,将其放入到一个以类名为键,值为bean的peanPool中。
给类加@Component注解表示这个类需要被加入到bean池
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.isInterface()
|| !klass.isAnnotationPresent(Component.class)) {
return;
}
// 将这个类实例化,并将其放到beanPool中
try {
Object object = null;
Component component = klass.getAnnotation(Component.class);
boolean singleton = component.singleton();
BeanDefinition beanDefinition = new BeanDefinition();
if (singleton) {
object = klass.newInstance();
}
beanDefinition.setSingleton(singleton);
beanDefinition.setKlass(klass);
beanDefinition.setObject(object);
BeanPool.put(klass.getName(), beanDefinition);
//MethodDependence.checkDependence(klass);
//第二种放入bean的方法,同样需要在扫描包的时候进行
dealBean(object, klass);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}.scanPackage(packageName);
// 在这里进一步处理未完成的带参Bean注解方法的执行
// MethodDependence.invokeDependenceMethod();
}
-
通过给方法加@Bean注解的方式来获取bean放入到beanPool池子。方法的返回值就是需要放入到beanPool的一个bean。这个方法很灵活,因为你可以传入参数,可以在方法内部做一些其他操作,也不用改变对应类的代码。
bean注解示例@Component public class Config { public Config() { } @Bean public ForthClass getForth(Complex c, OneClass one1, OneClass one2) { ForthClass res = new ForthClass(); res.setOne1(one1); res.setOne2(one2); res.setComplex(c); System.out.println("这是获取ForthClass对象的方法"); return res; } @Bean public TwoClass getTwoClass() { TwoClass two = new TwoClass(); return two; } @Bean public Calendar getDate() { Calendar date = Calendar.getInstance(); return date; } }
解析带bean方法
private static void dealBean(Object object, Class<?> klass) { Method[] methods = klass.getDeclaredMethods(); for (Method method : methods) { if (!method.isAnnotationPresent(Bean.class)) { continue; } if (method.getParameterCount() > 0) { // 处理带参数的方法; // 处理相对复杂,涉及到参数循环依赖问题,放到下一篇博文再陈述! dealMethodWithPara(klass, object, method); continue; } Class<?> returnType = method.getReturnType(); try { Object beanObject = method.invoke(object); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setKlass(returnType); beanDefinition.setObject(beanObject); Bean bean = method.getAnnotation(Bean.class); if(bean == null || bean.name() == ""){ BeanPool.put(returnType.getName(), beanDefinition); }else{ String alias = bean.name(); BeanPool.put(alias, beanDefinition); } //MethodDependence.checkDependence(returnType); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
2. 根据类名获取bean
此前的操作,我们只是把bean放入到了beanPool中,并没有对该类中成员进行操作。可能该类中的成员是@Autowired修饰的,这就意味着当我们根据类名获取bean时,需要对该bean对象中带有@Autowired注解的成员进行自动的注入!
获取的时候再注入,这是一个很好的时机,因为获取时意味着相关类应该都是被put到了beanPool中的。这里用到了懒汉模式。
public <T> T getBean(String className) {
BeanDefinition bean = getBeanObject(className);
if (bean == null) {
showCircleDependency(); //显示循环依赖
System.out.println("Bean[" + className + "]不存在!");
return null;
}
Object object = bean.getObject();
if (!bean.isInject() || !bean.isSingleton()) {
bean.setInject(true);
// 这里完成对object中需要注入的成员的初始化工作!
inject(bean);
}
return (T) object;
}
private void inject(BeanDefinition bean) {
Object object = bean.getObject();
Class<?> klass = bean.getKlass();
Field[] fields = klass.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(Autowired.class)) {
continue;
}
Class<?> fieldClass = field.getType();
Object value = null;
if(field.isAnnotationPresent(Qualifier.class)){
Qualifier qua = field.getAnnotation(Qualifier.class);
value = getBean(qua.name());
}else{
value = getBean(fieldClass);
}
field.setAccessible(true);
try {
field.set(object, value);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}