模拟spring的注解,制作自己的小容器

 去年的时候闲着没事翻看spring的源代码,了解了spring core的一些运行机制,但对于注解方面的东西还是似懂非懂。今天心血来潮的想按照自己的想法模拟一下spring容器的功能,虽然只是完成了微不足道的一点功能,但相信对抱着学习注解或批斗精神来的朋友应该会有所帮助。

实现的功能有:加载类及对象,属性依赖注入;可以根据类型以及beanName取值。

首先,什么是注解?百度文档上说:Annotation(注解)是JDK5.0及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME)。

闲话不多说,开始贴自己的代码以及思路

package com.ml.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;

/**
 * 所有用此注解的JavaBean都会被添加到容器里
 * @author 马磊
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)//<span style="color: rgb(51, 51, 51); font-family: arial, 宋体, sans-serif; font-size: 14px; line-height: 24px; text-indent: 28px;">此类型注解会保留在class文件中,JVM会读取它</span>
@Target({TYPE})//注解作用在类上
public @interface Service {
	
	public String name() default "";
}
package com.ml.annotation;

import static java.lang.annotation.ElementType.*;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 作用在属性和方法上 用于完成依赖注入
 * @author 马磊
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD,METHOD})
public @interface Resource {

}
</pre><p>现在注解已经搞定了,接下来就是编写我们的注解探侦器。</p><p>我的思路是这样的:先确定编译后的项目目录,然后遍历目录下的子目录和.class文件,使用Class加载器加载他们,再通过JAVA反射进行处理 </p><p>第一步  确定目录 </p><p><pre name="code" class="java">	private URL getLocation() {
		try {
			return DefaultScanner.class.getClassLoader().getResources("//")
					.nextElement();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

第二歩 遍历目录和文件 

package com.ml.scanner;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import com.ml.annotation.Service;

/**
 * 注解探针器
 * @author 马磊
 *
 */
public class DefaultScanner implements Scanner {

	private String basePath = this.getLocation().getFile();
	private Map<String, Object> objectMap = new HashMap<String, Object>();
	private Map<Class<?>, Object> clazzMap = new HashMap<Class<?>, Object>();

	/**
	 * 开始侦测
	 */
	public Map<String, Map<?, ?>> scanner() {
		Map<String, Map<?, ?>> map = new HashMap<String, Map<?, ?>>();
		File base = new File(this.basePath);
		basePath = base.getAbsolutePath();
		this.listFile(base);
		map.put("clazz", this.clazzMap);
		map.put("obj", this.objectMap);
		return map;
	}
	
	/**
	 * 侦测某个文件
	 * @param file
	 */
	private void scanner(File file) {
		String filePath = null;
		try {
			filePath = file.getCanonicalPath();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		//将文件路径变为我们的class类的全限定名
		filePath = filePath.replace(this.basePath + "\\", "").replaceAll("\\\\", ".").replace(".class", "");
		try {
			//使用类加载器加载
			Class<?> clazz = DefaultScanner.class.getClassLoader().loadClass(
					filePath);
			//判断是不是已经放到容器中了
			if(clazzMap.containsKey(clazz))
				return;
			//利用反射判断是否有Service注解
			Service service = clazz.getAnnotation(Service.class);
			if (service == null)
				return;
			//注解的name()方法
			String beanName = service.name();
			if(beanName != null && objectMap.containsKey(beanName))
				return;
			
			Object obj = this.loadRequiredClass(clazz);
			if (beanName != null && !beanName.equals(""))
				objectMap.put(beanName, obj);
			else
				clazzMap.put(clazz, obj);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 实例化对象 并且进行属性依赖注入
	 * @param clazz
	 * @return
	 */
	private Object loadRequiredClass(Class<?> clazz) {
		Object obj = null;
		try {
			obj = clazz.newInstance();
			Field[] fields = clazz.getDeclaredFields();
			for (Field field : fields) {
				Resource resource = field.getAnnotation(Resource.class);
				if (resource == null)
					continue;
				String requiredName = resource.name();
				Class<?> fieldClass = field.getType();
				if(fieldClass.getAnnotation(Service.class) == null)
					throw new RuntimeException("the required class did not have Service Annotation");
				Object fieldValue;
				if (requiredName == null && clazzMap.containsKey(fieldClass)) {
					fieldValue = clazzMap.get(fieldClass);
				} else if(objectMap.containsKey(requiredName)){
					fieldValue = objectMap.get(requiredName);
				}else{
					fieldValue = this.loadRequiredClass(fieldClass);
					clazzMap.put(fieldClass, fieldValue);
				}
				field.setAccessible(true);
				field.set(obj, fieldValue);
			}
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		}
		return obj;
	}
	
	/**
	 * 遍历文件夹或者进行单一文件的侦测加载
	 * @param file
	 */
	private void listFile(File file) {
		if (file.isDirectory()) {
			File[] files = file.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".class") || dir.isDirectory();
				}
			});
			if (files != null) {
				for (File temp : files) {
					this.listFile(temp);
				}
			}
		} else {
			this.scanner(file);
		}
	}
	
}

这样就完成了注解方面的功能 


使用一个类来完成容器的功能

package com.ml.context;

import java.util.HashMap;
import java.util.Map;

import com.ml.scanner.DefaultScanner;
import com.ml.scanner.Scanner;

/**
 * 容器
 * @author 马磊
 *
 */
public class AnnotationApplicationContext extends RefreshableApplicationContext{
	
	private Map<Class<?>,Object> clazzContainer;//存放类 -对象
	private Map<String,Object> namedContainer ;//存放bean名-对象
	private Scanner scanner = new DefaultScanner();//默认的注解探针器
	private static ApplicationContext applicationContext;//
	
	/**
	 * 单例
	 * @return
	 */
	public static ApplicationContext configure(){
		if(applicationContext == null)
			applicationContext = new AnnotationApplicationContext().refresh();
		return applicationContext;
	}
	
	
	/**
	 * 根据类来取得对象
	 */
	@Override
	public <T> T get(Class<T> clazz) {
		if(clazz == null)
			throw new IllegalArgumentException("clazz can not be null");
		return clazz.cast(this.clazzContainer.get(clazz));
	}
	
	/**
	 * 根据bean名取得对象
	 */
	@Override
	public Object get(String beanName) {
		if(beanName == null)
			throw new IllegalArgumentException("clazz can not be null");
		return this.namedContainer.get(beanName);
	}
	
	/**
	 * 
	 */
	@Override
	public <T> T get(Class<T> clazz, String beanName) {
		return clazz.cast(this.get(beanName));
	}
	
	/**
	 * 刷新容器
	 */
	@SuppressWarnings("unchecked")
	@Override
	public ApplicationContext refresh() {
		clazzContainer = new HashMap<Class<?>, Object>();
		namedContainer = new HashMap<String, Object>();
		Map<String,Map<?,?>> map = scanner.scanner();
		this.clazzContainer = (Map<Class<?>, Object>) map.get("clazz");
		this.namedContainer = (Map<String, Object>) map.get("obj");
		return this;
	}
}


表达能力太差,还请诸位见谅。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值