(Spring)IoC初步解析(包扫描配置,IoC注入分析)

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.当前是单例模式,之后将尝试多例模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔幻音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值