自动动手实现一个IOC-simpleioc

栏目: Java · 发布时间: 5年前

内容简介:我们都知道 Spring 的两大核心是IOC 和 AOP。也有人认为是IOC、DI和AOP。这几项应该是所有的java后台都应该掌握的。所以为了加深对这几个概念的理解,本人根据自己的理解动手实现了一个简单的IOC。IOC:(Inversion of Control) 控制反转,是一种设计思想。包含了两方面:一、控制,二、反转。

我们都知道 Spring 的两大核心是IOC 和 AOP。也有人认为是IOC、DI和AOP。这几项应该是所有的 java 后台都应该掌握的。所以为了加深对这几个概念的理解,本人根据自己的理解动手实现了一个简单的IOC。

github地址

1.概念理解

IOC:(Inversion of Control) 控制反转,是一种设计思想。包含了两方面:一、控制,二、反转。 控制 指的是当前对象对内部成员的控制权。 反转 指的是这种控制权不由当前对象管理了,由其他类、第三方容器来管理。

DI:(Dependency Injection) 依赖注入,属性或组件之间的关系由容器去记录,由容器动态的去将某个依赖关系注入到某个组件中。

2. IOC原理

其实IOC容器就是一个大工厂,用以管理所有的对象以及依赖关系。原理就是通过Java的反射技术来实现的。通过反射可以获得类的所有信息(成员变量、类名等等)。下面是一个IOC的流程图:

自动动手实现一个IOC-simpleioc

3. 代码实现

在我的实现里,我通过模仿spring的注解的方式来描述类的关系以及bean的注入。通过maven来管理jar包。需要说明的是我通过reflections来帮助获得被自定义注解注释的类maven依赖如下

<dependency>
        <groupId>org.reflections</groupId>
        <artifactId>reflections</artifactId>
        <version>0.9.11</version>
    </dependency>
复制代码

项目最终目录结构

自动动手实现一个IOC-simpleioc

3.1 注解类

在代码中,有几个注解类,SimpleAutowire、SimpleBean、SimpleCompotent以及SimpleConfigure。他们是按照Spring的Bean、Autowire、Compotent、Configure的简单实现。目前SimpleIOC 只支持单例bean。可以根据自定义的beanName进行bean的生成以及注入,亦可以根据默认的类的全限定名进行bean的实例化以及注入。         以上几个注解类的代码如下:

package com.lichaobao.simpleioc.annotations;

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

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 标记需要注入的属性
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SimpleAutowire {
    String value() default "";
}
复制代码
package com.lichaobao.simpleioc.annotations;

import java.lang.annotation.*;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 标记需要生成的单例bean
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface SimpleBean {
    String name() default "";
}

复制代码
package com.lichaobao.simpleioc.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 标记需要生成的bean
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleCompotent {
    String value() default "";
}

复制代码
package com.lichaobao.simpleioc.annotations;

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

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 标记配置类 用于生成bean
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface SimpleConfigure {
}

复制代码

我们可以看到,注解类实现都大同小异,其实注解类的主要作用就是为了标记,帮助我们来区分具体类的具体作用。具体我们要这些被注解的类去干什么,需要我们用代码去具体实现。

3.2 BeanDefinition

在贴出如何实现bean的初始化以及管理功能之前,我们先来说一下BeanDefinition这个类。

package com.lichaobao.simpleioc.factory;

import java.util.HashMap;
import java.util.Map;
/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 存放bean的数据结构
 */
public class BeanDefinition {
   private Object bean;
   private String className;
   private Class beanClass;
   private Map<String,Object> propertyMap = new HashMap<String, Object>();
    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
        try{
            this.beanClass = Class.forName(className);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public Class getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(Class beanClass) {
        this.beanClass = beanClass;
    }

    public Map<String, Object> getPropertyMap() {
        return propertyMap;
    }

    public void setPropertyMap(Map<String, Object> propertyMap) {
        this.propertyMap = propertyMap;
    }
}

复制代码

我们用BeanDefinition来描述或者说记录一个bean的信息,包括这个bean的实例化对象、这个bean的名称、这个bean所依赖的属性以及这个bean的class类。在spring 源码中,同样也是根据BeanDefinition这个数据结构来记录bean的信息。不过spring中的实现更为复杂。我们这里只是简单的对其进行一个实现。

3.2 根据SimpleConfigure与SimpleBean完成类似spring 配置类的功能

说完存储的数据结构,我们来说说如何去扫描被上面注解所标注的类,以及进行bean的初始化。前面提到,我们通过org.reflections这个包来完成自定义注解的扫描。使用方式很简单,我们只需要传入要扫描的包名就行了如下:

Reflections reflections = new Reflections(packageName);
Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleConfigure.class);
复制代码

