目录:

  • 自定义注解bean
  • bean工厂
  • 依赖注入&循环依赖

在上一节,自定义注解实现了接口的配置调用,但是我们没有使用到spring的依赖注入及统一bean管理;那么本节我们将来实现这一块的功能

1. 自定义注解@Bean

package com.mp.framework.beans;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyBean {
}

在类上表名有@MyBean注解的,代表我们要把这个bean交由spring来统一管理

2. 自定义注入: @MyAutowired

package com.mp.framework.beans;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowired {
}

在字段上有@MyAutowired注解的,表示我们要做依赖注入,这里要解决的就是循环依赖的问题

3. 核心代码,实现bean的统一管理 MyBeanFactory

package com.mp.framework.beans;

import com.mp.demo.controller.TestController;
import com.mp.framework.annotation.MyController;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * ClassName: MyBeanFactory
 * Function:  核心代码,将bean通过map的形式进行保存
 * Date:      2020-05-07 15:45
 * author     mp
 * version    V1.0
 */
public class MyBeanFactory {
    // bean集合
    public static Map<Class<?>,Object> beanMaps = new ConcurrentHashMap<>();

    // 初始化
    public static void init(List<Class<?>> list) {
        list.stream().filter(cls -> cls.isAnnotationPresent(MyBean.class) || cls.isAnnotationPresent(MyController.class))  // 只处理 @MyBean 和 @Contrller注解的类
            .forEach(createBean);

        System.out.println("初始化bean: "+beanMaps);

    }

    // 创建bean
    static Consumer<Class<?>> createBean = cls -> {
        // 从bean集合中检查,bean是否已经创建
        Object bean = beanMaps.get(cls);
        // 获取字段类型,用于判断循环依赖
        Field[] fields = cls.getDeclaredFields();

        if (bean == null) { // 没有创建
            try {
                bean = cls.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            beanMaps.put(cls,bean);   // 容器管理bean
        }

        // 检查循环依赖
        MyBeanFactory.fieldsBeanTrans.accept(bean,fields);
    };

    // 检查循环依赖
    static BiConsumer<Object,Field[]> fieldsBeanTrans = (bean,fields) -> {
        Stream.of(fields).filter(field -> field.isAnnotationPresent(MyAutowired.class))
                .forEach(field -> {
                    Class<?> type = field.getType();
                    // 检查依赖的bean是否已经创建
                    Object obj = beanMaps.get(type);
                    if (obj == null){
                        // 创建bean
                        try {
                            obj = type.newInstance();
                            beanMaps.put(type,obj);
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                    field.setAccessible(true);
                    try {
                        field.set(bean,obj); // 字段赋值,即为bean设置依赖域,不然autowire 自动注入为null
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
    };

    // 获取bean
    static public Object getBean(Class<?> cls){
        return beanMaps.get(cls);
    }
}

说明:

(1)依旧利用获取类上注解信息来判断是否需要初始化bean,并统一管理;这里我们只处理@MyBean 和 @MyController两个注解

(2)bean的统一管理,实质是放在一个Map集合中,在初始化bean之前,需要到集合中查询一遍,已确保bean只初始化一次

(3)关于循环依赖,在获取到bean后,通过反射机制,获取到所有的字段,再获取到字段Type,并检查bean集合中是否已经创建该依赖bean;

(4)针对循环依赖,需要设置bean的依赖域,即 field.set(bean,obj),如果不手动设置的话,最后自动引入的话对象字段就为null

4. 接口映射类需要调整MappingHandler,  之前是手动初始化对象,现在改由统一从bean集合中取

package com.mp.framework.handler;

import com.mp.framework.beans.MyBeanFactory;
import org.apache.naming.factory.BeanFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * ClassName: MappingHandler
 * Function:  TODO
 * Date:      2020-05-07 14:12
 * author     mp
 * version    V1.0
 */
public class MappingHandler {
    private String uri;
    private Method method;
    private Class<?> cls;
    private String[] args;

    public MappingHandler(String uri, Method method, Class<?> cls, String[] args) {
        this.uri = uri;
        this.method = method;
        this.cls = cls;
        this.args = args;
    }

    // 执行接口方法
    public boolean handle(ServletRequest req, ServletResponse res) throws IllegalAccessException, InstantiationException, InvocationTargetException, IOException {
        String reqUri = ((HttpServletRequest) req).getRequestURI();
        if (!this.uri.equals(reqUri)) return false;

        Object[] params = new Object[args.length];
        // 获取参数
        for (int i=0;i< args.length;i++){
            params[i] = req.getParameter(args[i]);  // 获取请求参数值
        }

//        Object obj = this.cls.newInstance();
        Object obj = MyBeanFactory.beanMaps.get(this.cls); //统一bean管理

        // 执行方法,获取返回值
        Object response = this.method.invoke(obj, params);
        res.getWriter().write(Optional.ofNullable(response).map(r -> r.toString()).orElse(""));  // 针对无返回值类型的接口,需要做null处理
        return true;

    }
}

5.调整MyApplication启动类

package com.mp.framework.starter;

import com.mp.framework.beans.MyBeanFactory;
import com.mp.framework.core.ClassScanner;
import com.mp.framework.handler.HandlerManager;
import com.mp.framework.web.server.TomcatServer;
import com.mp.framework.web.server.TomcatServerNew;
import org.apache.catalina.LifecycleException;

import java.io.IOException;
import java.util.List;

/**
 * ClassName: MyApplication
 * Function:  TODO
 * Date:      2020-05-07 09:44
 * author     mp
 * version    V1.0
 */
public class MyApplication {
    public static void run(Class<?> cls,String[] args) throws LifecycleException, IOException, ClassNotFoundException {
        System.out.println("hello my0-spring application!!!");

        // 实例化Tomca t服务
//        TomcatServer tomcatServer = new TomcatServer(args);
        TomcatServerNew tomcatServer = new TomcatServerNew(args);
        tomcatServer.startServer();

        // 添加获取类列表和反射调用
        List<Class<?>> list = new ClassScanner().doScanner(cls.getPackage().getName());
        list.stream().forEach(c -> System.out.println(c.getName()));
        // 筛选controller 接口,处理接口映射
        HandlerManager.resolveMappingHandler(list);
        MyBeanFactory.init(list);  // 初始化bean工厂

    }
}


MyBeanFactory.init(list); 启动bean统一初始化工作.


6. 重启服务,访问接口 http://localhost:9999/v1/test?name=mp  

返回结果: service01: mp

 

至此: 我们自己写的spring项目已经初具功能,但是还有很大的不足,很多地方考虑不周全,后面我将去读springboot的源码,学习大佬的经验...