去年的时候闲着没事翻看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;
}
}
表达能力太差,还请诸位见谅。