现在扫描完配置类我们只需要将配置类里的被SimpleBean注释的方法拿出来,然后执行这些方法就能实现类似于spring的配置类的功能。 BeanFactory代码如下:

package com.lichaobao.simpleioc.factory;

import com.lichaobao.simpleioc.annotations.SimpleBean;
import com.lichaobao.simpleioc.annotations.SimpleConfigure;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 */
public class BeanFactory  {
    /**
     * 构造函数私有化  防止被调用
     */
    private BeanFactory(){
    }

    /**
     * 单例模式  内部静态类
     */
    private static class BeanFactorySingle{
        private static BeanFactory beanFactory = new BeanFactory();
    }
    /**
     * 存放bean实例
     */
    private static Map<String,BeanDefinition> beanMap = new HashMap<String, BeanDefinition>();
    /**
     * 扫描被SimpleConfigure 标记的类 找到其中被SimpleBean标记的方法,进行bean的初始化以及保存
     */
    public void init(String packageName) throws Exception {
        if(beanMap.size()==0){
            Reflections reflections = new Reflections(packageName);
            Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleConfigure.class);
            for (Class clazz:classesList){
                Method[] methods = clazz.getDeclaredMethods();
                for(Method method:methods){
                    if(method.isAnnotationPresent(SimpleBean.class)){
                        Type type = method.getGenericReturnType();
                        String className = type.toString().split(" ")[1];
                        SimpleBean simpleBean = method.getAnnotation(SimpleBean.class);
                        Object o = clazz.newInstance();
                        Object bean =  method.invoke(o);
                        String beanName = "".equals(simpleBean.name())? className:simpleBean.name();
                        registry(beanName,className,bean,null);
                    }
                }
            }
        }
    }

    /**
     * 获得 bean
     * @param beanName bean名称(bean的自定义名称(如果添加的话)默认为类的全限定名)
     * @return Object
     * @throws Exception Not found
     */
    public Object getBean(String beanName)throws Exception{
        BeanDefinition beanDefinition = beanMap.get(beanName);
        if(beanDefinition == null){
            throw new Exception("No beans found name "+ beanName);
        }
        Object bean = beanDefinition.getBean();
        if(bean == null){
            bean = doCreate(beanDefinition);
        }
        return bean;
    }

    /**
     * 如果bean是空  则根据 beanDefinition中记录的bean结构 进行bean的初始化
     * @param beanDefinition 存放bean结构
     * @return Object
     * @throws Exception  InstantiationException, IllegalAccessException
     */
    private Object doCreate(BeanDefinition beanDefinition) throws Exception{
        Object bean = beanDefinition.getBeanClass().newInstance();
        bean =  addProperty(bean,beanDefinition);
        return bean;
    }

    /**
     * 根据beanDefinition 中的记录 将属性的值赋给要生成的bean
     * @param bean  bean
     * @param beanDefinition 存放bean数据结构
     * @return object
     * @throws Exception NoSuchFieldException, SecurityException
     */
    private Object addProperty(Object bean,BeanDefinition beanDefinition)throws Exception{
        Map<String,Object> map = beanDefinition.getPropertyMap();
        Field[] fields = beanDefinition.getBeanClass().getDeclaredFields();
        for(Field field : fields){
            Field declaredField = bean.getClass().getDeclaredField(field.getName());
            declaredField.setAccessible(true);
            declaredField.set(bean,map.get(field.getName()));
        }
        return bean;
    }
    void registry(String beanName,String className,Object bean,Map<String,Object> map) throws Exception {
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setClassName(className);
        beanDefinition.setBean(bean);
        beanDefinition.setPropertyMap(map);
        if(beanMap.containsKey(beanName)){
            throw new Exception("重复注册bean");
        }
        beanMap.put(beanName,beanDefinition);
    }

    public static BeanFactory getInstance(){
        return BeanFactorySingle.beanFactory;
    }
}

复制代码

3.3 依赖注入功能实现

上面的类说明了SimpleBean与SimpleConfigure的功能即完成配置类的功能,那DI我们是怎么实现的呢,接下来我们就要说说AutowireFactory这个类。在AutowireFactory中我们通过扫描所有被注解SimpleCompotent的类,根据上一步BeanFactory中实例化的bean注入到此类中的被SimpleAutowire注释的属性。完成被SimpleCompotent注释的类的bean的实例化。具体实现如下,同样也是用到反射的知识。

package com.lichaobao.simpleioc.factory;

