TeaFramework——IOC容器实现(二)

    上一篇讲了生成的Bean这样放在Map容器里已经属性的注入过程,这一篇中将讲到Class怎么被扫描以及怎么被实例化的。

    对于web工程而言,要扫描的class有两个来源:

    1、项目中写的class,放在classpath路径下

    2、class被打包成jar,放在classpath路径或者classpath的子路径下,如:lib等。

    对于要扫描的路径,windows和Linux、MacOS有些不同,所以对windows特殊处理一下,ScanPackageInitialization中主要是init方法。

public void init() throws Exception {
		logger.info("开始获取需要扫描的类");
		String classpath = getClass().getResource("/").getPath();
		// windows下
		if ("\\".equals(File.separator)) {
			classpath = classpath.substring(1);
		}
		getCommonClassPaths(classpath);
		getJarClassPaths(classpath);
		logger.info("获取需要扫描的类结束");
	}

    init方法中有两个主要方法,先来说getCommonClassPaths,这个方法是获取放在classpath路径下的所有class的全名,包括完整包路径。

private void getCommonClassPaths(String classpath) {
		for (String basePackage : SystemVariable.baseScanPackages) {
			String searchPath = classpath + basePackage.replace(".", File.separator);
			getClassPath(new File(searchPath));//递归获取class的文件路径及文件名
		}
		for (String filePath : filePaths) {
			String relativeClassPath = filePath.substring(classpath.length()).replace(CLASS_SUFFIX, "")
					.replace(File.separator, ".");
			classPaths.add(relativeClassPath);
		}
		filePaths = null;// 便于释放内存
	}

    getJarClassPaths是获取classpath路径及其子路径下所有jar内的class路径

private void getJarClassPaths(String classpath) throws Exception {
		getJarPath(new File(classpath).getParentFile());// 获取或有jar
		for (String jarPath : jarPaths) {
			JarFile jarFile = new JarFile(jarPath);
			Enumeration<JarEntry> entrys = jarFile.entries();
			while (entrys.hasMoreElements()) {
				JarEntry jarEntry = entrys.nextElement();
				String file = jarEntry.getName();
				if (file.endsWith(CLASS_SUFFIX)) {
					for (String basePackage : SystemVariable.baseScanPackages) {
						if (file.startsWith(basePackage.replace(".", "/"))) {
							String clazz = file.replace(CLASS_SUFFIX, "").replace("/", ".");
							classPaths.add(clazz);
						}
					}
				}
			}
			jarFile.close();
		}
	}

    获取到class的全路径以及名称后,下一步就是加载了,我们需要自定义一个类加载器

public class ClassLoaderUtil {
	private static URLClassLoader loader;
	static {
		try {
			URL[] urls = new URL[ScanPackageInitialization.jarPaths.size() + 1];
			for (int i = 0; i < ScanPackageInitialization.jarPaths.size(); i++) {
				urls[i] = new URL("file", null, ScanPackageInitialization.jarPaths.get(i));
			}
			urls[urls.length - 1] = new URL("file", null, ClassLoaderUtil.class.getResource("/").getPath());
			loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static <T> Class<T> loadClass(String className) throws Exception {
		return (Class<T>) loader.loadClass(className);
	}

}

    这里定义一个URLClassLoader,把所有jar包的路径以及classpath路径初始化给它,并指定一个父类加载器,Thread.currentThread().getContextClassLoader()实际上是由web容器生成,根据双亲委托模型,可以防止类被重复加载,双亲委托模型将在后面的博客中说明。

    有了类加载器,我们可以把类装载进内存,然后实例化了。类的实例化都写在BeanContainerInitialization中,这里有些代理的对象,后面AOP时会说到。

public void init() throws Exception {
		for (String classPath : ScanPackageInitialization.classPaths) {
			Class<?> clazz = ClassLoaderUtil.loadClass(classPath);
			if (clazz.isAnnotationPresent(TeaDao.class)) {
				Object bean = Enhancer.create(clazz, new MethodInterceptor() {
					public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
							throws Throwable {
						BeanProxy beanProxy = new BeanProxy(obj, args, method, proxy).setInvokeSuper(false)
								.setInterfaceExecutor(OrmProxy.getInstance());
						addProxy(beanProxy, clazz);// 添加代理类
						beanProxy.invoke(beanProxy);
						return beanProxy.getResult();
					}
				});
				putBean(clazz, bean);
				if (!"".equals(((TeaDao) clazz.getAnnotation(TeaDao.class)).value())) {
					BeanContainer.getInstance().putBean(((TeaDao) clazz.getAnnotation(TeaDao.class)).value(), bean);
				} else {
					BeanContainer.getInstance().putBean(generateBeanName(clazz), bean);
				}
			} else if (clazz.isAnnotationPresent(Component.class)) {
				Object bean = null;
				if (isNeedProxy(clazz)) {
					bean = Enhancer.create(clazz, new MethodInterceptor() {
						public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
								throws Throwable {
							BeanProxy beanProxy = new BeanProxy(obj, args, method, proxy).setInvokeSuper(true);
							addProxy(beanProxy, clazz);// 添加代理类
							beanProxy.invoke(beanProxy);
							return beanProxy.getResult();
						}
					});
				} else {
					bean = clazz.newInstance();
				}
				putBean(clazz, bean);
				String componentValue = ((Component) clazz.getAnnotation(Component.class)).value();
				if (!"".equals(componentValue)) {
					BeanContainer.getInstance().putBean(componentValue, bean);
				} else {
					BeanContainer.getInstance().putBean(generateBeanName(clazz), bean);
				}
				Namespace namespace = clazz.getAnnotation(Namespace.class);
				if (namespace != null) {
					NamespaceBeanMapping.putController(namespace.value(), bean);
					Method[] methods = bean.getClass().getMethods();
					for (Method method : methods) {
						if (!method.getDeclaringClass().equals(java.lang.Object.class)) {
							NamespaceBeanMapping.putControllerMethod(namespace.value(), method);
						}

					}
				}
			}
		}
		BeanContainer.getInstance().inject();
	}

    需要被代理的对象,我们对其生成代理对象,对于不需要代理的对象,我们通过反射newInstance生成对象,生成之后有个putBean(clazz, bean)方法,该方法把对象塞进bean容器中。

private void putBean(Class<?> clazz, Object bean) {
		BeanContainer.getInstance().putBean(clazz, bean);
		Set<Class<?>> set = new HashSet<Class<?>>();
		findAllSuperClass(clazz, set);
		for (Class<?> superClass : set) {
			BeanContainer.getInstance().putBean(superClass, bean);
		}
	}

      putBean有两个映射操作,一个是映射成自身class与实例,另一个映射成父类class与实例,这样就可以实现ByType注入了。

    最后,所有的bean放进Map完成,就可以逐个注入属性了,于是调用一下BeanContainer.getInstance().inject()方法就大功告成了。

     项目地址:https://git.oschina.net/lxkm/teaframework
     博客:https://blog.csdn.net/dong_lxkm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值