博客
关于我
手写框架,模拟简易的SpringIOC
阅读量:324 次
发布时间:2019-03-04

本文共 13879 字,大约阅读时间需要 46 分钟。

要实现模拟IOC,要使用过spring,明白IOC的执行流程,会使用反射等

1. 通过基于注解的IOC的执行流程整理实现思路

在这里插入图片描述

来自楠哥视频里的图

实现步骤

根据该图,我们可以将步骤分为下列几步:

① 扫描包,获取该包下所有加了Component注解的类

② 通过反射机制获取该类的Class,即是图中的原材料的组件

③ 将获取的类的Class和Component注解中的value值封装成为一个beanDefinitions对象

④ 创建一个ioc容器,以存储bean(即是beanName,和对应的实例对象)

2. 实现

(1) 创建层级目录及文件

在这里插入图片描述

说明:MyAnnotationConfigApplication 为实现springIOC中的AnnotationConfigApplication

MyBeanDefinition为实现springIOC中的BeanDefinition

MyTools为扫描包的工具类

(2) 自定义要实现的IOC注解

在这里插入图片描述

① MyComponent 注解

//自定义一个MyComponent注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface MyComponent {       String value() default "";}

② MyValue 注解

//自定义Value注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface MyValue {       String value() default "";}

③ MyAutowired 注解

//自定义autowired注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface MyAutowired {   }

④ MyQualifier 注解

//自定义Qualifier注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface MyQualifier {       String value() default "";}

(3) MyBeanDenifition 类

@Data@AllArgsConstructor@NoArgsConstructorpublic class MyBeanDefinition {       private String beanName;    private Class beanClass;}

使用lombok,MyBeanDefinition为模拟IOC的BeanDeFinition类

成员属性说明:
beanName 为@MyComponent注解中的value值,即是bean名,如果value为空,则使用首位变小写后的类名为beanName
beanClass 为要提供bean(实例对象)的Class(原材料)

(4) MyTools 工具类

实现扫描包的功能,返回一个set集合 包含该包下所有的类

