美文网首页
手写SpringMVC:Bean管理(基石)

手写SpringMVC:Bean管理(基石)

作者: 谁家的猪 | 来源:发表于2019-07-31 08:23 被阅读0次

Bean

  • 生命周期长
  • 在整个虚拟机内可见
  • 维护成本高,单例存在

优势

  • 运行期效率高
  • 统一维护,便于管理和扩展

Spring实现方式

  • 包扫描并自动装配(反射)
  • BeanFactory(统一管理)
  • 依赖注入

依赖注入/控制反转

  • IOC(Inversion Of Control):思想
  • DI(Dependency Injection):方式

普通控制方式


普通控制方式.png

控制反转方式


控制反转.png

如何实现依赖注入

  • 扫描包获得类定义(已达成)
  • 初始化Bean,并实现依赖注入
  • 解决Bean初始化顺序问题

依赖注入次序问题

依赖注入次序.png

实现自己的依赖注入

  1. 创建注解@Bean、@AutoWried
package com.istimeless.beans;

import java.lang.annotation.*;

/**
 * @author lijiayin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoWired {
}
package com.istimeless.beans;

import java.lang.annotation.*;

/**
 * @author lijiayin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Bean {
}
  1. 创建BeanFactory
package com.istimeless.beans;

import com.istimeless.web.mvc.Controller;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lijiayin
 */
public class BeanFactory {
    private static Map<Class<?>, Object> classToBean = new ConcurrentHashMap<>();
    
    public static Object getBean(Class<?> cls) {
        return classToBean.get(cls);
    }

    /**
     * 初始化Bean
     * @param classList
     * @throws Exception
     */
    public static void initBean(List<Class<?>> classList) throws Exception {
        List<Class<?>> toCreate = new ArrayList<>(classList);
        while (toCreate.size() != 0){
            int remainSize = toCreate.size();
            for (int i = 0; i < toCreate.size(); i++) {
                if(finishCreate(toCreate.get(i))){
                    toCreate.remove(i);
                }
            }
            //没有任何对象被创建,循环引用
            if(toCreate.size() == remainSize){
                throw new Exception("cycle dependency!");
            }
        }
        
    }

    private static boolean finishCreate(Class<?> cls) throws IllegalAccessException, InstantiationException {
        if(!cls.isAnnotationPresent(Bean.class) 
                && !cls.isAnnotationPresent(Controller.class)){
            return true;
        }
        Object bean = cls.newInstance();
        for (Field field : cls.getDeclaredFields()){
            if(field.isAnnotationPresent(AutoWired.class)){
                Class<?> fieldType = field.getType();
                Object reliantBean = BeanFactory.getBean(fieldType);
                //对象还没创建,等待下一次循环创建
                if(reliantBean == null){
                    return false;
                }
                field.setAccessible(true);
                field.set(bean, reliantBean);
            }
        }
        classToBean.put(cls, bean);
        return true;
    }
}
  1. 修改启动类,项目启动时加载类
package com.istimeless.starter;

import com.istimeless.beans.BeanFactory;
import com.istimeless.core.ClassScanner;
import com.istimeless.web.handler.HandlerManager;
import com.istimeless.web.server.TomcatServer;

import java.util.List;

/**
 * @author lijiayin
 */
public class MiniApplication {
    public static void run(Class<?> cls, String[] args){
        System.out.println("Hello Mini-Spring!");
        TomcatServer tomcatServer = new TomcatServer(args);
        try {
            tomcatServer.startServer();
            List<Class<?>> classList = ClassScanner.scanClasses(cls.getPackage().getName());
            //处理请求
            HandlerManager.resolveMappingHandler(classList);
            //初始化Bean
            BeanFactory.initBean(classList);
            classList.forEach(e -> System.out.println(e.getName()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 修改MappingHandler类,不需要创建Controller对象,直接从BeanFactory获取
package com.istimeless.web.handler;

import com.istimeless.beans.BeanFactory;
import lombok.AllArgsConstructor;

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;

/**
 * @author lijiayin
 */
@AllArgsConstructor
public class MappingHandler {
    /**
     * 请求uri
     */
    private String uri;
    /**
     * 请求方法
     */
    private Method method;
    /**
     * 请求的类
     */
    private Class<?> controller;
    /**
     * 请求的参数名
     */
    private String[] args;
    
    public boolean handle(ServletRequest req, ServletResponse res)
            throws IllegalAccessException, InstantiationException, 
            InvocationTargetException, IOException {
        String requestUri = ((HttpServletRequest) req).getRequestURI();
        if(!uri.equals(requestUri)){
            return false;
        }
        
        //获取参数
        Object[] parameters = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            parameters[i] = req.getParameter(args[i]);
        }
        
        Object ctl = BeanFactory.getBean(controller);
        Object response = method.invoke(ctl, parameters);
        res.getWriter().println(response.toString());
        return true;
    }
}
  1. 创建SalaryService进行测试
package com.istimeless.service;

import com.istimeless.beans.Bean;

/**
 * @author lijiayin
 */
@Bean
public class SalaryService {
    public Integer calSalary(Integer experience){
        return experience * 5000;
    }
}
  1. 修改SalaryController,注入SalaryService
package com.istimeless.controller;

import com.istimeless.beans.AutoWired;
import com.istimeless.service.SalaryService;
import com.istimeless.web.mvc.Controller;
import com.istimeless.web.mvc.RequestMapping;
import com.istimeless.web.mvc.RequestParam;

/**
 * @author lijiayin
 */
@Controller
@RequestMapping("/salary")
public class SalaryController {
    @AutoWired
    private SalaryService salaryService;
    
    @RequestMapping("/getSalary.json")
    public Integer getSalary(@RequestParam("name") String name, 
                             @RequestParam("experience") String experience){
        return salaryService.calSalary(Integer.parseInt(experience));
    }
}
  1. 测试结果


    测试结果.png

GitHub地址

GitHub - SereneSoul/mini-spring: 手写迷你Spring框架

相关文章

网友评论

      本文标题:手写SpringMVC:Bean管理(基石)

      本文链接:https://www.haomeiwen.com/subject/qetblctx.html