import com.lichaobao.simpleioc.annotations.SimpleAutowire;
import com.lichaobao.simpleioc.annotations.SimpleCompotent;
import org.reflections.Reflections;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 * 扫描所有被SimpleCompotent标记的类
 * 根据其内部属性被SimpleAutowire标记的属性
 * 从BeanFactory中找到相应的bean进行注入并进行
 * bean的初始化。
 */
public class AutowireFactory {
    /**
     * 扫描指定包名下的类 根据SimpleCompotent 以及SimpleAutowire进行注入
     * @param packageName 包名
     * @throws Exception Exception
     */
    public void init(String packageName) throws Exception {
        Reflections reflections = new Reflections(packageName);
        Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(SimpleCompotent.class);
        for(Class clazz:classesList){
            Object bean = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            Map<String,Object> fieldMap = new HashMap<String, Object>();
            for(Field field :fields){
                field.setAccessible(true);
                if(field.isAnnotationPresent(SimpleAutowire.class)){
                    SimpleAutowire simpleAutowire = field.getAnnotation(SimpleAutowire.class);
                    Type type = field.getGenericType();
                    String beanName = "".equals(simpleAutowire.value()) ?
                            type.toString().split(" ")[1]:simpleAutowire.value();
                    Object fieldBean = BeanFactory.getInstance().getBean(beanName);
                    field.set(bean,fieldBean);
                    fieldMap.put(field.getName(),fieldBean);
                }else{
                    fieldMap.put(field.getName(),field.get(bean));
                }
                SimpleCompotent simpleCompotent = (SimpleCompotent)clazz.getAnnotation(SimpleCompotent.class);
                String beanName = "".equals(simpleCompotent.value()) ? clazz.getName():simpleCompotent.value();
                BeanFactory.getInstance().registry(beanName,clazz.getName(),bean,fieldMap);
            }
        }
    }
}

复制代码

4. 测试

  1. 首先我们新建一个类Test,代码如下:
package com.lichaobao.test;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 */
public class Test {
    private String a;

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public Test(String a) {
        this.a = a;
    }
    public void doSomething(){
        System.out.println(a);
    }
}

复制代码
  1. 新建一个配置类,用上SimpleConfigure以及SimpleBean完成对Test类的实例化
package com.lichaobao.test;

import com.lichaobao.simpleioc.annotations.SimpleBean;
import com.lichaobao.simpleioc.annotations.SimpleConfigure;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 */
@SimpleConfigure
public class Configure {
    @SimpleBean
    public Test test(){
        return new Test("我是配置类里生成的test");
    }
}

复制代码
  1. 新建一个类TestA 用上SimpleCompotent与SimpleAutowire来完成属性的依赖注入以及TestA类的实例化
package com.lichaobao.test;

import com.lichaobao.simpleioc.annotations.SimpleAutowire;
import com.lichaobao.simpleioc.annotations.SimpleCompotent;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 */
@SimpleCompotent
public class TestA {
    @SimpleAutowire
    private Test test;
    void watch(){
        System.out.println("======================");
        System.out.println("我看看test属性被注入了没");
        test.doSomething();
        System.out.println("上面打印了,属实注入了呀");
    }
}

复制代码
  1. 测试类如下:
package com.lichaobao.test;

import com.lichaobao.simpleioc.factory.AutowireFactory;
import com.lichaobao.simpleioc.factory.BeanFactory;

/**
 * @author lichaobao
 * @date 2019/4/18
 * @QQ 1527563274
 */
public class TestMain {
    public static void main(String[] args)throws Exception{
        AutowireFactory autowireFactory = new AutowireFactory();
        BeanFactory.getInstance().init("com.lichaobao");
        autowireFactory.init("com.lichaobao");
        Test test = (Test) BeanFactory.getInstance().getBean("com.lichaobao.test.Test");
        TestA testA = (TestA) BeanFactory.getInstance().getBean("com.lichaobao.test.TestA");
        test.doSomething();
        testA.watch();
    }
}

复制代码

运行后打印

自动动手实现一个IOC-simpleioc

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

科技之巅

科技之巅

麻省理工科技评论 / 人民邮电出版社 / 2016-10-1 / CNY 98.00

《麻省理工科技评论》从2001年开始,每年都会公布“10大突破技术”,即TR10(Technology Review 10),并预测其大规模商业化的潜力,以及对人类生活和社会的重大影响。 这些技术代表了当前世界科技的发展前沿和未来发展方向,集中反映了近年来世界科技发展的新特点和新趋势,将引领面向未来的研究方向。其中许多技术已经走向市场,主导着产业技术的发展,极大地推动了经济社会发展和科技创新......一起来看看 《科技之巅》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具