public static Set
> getClasses(String pack) { // 第一个class类的集合 Set
> classes = new LinkedHashSet
>(); // 是否循环迭代 boolean recursive = true; // 获取包的名字 并进行替换 String packageName = pack; String packageDirName = packageName.replace('.', '/'); // 定义一个枚举的集合 并进行循环来处理这个目录下的things Enumeration
dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); // 循环迭代下去 while (dirs.hasMoreElements()) { // 获取下一个元素 URL url = dirs.nextElement(); // 得到协议的名称 String protocol = url.getProtocol(); // 如果是以文件的形式保存在服务器上 if ("file".equals(protocol)) { // 获取包的物理路径 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); // 以文件的方式扫描整个包下的文件 并添加到集合中 findClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)) { // 如果是jar包文件 // 定义一个JarFile System.out.println("jar类型的扫描"); JarFile jar; try { // 获取jar jar = ((JarURLConnection) url.openConnection()).getJarFile(); // 从此jar包 得到一个枚举类 Enumeration
entries = jar.entries(); findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes); } catch (IOException e) { // log.error("在扫描用户定义视图时从jar包获取文件出错"); e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; }

(5) MyAnnotationConfigApplication 类 (重点)

该类的实现步骤:

① 创建一个成员变量HashMap模拟ioc容器

// 创建一个HashMap模拟ioc容器    private Map
ioc = new HashMap<>();

② 编写 findBeanDefinition 函数,传入包名,调用工具类,获取到所有添加了MyComponent注解的类后,将其所有类(Class原型)和beanName封装为MyBeanDefinition对象集合后返回

这一步,即实现了MyComponent注解的功能

具体解释见代码注释

public Set
findMyBeanDefinitions(String packageName){ // 1.获取包下的所有类 Set
> classes = com.ruoxi.MyIOC.MyTools.getClasses(packageName); //使用工具类,用set集合获取该包名下的所有类 // 使用HashSet存储BeanDefinition Set
beanDefinitions = new HashSet<>(); // 2.遍历这些类,找到添加了注解的类 Iterator
> iterator = classes.iterator(); while(iterator.hasNext()){ Class
clazz = iterator.next(); // 获取注解的对象 MyComponent componentAnnotation = clazz.getAnnotation(MyComponent.class); //如果componentAnnotation != null ,则添加了该注解 if(componentAnnotation!=null){ // 获取注解的值 String beanName = componentAnnotation.value(); // 如果没有写beanName(即注解的值) 那么将类名的首字母变小写后作为beanName if(beanName.equals("")) { String className = clazz.getSimpleName(); //获取类名 //将类名的第一个截取变小写,然后加上后面的字符串 beanName = className.substring(0,1).toLowerCase()+className.substring(1); } // 3. 将这些类封装为BeanDefinition 并存入beanDefinitions beanDefinitions.add(new MyBeanDefinition(beanName,clazz)); } } return beanDefinitions; }

③ 编写 createObjects 函数,传入上一步封装好的MyBeanDefinition对象集合,在该函数中遍历该集合,利用反射机制创建bean对象,并遍历类的各个属性,以实现MyValue注解的功能

这一步,即实现了MyValue注解的功能

具体解释见代码注释

public void createObject(Set
beanDefinitions){ Iterator
iterator = beanDefinitions.iterator(); //遍历beanDefinition集合 while (iterator.hasNext()) { MyBeanDefinition beanDefinition = iterator.next(); Class clazz = beanDefinition.getBeanClass(); // 获取beanName String beanName = beanDefinition.getBeanName(); try { // 利用反射机制创建bean对象 注意这里的是beanDefinition里的beanClass对象 Object object = clazz.getConstructor().newInstance(); //完成属性的赋值 Field[] fields = clazz.getDeclaredFields(); //遍历属性 判断是否添加了myValue注解 for (Field field : fields) { MyValue myValueAnnotation = field.getAnnotation(MyValue.class); if(myValueAnnotation != null){ //获取注解的值 String value = myValueAnnotation.value(); //通过截取属性名 拼接为方法名 String fieldName = field.getName(); String methodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1); //得到设置属性的方法 Method method = clazz.getMethod(methodName,field.getType()); //完成数据类型转换 Object val = null; //将value转化为对应的属性类型,并给属性赋值 switch (field.getType().getName()){ //这里只列举了部分类型 case "java.lang.Integer": val = Integer.parseInt(value); break; case "java.lang.String": val = value; break; case "java.lang.Float": val = Float.parseFloat(value); break; case "java.lang.Double": val= Double.parseDouble(value); break; } method.invoke(object,val); //执行方法,给属性赋值 } } // 将该对象和beanName存入ioc容器 ioc.put(beanName,object); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } }

④ 编写getBean函数,通过传入beanName名取得bean

// 通过beanName获取bean    public Object getBean(String beanName){           return ioc.get(beanName);    }

⑤ 编写 autowireObject 函数,还是传入BeanDefinition的Set集合,遍历Set中的Class中的属性,查询是否有添加MyAutowired或MyQualifier注解,如果有,则实现对应属性(内部类)注入bean的功能

这一步,即实现了MyAutowired和MyQualifier注解的功能

具体解释见代码注释

//实现autowire和MyQualifier注解的功能    public void autowireObject(Set
beanDefinitions){ Iterator
iterator = beanDefinitions.iterator(); while (iterator.hasNext()) { MyBeanDefinition beanDefinition = iterator.next(); //创建beanDefinition中的beanClass对象 Class clazz = beanDefinition.getBeanClass(); //遍历属性,查找是否有添加 MyAutowired或MyQualifier注解 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { MyAutowired myAutowired = field.getAnnotation(MyAutowired.class); if(myAutowired!=null){ MyQualifier myQualifier = field.getAnnotation(MyQualifier.class); //如果有MyQualifier注解,则为ByName方式 否则为ByType方式 if(myQualifier!=null){ try { //通过beanName获取bean对象 这里即是内部类的对象 String beanName = myQualifier.value(); Object bean = getBean(beanName); String fieldName = field.getName(); String methodName = "set"+ fieldName.substring(0,1).toUpperCase()+ fieldName.substring(1); Method method = clazz.getMethod(methodName,field.getType()); //取出对象(bean) //beanDefinition.getBeanName()即是当前的beanName Object object = getBean(beanDefinition.getBeanName()); //执行method 将bean注入到object中,这里即是将对象的bean赋值给内部类 method.invoke(object,bean); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } else { //当只有autowired注解时 通过byType方式获取bean Class fieldClass = field.getType(); // 通过反射获取指定的注解 MyComponent fieldClassAnnotation = (MyComponent) fieldClass.getDeclaredAnnotation(MyComponent.class); //如果属性的类也添加了MyComponent注解 则获取属性的Class的MyComponent注解的值 并注入属性的值 if(fieldClassAnnotation != null){ String filedBeanName = fieldClassAnnotation.value(); if(filedBeanName.equals("")){ //如果属性的Component注解没有赋值,则将首字母变小写后成为beanName String className = fieldClass.getSimpleName(); //获取属性类名 //将类名的第一个截取变小写,然后加上后面的字符串 filedBeanName = className.substring(0,1).toLowerCase()+className.substring(1); } // 获取该属性的bean Object fieldBean = getBean(filedBeanName); try { // 拼接字符串得到set属性的方法 String fieldName = fieldClass.getSimpleName(); String setFiledMethodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1); Method method = clazz.getMethod(setFiledMethodName,field.getType()); //取出当前beanDefinition的类对象bean //beanDefinition.getBeanName()即是当前的beanName Object object = getBean(beanDefinition.getBeanName()); method.invoke(object,fieldBean); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } } }

⑥ 编写MyAnnotationConfigAppliaction类的有参构造函数,并使用上述所有的函数来实现ioc的功能

public MyAnnotationConfigApplicationContext(String packageName){           //遍历包,找到目标类(原材料)        Set
beanDefinitions = findMyBeanDefinitions(packageName); //根据原材料创建bean createObject(beanDefinitions); //判断是否有MyAutowired或MyQualifier注解,如有则对应实现功能 autowireObject(beanDefinitions); }

至此,模拟SpringIOC的功能已大致完成,接下来是测试

3. 测试

(1) 创建俩个类并加上自定义注解

① TestIOC.class

@Data@AllArgsConstructor@NoArgsConstructor@MyComponentpublic class TestIOC {       @MyValue("123")    private Integer id;    @MyValue("小明")    private String name;    @MyAutowired//    @MyQualifier("zi")    private Zi zi;}

② Zi.class

@Data@NoArgsConstructor@AllArgsConstructor@MyComponentpublic class Zi {       @MyValue("123456")    private Integer Ziid;    @MyValue("打工人")    private String Ziname;}

(2) 在main方法中进行测试,打印输出getBean(“testIOC”)

① 不在TestIOC类中给内部类Zi属性添加MyQualifier注解,只单用MyAutowired注解

@MyAutowired//    @MyQualifier("zi")    private Zi zi;
public class Test {       public static void main(String[] args) {           MyAnnotationConfigApplicationContext myAnnotationConfigApplication = new MyAnnotationConfigApplicationContext("com.ruoxi.entity");        System.out.println(myAnnotationConfigApplication.getBean("testIOC"));    }}

在这里插入图片描述

通过打印结果可以看见,成功取出了名为"testIOC"的bean,且各个属性均已赋值,说明@MyComponent注解生效,@MyValue注解也已生效

同时内部类zi也被注入了值,说明Zi类的bean也注入,且TestIOC类的成员变量Zi被注入了这个bean值,说明@MyAutowired注解生效

② 在TestIOC类中给内部类Zi属性添加上MyQualifier注解

@MyAutowired    @MyQualifier("zi")    private Zi zi;

在这里插入图片描述

同样成功打印,说明MyQualifier注解也生效

至此,模拟SpringIOC已完成

转载地址:http://yoph.baihongyu.com/

你可能感兴趣的文章
go等待N个线程完成操作总结
查看>>
消息队列 RocketMQ 并发量十万级
查看>>
C# 生成编号(防并发)
查看>>
ReactJs入门教程-精华版
查看>>
乐观锁悲观锁应用
查看>>
Window环境下安装Redis 并 自启动Redis 及 Redis Desktop Manager
查看>>
简单说说TCP三次握手、四次挥手机制
查看>>
.net Core 使用IHttpClientFactory请求
查看>>
多线程之旅(准备阶段)
查看>>
Python 之网络式编程
查看>>
MySql5.5安装步骤及MySql_Front视图配置
查看>>
mybatis绑定错误-- Invalid bound statement (not found)
查看>>
springmvc Controller详解
查看>>
mybatis #{}和${}区别
查看>>
Java Objects工具类重点方法使用
查看>>
Java内存模型(JMM)
查看>>
AQS相关
查看>>
在IIS与SQL Server 2005中设置,让Reporting Services发布的web报表允许匿名访问
查看>>
abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一)
查看>>
abp(net core)+easyui+efcore实现仓储管理系统——多语言(十)
查看>>