实现Spring中的IOC和DI其实也不是很难,关键是要找到地方下手,我们可以先仿照Spring中的注解声明
1.IOC操作
Component:用于实现ioc控制反转,属性你可以自己定义 但是本次实现没有使用
Service:专门用于controller注解
2.DI
Autowired:利用该注解实现将对象注入到属性 ,也就是自动装配
Bean:将对象注入到方法
核心是ApplicationContext:里面将所有的托管bean都存到一个Map中,实现包扫描,和注入都在这个类中实现
提供一个genBean()方法获取已经托管好的Bean
包结构
然后从Spring中的入口类 appConfig类开始 我们发现中间最主要的是 ApplicationContext类,我们可以这么想 ,假设将我要扫描包的所有路径传入到这个类中,然后循环所有的这个包里面的内容 将带有java后缀名的类利用反射进行实例话,然后筛选带有Component和Service的注解的的类实例话将其放到一个Map中,然后提供一个getBean()方法根据类名来获取这个类的实例 具体这个类的实现代码如下 注释写的比较详细
public class ApplicationContext implements applicationContent{
private Class<?> clazz;//传入appconfig的class反射 因为我要将其看作是一个配置文件读取要扫描哪一个包
//存储bean
Map<String, Object> map=new HashMap<String, Object>();
//根据bean来获取
@Override
public Object getBean(String beanname) {
// TODO Auto-generated method stub
return map.get(beanname.toLowerCase());
}
//扫描注解 将bean进行托管
public ApplicationContext(Class<?> clazz) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{
this.clazz=clazz;//这里即是sppConfig类的反射实例
Annotation [] a=clazz.getDeclaredAnnotations();//获取该反射实例上面的所有注解
//我这里是这样的 这个方法上面有很多注解,但是这里面有一个规则,就是必须要先声明Configure注释菜进行包扫描
int y=0;
for (int i = 0; i < a.length; i++) {
if(a[i] instanceof Configuration){
//说明这个类是配置类 菜有可能去扫描到ComponentScan类
y++;
//这里前面就是扫描到Configuration 前提就是要先有Configure
}else if(a[i] instanceof ComponentScan && y==1){
ComponentScan comp=clazz.getAnnotation(ComponentScan.class);//获取到注解的实例
String pkgName=comp.basePackages();//获取到注解的包路径
//这里要扫描这个报下面的所有路径 我这边写的路径是 com.ly 所以要变成 com/ly
String pkgDirName = pkgName.replace('.', File.separatorChar);
//得到绝对路径
String user=System.getProperty("user.dir")+"/src/main/java/"+pkgDirName;
//开始扫描所有的类
findAll(user);//开始扫描这个包里面所有的类
// System.out.println(list);
}
}
}
/**
* 采用递归 找出这个包下面的所有的内容
* @param basepath
* @return
* @throws MalformedURLException
* @throws ClassNotFoundException
* @throws InstantiationException
* @throws IllegalAccessException
*/
public ArrayList findAll(String basepath) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException{
//写终止条件
File file = new File(basepath);
//System.out.println(file);
if(file==null){
return list;
}
File[] fileList = file.listFiles();//将该目录下的所有文件放置在一个File类型的数组中
//循环一个列表下面所有的文件或者是目录 ,如果是目录就就再次递归
for (File f : fileList) {
if (f.isDirectory()) {//判断是否为文件夹
//递归
findAll(f.getAbsolutePath());
}else{
if(f.getName().endsWith(".java")){
//扫描到java类
//需要截取
String name=f.getName().substring(0,f.getName().lastIndexOf("."));
list.add(name);//这里file.getAbsoluteFile user/java/com/ly 我只需要com.ly
String new_name=file.getAbsoluteFile().toString().split("java/")[1].replace("/", ".");
Class cc=Class.forName(new_name+"."+name);//获取该类的反射实例
//===============实现类上面的注解 进行IOC托管=============================
Annotation [] a=cc.getAnnotations();
for (Annotation annotation : a) {
//我这里没有将 Component 和Service注解进行分开处理 只要是出现该类注解就直接将其托管
if(annotation instanceof Component
|| annotation instanceof Sercice){
//说明要进行装载 先判断是不是有这个对象
// Component comp=clazz.getAnnotation(Component.class);//获取到注解
//有这个注解 所以我就要将类托管起来 注意这里的键 可以随你自己去指定 我这里是使用这个类的类名的小写做键
map.put(name.toLowerCase(), cc.newInstance());
}
}
//========================实现属性上面的注解解析=========================================
Method []me=cc.getDeclaredMethods();
//循环所有的方法
for (Method method : me) {
//获取这个方法上面的所有注解
Annotation [] ann=method.getAnnotations();
if (ann==null)
break;
for (Annotation annotation : ann) {
if(annotation instanceof Bean){
try {
//激活这个方法 将参数传进去
Object obj_1=method.invoke(cc, null);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//=======================对autowired注解的解析 实现DI的注入==================================
Field[] fields=cc.getDeclaredFields();
for (Field field : fields) {
Annotation[] allFAnnos=field.getDeclaredAnnotations();
for (Annotation annotation : allFAnnos) {
// System.out.println(annotation);
//循环所有的注解 如果这个类的字段上面
if(annotation instanceof Autowired){
System.out.println("看这里"+field.getName());
Object obj=null;//实例话对象
//实例化类 先获取类路径
String[] path=field.toGenericString().split(" ");
//加载类
Class biz_class=Class.forName(path[1]);
if(biz_class.isInterface()){
//看是不是一个接口 获取所有的接口
Class<?> [] intClass=biz_class.getInterfaces();
//循环所有的接口
for (Class<?> class1 : intClass) {
//假设只有一个接口
obj=class1.newInstance();//实例话这个接口的实现类
}
}else{
//实例化
obj=biz_class.newInstance();//实例化
//需要将这个值复制
// 取消语言访问检查
field.setAccessible(true);
//对象是cc obj就是实例话对象
//System.out.println(field.getName().substring(field.getName().lastIndexOf("."),0).toLowerCase() );
field.set(map.get( field.getName() ), obj);
}
}
}
}
}
}
}
return list;
}
}
appconfig 入口类
其中用到的注解需要自己去定义 本段代码实现了 对Component和Server的托管,还实现了 Bean和Autowire上午注入,采用递归的方式去扫描包下面的每一个文件,这里需要一个注意点就是当进行autowired的时候因为涉及到给私有变量去赋值所以要加一下 field.setAccessible(true); 取消访问检查 ,还有注意getDeclaredFields();和getFields()的区别 ,getDeclaredFields一个是获取自己声明的属性,当你使用getFields去获取所有的属性的时候你会发现获取的是空,总是会报空指针异常