Handwritten a simple framework to SpirngMVC

  spring for java programmers, no doubt chopsticks to eat. In each program to work, we almost can not do without it, I believe both past, present or future, or to a period of time, it will continue to play an important role. I have a certain degree of self-insight into spring, so refer to the online videos and articles, sorting out a simple set of SpirngMVC.

 

  Project address posted first, then talk about the next process.

  Handwritten simple SpringMvc framework.

 

  It is divided into several steps:

  1. The following document scan package.

  The scan-to-file to initialize the bean factory.

  The @Controller @RequestMapping annotations, handles the mapping.

  4. Start tomcat.

 

1. Based Maven project, the main framework of the module frame SpringMVC, test Test for convenience only, references to individual test frame framework.

 

 2. Next to speak at the main framework module. FIG follows:

(1) MiniApplication inlet class frame. There are four main functions.

package com.chan.starter;

import com.chan.beans.BeanFactory;
import com.chan.core.ClassScanner;
import com.chan.web.handler.HandlerManage;
import com.chan.web.server.TomcatServer;
import org.apache.catalina.LifecycleException;

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

/**
 * @description: 项目的入口
 * @author: Chen
 * @create: 2019-06-23 14:22
 **/
public class MiniApplication {

    public static void run(Class<?> clz,String[] agrs){
        try {
            //根据传进来的clz所在的包取扫描包下的数据
            List<Class<?>> classList = ClassScanner.scanClasses(clz.getPackage().getName());
            try {
                //根据扫描到到文件,初始化bean工厂。
                BeanFactory.init(classList);
                //根据@Controller @RequestMapping 注解,处理映射关系。
                HandlerManage.resolveMappingHandleList(classList);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            //启动tomcat
            TomcatServer tomcatServer = new TomcatServer(agrs);
            tomcatServer.startServer();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }

}

(2)功能一  扫描包下的文件。

package com.chan.core;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @description:类扫描器
 * @author: Chen
 * @create: 2019-07-02 22:29
 **/
public class ClassScanner {

    /**
     * 扫描包下的文件并返回。  
     * 如果是jar包,则取jar的文件。如果是文件夹,这递归取
     * @param packegeName
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static List<Class<?>> scanClasses(String packegeName) throws IOException, ClassNotFoundException {

        List<Class<?>> classList = new ArrayList<>();
        String path = packegeName.replace(".","/");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> resources = classLoader.getResources(path);
        while (resources.hasMoreElements()){

            URL resource = resources.nextElement();
            //如果资源是jar包,那么遍历jar里面到文件
            if (resource.getProtocol().contains("jar")){
                JarURLConnection jarURLConnection = (JarURLConnection) resource.openConnection();
                String jarFilePath = jarURLConnection.getJarFile().getName();
                classList.addAll(getClassesFromJar(path,jarFilePath));
            }
            //是文件的话,递归取的文件
            else if (resource.getProtocol().contains("file")){
                File dir = new File(resource.getFile());
                for (File file:dir.listFiles()){
                    if (file.isDirectory()){
                        classList.addAll(scanClasses(packegeName + "." + file.getName()));
                    }else {
                        String className =packegeName +"." +file.getName().replace(".class", "");
                        classList.add(Class.forName(className));
                    }
                }

            }

        }
        return classList;

    }

    private static List<Class<?>> getClassesFromJar(String path, String jarFilePath) throws IOException, ClassNotFoundException {

        List<Class<?>> classList = new ArrayList<>();
        JarFile jarFile = new JarFile(jarFilePath);
        Enumeration<JarEntry> jarEntrys = jarFile.entries();
        while (jarEntrys.hasMoreElements()){
            JarEntry jarEntry = jarEntrys.nextElement();
            String name = jarEntry.getName();
            if (name.startsWith(path)&&name.endsWith(".class")){
                String classFullName = name.replace("/", ".").substring(0, name.length() - 6);
                classList.add(Class.forName(classFullName));
            }
        }

        return classList;

    }

}

 

(3) 根据扫描到到文件,初始化bean工厂。

package com.chan.beans;

import com.chan.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;

/**
 * @description: bean 工厂
 * @author: Chen
 * @create: 2019-07-03 00:03
 **/
public class BeanFactory {

    /**
     * 配置bean容器 存放bean
     */
    private static Map<Class<?>,Object> beanMap = new ConcurrentHashMap<>();

    /**
     * 根据类  获取bean
     * @param clz
     * @return
     */
    public static Object getBean(Class<?> clz){return beanMap.get(clz);}

    /**
     * 根据扫描到到类  依次按照规则注入到bean容器中
     * @param classList
     * @throws Exception
     */
    public static void init(List<Class<?>> classList) throws Exception {

        List<Class<?>> toCreate = new ArrayList<>(classList);

        /**
         * 将满足注入条件循环注入bean容器
         */
        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 (remainSize==toCreate.size()){
                throw new Exception("cycle dependency");
            }

        }

    }


    /**
     * 判断是否是需要注入到bean
     * 如果不是 直接返回true  并且添加到bean容器
     * 如果是,但是当时不满足注入条件  返回false  等到下次循环再调用
     * 直至满足条件,添加到bean容器中
     * @param clz
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static boolean finishCreate(Class<?> clz) throws IllegalAccessException, InstantiationException {

        if (!clz.isAnnotationPresent(Controller.class)&&!clz.isAnnotationPresent(Bean.class)){
            return true;
        }

        Object bean = clz.newInstance();
        for (Field field : clz.getDeclaredFields()){
            if (field.isAnnotationPresent(AutoWired.class)){
                Class<?> fieldType = field.getType();
                Object filedTypeObj = BeanFactory.getBean(fieldType);
                if (filedTypeObj==null){
                    return false;
                }

                field.setAccessible(true);
                field.set(bean,filedTypeObj);
            }
        }

        beanMap.put(clz,bean);
        return true;
    }

}

 

(4)根据@Controller @RequestMapping 注解,处理映射关系。 

package com.chan.web.handler;

import com.chan.web.mvc.Controller;
import com.chan.web.mvc.RequestMapping;
import com.chan.web.mvc.RequestParam;
import com.sun.glass.events.mac.NpapiEvent;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 映射处理管理中心
 * @author: Chen
 * @create: 2019-07-03 00:34
 **/
public class HandlerManage {

    /**
     * 保存需要映射的列表
     */
    public static List<MappingHandle> mappingHandleList = new ArrayList<>();

    public static void resolveMappingHandleList(List<Class<?>> classList){

        for (Class<?> clz :classList){

            if (clz.isAnnotationPresent(Controller.class)){
                parseHandlerFromController(clz);
            }

        }

    }

    private static void parseHandlerFromController(Class<?> clz) {

        Method[] methods = clz.getMethods();

        for (Method method:methods){

            if (!method.isAnnotationPresent(RequestMapping.class)){
                continue;
            }

            String uri = method.getDeclaredAnnotation(RequestMapping.class).value();
            List<String> paramNameList = new ArrayList<>();

            for (Parameter parameter:method.getParameters()){
                if (parameter.isAnnotationPresent(RequestParam.class)){
                    paramNameList.add(parameter.getDeclaredAnnotation(RequestParam.class).value());
                }
            }
            String[] args = paramNameList.toArray(new String[paramNameList.size()]);
            MappingHandle mappingHandle = new MappingHandle(uri,method,clz,args);
            HandlerManage.mappingHandleList.add(mappingHandle);
        }

    }

}

 

(5)配置DispatcherServlet启动tomcat。

package com.chan.web.server;

import com.chan.util.YmlUtil;
import com.chan.web.servlet.DispatcherServlet;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;

import javax.servlet.Servlet;

/**
 * @description:根据 tomcat 的包进行处理
 * @author: Chen
 * @create: 2019-06-23 14:58
 **/
public class TomcatServer {

    private Tomcat tomcat;
    private String[] args;

    public TomcatServer(String[] args){
        this.args = args;
    }

    public void startServer() throws LifecycleException {
        tomcat = new Tomcat();
        tomcat.setPort(YmlUtil.get("server.port"));
        tomcat.start();

        Context context = new StandardContext();
        context.setPath("");
        context.addLifecycleListener(new Tomcat.FixContextListener());

        DispatcherServlet servlet = new DispatcherServlet();
        Tomcat.addServlet(context, "dispatcherServlet", servlet).setAsyncSupported(true);
        context.addServletMappingDecoded("/", "dispatcherServlet");
        tomcat.getHost().addChild(context);

        Thread awaitThread = new Thread("tomcar-await-thread"){
            @Override
            public void run() {
                TomcatServer.this.tomcat.getServer().await();
            }
        };
        awaitThread.setDaemon(false);
        awaitThread.start();
    }

}
package com.chan.web.servlet;

import com.chan.web.handler.HandlerManage;
import com.chan.web.handler.MappingHandle;

import javax.servlet.*;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

/**
 * @description: serlet适配器,所有的url都会进入到此处,
 * 在此处根据@RequestMaping所映射到方法进行操作。
 * @author: Chen
 * @create: 2019-06-23 15:15
 **/
public class DispatcherServlet implements Servlet {
    @Override
    public void init(ServletConfig config) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {

        System.out.println("HandlerManage.mappingHandleList:"+HandlerManage.mappingHandleList.size());

        for (MappingHandle mappingHandle : HandlerManage.mappingHandleList){
            try {
                if (mappingHandle.handle(req,res)){
                    return;
                }else {
                    System.out.println("false");
                }
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}
package com.chan.web.handler;

import com.chan.beans.BeanFactory;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @description: 映射处理类
 * @author: Chen
 * @create: 2019-07-03 00:35
 **/
public class MappingHandle {

    String uri;

    Method method;

    Class<?> controller;

    String[] agrs;

    public MappingHandle(String uri,Method method,Class<?> controller,String[] agrs){
        this.uri = uri;
        this.method = method;
        this.controller = controller;
        this.agrs = agrs;
    }

    /**
     * 将扫描到到RequestMapping的路径  进行处理
     * 通过反射调用 调用该方法
     * @param req
     * @param res
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IOException
     */
    public boolean handle(ServletRequest req, ServletResponse res) throws InvocationTargetException, IllegalAccessException, IOException {

        String requestURI = ((HttpServletRequest)req).getRequestURI();

        System.out.println("uri:"+uri);
        System.out.println("requestURI:"+requestURI.substring(1));
        if (!uri.equals(requestURI.substring(1))){
            return false;
        }

        String[] params = new String[agrs.length];
        for (int i =0;i< params.length;i++){
            params[i] = req.getParameter(agrs[i]);
        }

        Object obj = BeanFactory.getBean(controller);
        System.out.println(obj);
        if (obj==null){
            return false;
        }

        Object ret = method.invoke(obj,params);
        System.out.println("ret:"+ret.toString());
        res.getWriter().println(ret.toString());
        return true;
    }

}

 

 

3.测试framework模块。

 

(1) application.yml 配置项目启动的端口号。

(2)Application项目入口。

(3)controller 控制层

(4)service 业务层,在这里注入类bean。   

这里和springboot类似,这只是简单Spirng框架的大概思路流程。

在尾巴处再次贴上项目手写简单的SpringMvc框架。

 

Guess you like

Origin www.cnblogs.com/boychen/p/11135916.html