自动动手实现一个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

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

查看所有标签

猜你喜欢:

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

HTML5

HTML5

Matthew David / Focal Press / 2010-07-29 / USD 39.95

Implement the powerful new multimedia and interactive capabilities offered by HTML5, including style control tools, illustration tools, video, audio, and rich media solutions. Understand how HTML5 is ......一起来看看 《HTML5》 这本书的介绍吧!

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

在线压缩/解压 HTML 代码

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码