mini springMvc

继上篇miniTomcat(https://mp.csdn.net/editor/html/115064859) 后继续根据spring IOC(控制反转)和aop(切面)原理来实现自动注入和切面代理。

最终实现的效果,实现自动注入@controller @component @Aspect注解,并模拟mvc处理minitmcat 的请求

package com.hole.mvcController;

import com.hole.http.HttpServletRequest;
import com.hole.http.HttpServletResponse;
import com.hole.iocFactory.Controller;
import com.hole.iocFactory.RequestMapping;
import com.hole.iocFactory.RequestParam;
import com.hole.iocFactory.mvc.RequestMethod;
import com.hole.server.Processor;
import com.hole.servlet.HttpServlet;

@Controller
public class TestController implements Processor {
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public String testGet(@RequestParam(value = "testParam") String testParam){
        return  "test get OK";
    }

    @RequestMapping(value = "/post", method = RequestMethod.POST)
    public String tesPost(@RequestParam(value = "testParam") String testParam){
        return  "test post OK";
    }

    @Override
    public void process(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {

    }
}

处理结果

res:test get OK

IOC控制反转和AOP功能

扫描指定包或者文件,识别标示性注解,例如@controller @component 等,将class对象保存至Bean工厂,在注入相应属性@AtuoAnnotion,实现注入。

实际中会存在复杂的情况,例如循环依赖问题,spring采用的三级缓存保存处理(class工厂层(一级,以后做代理包装类的作用),实例化层(防止实例代理后产生变化)以及最终的实例(最终保存的实例))

只是实现简单功能,不用考虑复杂情况,直接用map保存实例,aop处理后再次更新实例。

package com.hole.iocFactory;


import com.hole.server.InterceptorImpHttp2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;

import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @description: 注解类扫描
 * @author: guozi
 * @create: 2021-03-23
 */
public class ClassScanner {

    public static final Logger logger = LoggerFactory.getLogger(ClassScanner.class);

    // 线程上下文类加载器默认是应用类加载器,即 ClassLoader.getSystemClassLoader();
    // 从线程获取的classLoader,防止不同环境下classloader不一致
    private ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    /**
     * 存放解析的class
     */
    private List<Class<?>> classList = new ArrayList<>();

    /**
     * 限定pkg的类, /com/aws/controller
     */
    private String path = "";

    public ClassScanner(){
    }

    public ClassScanner(String path){
        path = path;
    }


    /**
     * @param pkgName 要扫描的包名位置
     */
    public  List<Class<?>> scanClass(String pkgName){
        //处理路径
        String path = pkgName.replace(".", "/");

        try{
            // 使用类加载器对象的 getResources(ResourceName) 方法获取资源集
            // Enumeration 是古老的迭代器版本,可当成 Iterator 使用
            //path =  path + "/miniSpring.jar";
            Enumeration<URL> resources = classLoader.getResources(path);
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                // 获取协议类型,判断是否为 jar 包
                if (url.getProtocol().equals("jar")) {
                    dealJar(url);
                } else if (url.getProtocol().equals("file")) {
                    //处理文件
                    //URL转换为file类型,File构造方法里有File(URI uri)
                    File file = new File(url.toURI());
                    dealFile(pkgName, file);
                }
            }
        }catch (IOException | URISyntaxException e){
            e.printStackTrace();
        }
        return classList;
    }

    /**
     * 处理class 文件
     */
    protected  void dealClass(Class<?> clazz){
        logger.info("扫描的class:{}, class属性:{}", clazz.getName(), clazz.getDeclaredFields());
    }

    /**
     * 处理jar文件
     */
    private void dealJar(URL url){
        // 将打开的 url 返回的 URLConnection 转换成其子类 JarURLConnection 包连接
        try{
            JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
            JarFile jarFile = jarURLConnection.getJarFile();
            Enumeration<JarEntry> jarEntries = jarFile.entries();
            while(jarEntries.hasMoreElements()){
                JarEntry jarEntry = jarEntries.nextElement();
                //如果不是*.class则不处理
                if(jarEntry.isDirectory() || !jarEntry.getName().endsWith(".class") ){
                    continue;
                }
                //判断是否限定了pkg,若不在pkg中,则跳过
                if(path.length() != 0 && !jarEntry.getName().startsWith(path)){
                    continue;
                }
                //处理class,加载该类
                String className = jarEntry.getName().substring(0, jarEntry.getName().lastIndexOf(".class"))
                        .replace("/", ".");
                Class<?> clazz = Class.forName(className);
                classList.add(clazz);
                dealClass(clazz);
            }
        }catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }

    /**
     * 处理file目录
     */
    private void dealFile(String path, File file){
        if(file.exists()){
            File[] files = file.listFiles();
            for(File childFile : files){
                String fileName = childFile.getName();
                if(childFile.isDirectory()){
                    dealFile(path + "." + childFile.getName(), childFile);
                }else{
                    if(fileName.endsWith(".class")){
                        dealClass(childFile, path);
                    }
                }
            }
        }
    }

    /**
     * 处理class类文件
     */
    private void dealClass(File file, String classPath){
        try{
            int index = file.getName().lastIndexOf(".class");
            String classFullPath = classPath + "." +file.getName().substring(0, index);
            Class<?> clazz = Class.forName(classFullPath);
            classList.add(clazz);
            dealClass(clazz);
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
    }

    public List<Class<?>> getClassList() {
        return classList;
    }
}
package com.hole.iocFactory;

import com.hole.exception.ClassNotBeInjectedException;
import com.hole.iocFactory.aspect.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: bean工厂,实现注解自动注入
 * @author: guozi
 * @create: 2021-03-23
 */
public class BeanFactory {

    /**
     * bean管理map
     */
    private static Map<Class<?>, Object> beans = new ConcurrentHashMap<>();

    /**
     * 带有 @AutoWired 注解修饰的属性的类
     */
    private static Set<Class<?>> beansHasAutoWiredField = Collections.synchronizedSet(new HashSet<>());

    public Object getBean(Class<?> clazz){
        try{
            if(beans.get(clazz) == null){
                if(!clazz.isAnnotationPresent(Component.class) && !clazz.isAnnotationPresent(Controller.class)){
                    throw new ClassNotBeInjectedException("该类没有注解");
                }
                Object singleInstance = clazz.newInstance();
                beans.put(clazz, singleInstance);
            }
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }

        return beans.get(clazz);
    }

    /**
     * 处理扫描的类(目前只处理Controller Component ),生成实例,并放入工厂,同时注入相应属性
     */
    public void initBeans(List<Class<?>> classList){
        //重新保存一份扫描类
        List<Class<?>> classesToCreate = new ArrayList<>(classList);
        //备注解的切面类
        List<Class<?>> aspectClasses = new ArrayList<>();

        //分开处理两中类
        for(Class<?> clazz : classesToCreate){
            if(clazz.isAnnotationPresent(Aspect.class)){
                //处理切面类
                aspectClasses.add(clazz);
            }else{
                //处理其他标注类
                createBean(clazz);
            }
        }

        //单独处理切面类
        resolveAop(aspectClasses);

        //重新更新Annotation引用的类
        for(Class<?> clazz : beansHasAutoWiredField){
            createBean(clazz);
        }
    }

    /**
     *为bean创建单实例,并注入相关属性
     */
    private void createBean(Class<?> clazz){
        try{
            //暂时只处理 @Component / @Controller 注解的类
            if(!clazz.isAnnotationPresent(Component.class) && !clazz.isAnnotationPresent(Controller.class)){
                return;
            }
            //初始化对象
            Object instance = null;
            if(beans.get(clazz) == null){
                instance = clazz.newInstance();
                beans.put(clazz, instance);
            }else{
                instance = beans.get(clazz);
            }


            for(Field field : clazz.getDeclaredFields()){
                if(!field.isAnnotationPresent(AutoWired.class)){
                    continue;
                }
                //保存bean,后续aop需要利用代理更新
                beansHasAutoWiredField.add(clazz);
                Class<?> fieldType = field.getType();
                if(fieldType.isInterface()){
                    //若注入的是接口,则需要第一个实现类
                    for(Class<?> key:beans.keySet()){
                        if(fieldType.isAssignableFrom(key)){
                            fieldType = key;
                            break;
                        }
                    }
                }
                field.set(instance, getBean(fieldType));
            }
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

    private void resolveAop( List<Class<?>> aspectClazz){
        if(aspectClazz.size() == 0){
            return ;
        }

        try{
            for(Class<?> clazz : aspectClazz){
                Object target = null;
                String method = null;
                String pointcutName = null; //切点函数
                Method before = null;
                Method after = null;

                //反射获取切点作用对象和切点作用方法
                for(Method me : clazz.getMethods()){
                    if(me.isAnnotationPresent(Pointcut.class)){
                        String pointcut =  me.getAnnotation(Pointcut.class).value();
                        String classStr = pointcut.substring(0, pointcut.lastIndexOf("."));
                        target = Thread.currentThread().getContextClassLoader().loadClass(classStr).newInstance();
                        method = pointcut.substring(pointcut.lastIndexOf(".") + 1);
                        pointcutName = me.getName();
                    }
                }

                //反射获取切面函数
                for(Method me : clazz.getMethods()){
                    if(me.isAnnotationPresent(Before.class)){
                        String value = me.getAnnotation(Before.class).value();
                        value = value.substring(0, value.indexOf("("));
                        if (value.equals(pointcutName)) {
                            before = me;
                        }
                    }else if(me.isAnnotationPresent(After.class)){
                        String value = me.getAnnotation(After.class).value();
                        value = value.substring(0, value.indexOf("("));
                        if (value.equals(pointcutName)) {
                            after = me;
                        }
                    }
                }

                //重新代理对象
                AspectProxy aspectProxy = new AspectProxy(before, after, target, method,  clazz.newInstance());
                BeanFactory.beans.put(target.getClass(), aspectProxy.createProxy());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

    public static Map<Class<?>, Object> getBeans(){
        return beans;
    }



}

mvc功能

mvc主要是处理minitomcat 请求,根据url params method 来确定请求的Controller,实际mvc请求根据不同注解有个排序方式,选择最符合的controller method来处理请求

这里简单处理,以url为请求map的key值,来检查请求和请求处理方法的映射

package com.hole.iocFactory.mvc;

import com.hole.iocFactory.Controller;
import com.hole.iocFactory.RequestMapping;
import com.hole.iocFactory.RequestParam;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 管理controller params get/post  路径 -> hash -> map  方法
 * @author: guozi
 * @create: 2021-03-23
 */
public class HandlerManager {

    /**
     * 单独管理controller注解,简单处理,以请求路径的hash值为key值
     */
    public static Map<String, RequestHandler> handlerMap = new HashMap<>();

    public static void resolveHandler( Map<Class<?>, Object> beans){
        beans.keySet().stream().filter(clazz -> clazz.isAnnotationPresent(Controller.class))
                .forEach(HandlerManager::parseHandlerFromController);


    }


    private static void parseHandlerFromController(Class<?> clazz){
        //解析带有request的method
        Arrays.stream(clazz.getDeclaredMethods()).filter(method -> method.isAnnotationPresent(RequestMapping.class))
                .forEach(method -> {
                    String url = method.getAnnotation(RequestMapping.class).value();
                    RequestMethod[] requestMethod = method.getAnnotation(RequestMapping.class).method();
                    String[] params = Arrays.stream(method.getParameters())
                            .filter(parameter -> parameter.isAnnotationPresent(RequestParam.class))
                            .map(parameter -> parameter.getAnnotation(RequestParam.class).value())
                            .toArray(String[]::new);
                    handlerMap.put(url, new RequestHandler(clazz, url,method, params, requestMethod[0]));
                });
    }


}
package com.hole.iocFactory.mvc;

import com.hole.http.HttpServletRequest;
import com.hole.http.HttpServletResponse;
import com.hole.iocFactory.BeanFactory;

import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @description: 请求处理类
 * @author: guozi
 * @create: 2021-03-23
 */
public class RequestHandler implements  IRequestHandler{
    /**
     * controller对象
     */
    private Class<?> controller;

    /**
     * 请求路径
     */
    private String url;

    /**
     * 处理方法
     */
    private Method method;

    /**
     * 方法参数
     */
    private String[] params;

    /**
     * 请求方式
     */
    private RequestMethod requestMethod;

    RequestHandler(Class<?> target, String url, Method method, String[] params, RequestMethod requestMethod){
        this.controller = target;
        this.url = url;
        this.method = method;
        this.params = params;
        this.requestMethod = requestMethod;
    }

    /**
     * 实际处理该请求
     */
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
        try{
            //获取url
            String url = httpServletRequest.getRequestUrlNoParam();
            //根据请求url获取对应的request枚举类
            RequestMethod urlRequestMethod = RequestMethod.getEnumMap(httpServletRequest.getMethod());
            if(urlRequestMethod == null){
                urlRequestMethod = RequestMethod.GET;
            }
            //获取参数
            Object[] urlParameters = new Object[params.length];
            for(int i = 0; i < params.length; i++){
                //从请求中获取键值,传给controller函数
                urlParameters[i] = httpServletRequest.getParameters(params[i]);
            }


            Object response = method.invoke(BeanFactory.getBeans().get(controller), urlParameters);
            //输入html内容
            PrintWriter printWriter = httpServletResponse.getWriter();
            printWriter.print("HTTP/1.1 200 OK\r\n\r\n");
            printWriter.print("res:" + response.toString());
//            printWriter.print("<html>");
//            printWriter.print("<body>");
//            printWriter.print("<p>");
//            printWriter.print("</p>");
//            printWriter.print("</body>");
//            printWriter.print("</html>");
            printWriter.flush();
            printWriter.close();
        }catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }


}

minitomcat请求分发过程

package com.hole.server;

import com.hole.http.HttpServletRequest;
import com.hole.http.HttpServletResponse;
import com.hole.iocFactory.mvc.HandlerManager;
import com.hole.iocFactory.mvc.IRequestHandler;
import com.hole.iocFactory.mvc.RequestHandler;
import com.hole.servlet.ServletContext;
import com.hole.util.InterceptorProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;


/**
 * @describtion: 客户端连接信息处理服务
 * @author: guozi
 * @create: 2021-01-25
 */
public class ServerService implements Runnable{
    /**
     * 连接客户端信息
     */
    private Socket client;
    public static final Logger logger = LoggerFactory.getLogger(ServerService.class);

    public ServerService(Socket socket){
        super();
        this.client = socket;
    }

    @Override
    public void run() {
        InputStream input = null;
        OutputStream output = null;
        try{
            input = this.client.getInputStream();
            output = this.client.getOutputStream();
        }catch (Exception e){
            logger.error("客户端{}连接断开,原因:{}", this.client.getRemoteSocketAddress(), e.getMessage());
            return;
        }

        //采用的是http协议,基于连接信息,创建http请求和响应对象
        HttpServletRequest httpServletRequest = new HttpServletRequest(input);
        HttpServletResponse httpServletResponse = new HttpServletResponse(httpServletRequest, output);

        //判断是否是动态还是静态请求,动态请求包括.action->要给到的处理类
        String url = httpServletRequest.getRequestUrlNoParam();

        //servlet处理 , 链式步骤处理过来的请求
        IRequestHandler interceptorProxy = null;
        //判断是否有对应url 有的处理请求
        if(HandlerManager.handlerMap.get(url) != null){
            //servlet处理 , 链式步骤处理过来的请求
             interceptorProxy = HandlerManager.handlerMap.get(url);
            for(String interceptor : ServletContext.interceptorContext.values()){
                interceptorProxy = (IRequestHandler) InterceptorProxy.bind(interceptorProxy,interceptor);
            }
            //判断是否存在责任链,是的话启用
            if(interceptorProxy != null){
                interceptorProxy.handle(httpServletRequest, httpServletResponse);
            }else{
                HandlerManager.handlerMap.get(url).handle(httpServletRequest, httpServletResponse);
            }
        }else{
            Processor processor = new StaticProcessor();
            processor.process(httpServletRequest, httpServletResponse);
            //httpServletResponse.getWriter().println("no controller to handle");
        }


//        //责任链默认
//        if(url.contains(".action")){
//            processor = new DynamicProcessor();
//        }else{
//            processor = new StaticProcessor();
//        }
//        //servlet处理 , 链式步骤处理过来的请求
//        Processor interceptorProxy = processor;
//        for(String interceptor : ServletContext.interceptorContext.values()){
//             interceptorProxy = (Processor) InterceptorProxy.bind(interceptorProxy,interceptor);
//        }
//        interceptorProxy.process(httpServletRequest, httpServletResponse);
        try{
            //http是短连接,需要及时关闭
            this.client.close();
        }catch (Exception e){
            logger.error("客户端{}连接断开失败,原因:{}", this.client.getRemoteSocketAddress(), e.getMessage());
        }

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值