Spring的Ioc详细解析加模拟

IoC容器

是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。
其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),
对象在被创建的时候,是从IoC池子中获取对应的类对象。

模拟Ioc

实现的最终目的:对类中的加有@Autowired成员,可以根据其类类型自动从IoC中获取完成注入。

1. 把bean放入到IoC

bean的意思就是把类封装成一个bean类,其中包含类、对象、是否被注入、是否为单例模式这四个属性。
把bean放入到IoC的俩种方法

  1. 扫描包中带有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();
	}
  1. 通过给方法加@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();
			}
		}
}	
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值