仿真SpingMVC无web.xml启动

源于蚂蚁课堂的学习,点击这里查看

仅做流程和源码的仿真,细节上的一些BUG可忽略(读者可自行完善)
源码剖析可查看我的其他springMVC相关博文

1.maven依赖

  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.10</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.4</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>


  </dependencies>

2.配置程序入口 

import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.HandlesTypes;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:39<br>
 * @Version 1.0<br>
 */
@HandlesTypes(YXHWebInitializer.class)
public class MvcServletContainerInitializer implements ServletContainerInitializer {

    public void onStartup(Set<Class<?>> webInitializerClasses, ServletContext servletContext) throws ServletException {
        if (webInitializerClasses.isEmpty()) {
            servletContext.log("没有启动的配置类...");
            return;
        }

        try {
            for (Class<?> clazz : webInitializerClasses) {
                // YXHWebInitializer
                Method method = clazz.getMethod("onStartup", ServletContext.class);
                YXHWebInitializer webInitializer = (YXHWebInitializer) clazz.newInstance();
                method.invoke(webInitializer, servletContext);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:39<br>
 * @Version 1.0<br>
 */
public interface YXHWebInitializer {
    void onStartup(ServletContext servletContext);
}

 

import live.yanxiaohui.create.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:35<br>
 * @Version 1.0<br>
 */
public class SpringMvcInitializer implements YXHWebInitializer {

    public void onStartup(ServletContext servletContext) {

        // 1.将dispatcher注册至servle容器
        ServletRegistration.Dynamic dynamic =servletContext.addServlet("dispatcher", new DispatcherServlet(SpringMvcConfig.class));
        // 2.设置拦截的路径
        dynamic.addMapping("/");
        // 3.设置加载顺序的优先级
        dynamic.setLoadOnStartup(1);
    }
}
package live.yanxiaohui.create.config;

import live.yanxiaohui.create.annotation.YXHComponentScan;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 10:43<br>
 * @Version 1.0<br>
 */
@YXHComponentScan("live.yanxiaohui.create.controller")
public class SpringMvcConfig {
}

 

 3.servlet相关类

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 10:29<br>
 * @Version 1.0<br>
 */
public abstract class HttpServletBean extends HttpServlet {
    @Override
    public final void init() throws ServletException {
        // 1.公共初始化的操作
        
        // 2.子类的操作
        initServletBean();
    }

    protected void initServletBean() throws ServletException {
    }
}

 

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 10:29<br>
 * @Version 1.0<br>
 */
public abstract class FrameworkServlet extends HttpServletBean {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doService(req, resp);
    }

    protected abstract void doService(HttpServletRequest req, HttpServletResponse resp);


    @Override
    protected final void initServletBean() throws ServletException {
        initWebApplicationContext();
    }

    protected void initWebApplicationContext() {
        onRefresh();
    }


    protected void onRefresh() {

    }
}

import live.yanxiaohui.create.annotation.YXHComponentScan;
import live.yanxiaohui.create.annotation.YXHController;
import live.yanxiaohui.create.annotation.YXHRequestMapping;
import live.yanxiaohui.create.entity.HandlerMethod;
import live.yanxiaohui.create.entity.MappingRegistry;
import live.yanxiaohui.create.utils.ReflectionUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 10:29<br>
 * @Version 1.0<br>
 */
public class DispatcherServlet extends FrameworkServlet {

    // 配置类
    private Class<?> configClass;

    private final MappingRegistry mappingRegistry = new MappingRegistry();

    public DispatcherServlet(Class<?> configClass) {
        this.configClass = configClass;
    }

    @Override
    protected void doService(HttpServletRequest req, HttpServletResponse resp) {
        doDispatch(req, resp);
    }


    @Override
    protected void onRefresh() {
        initStrategies();
    }

    protected void initStrategies() {

        // springMVC的九大组件
        /*initMultipartResolver();
        initLocaleResolver();
        initThemeResolver();
        initHandlerMappings();
        initHandlerAdapters();
        initHandlerExceptionResolvers();
        initRequestToViewNameTranslator();
        initViewResolvers();
        initFlashMapManager();*/

        initHandlerMappings();
    }

    /**
     * 初始化路径映射
     */
    private void initHandlerMappings() {
        try {
            // 1.拿到扫包范围
            YXHComponentScan componentScan = configClass.getAnnotation(YXHComponentScan.class);
            if (componentScan == null) {
                System.out.println("扫包范围为空...");
                return;
            }
            String componentScanValue = componentScan.value();

            // 2.拿到包下有类
            List<Class<?>> classList = ReflectionUtils.getClasses(componentScanValue);

            // 3.判断类是否加controller
            for (Class<?> clazz : classList) {
                if (clazz.getAnnotation(YXHController.class) == null) {
                    continue;
                }
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    // 4.判断方法是否加requestmapping
                    YXHRequestMapping mapping = method.getAnnotation(YXHRequestMapping.class);
                    if (mapping == null) {
                        continue;
                    }
                    HandlerMethod handlerMethod = new HandlerMethod(clazz.newInstance(), clazz, method, method.getParameters());
                    // 5.将requestmapping和方法做绑定放入map
                    mappingRegistry.regist(mapping.value(), handlerMethod);

                }
            }
            System.out.println("路径映射注册完成....");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
        try {
            // 通过路径获取对应的mapping
            String url = req.getServletPath();
            HandlerMethod handlerMethod = mappingRegistry.getHandlerMethod(url);

            // 执行目标方法
            Object target = handlerMethod.getBean();
            Method method = handlerMethod.getMethod();
            Object[] parameters = handlerMethod.getParameters();
            Object retrunValue = method.invoke(target, parameters);

            // 视图解析
            render(retrunValue, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected void render(Object retrunValue, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 只做简单的仿真
        String viewName = String.valueOf(retrunValue);
        String path = "/WEB-INF/views/"+viewName+".jsp";
        // 请求转发
        request.getRequestDispatcher(path).forward(request, response);
    }
}

4.自定义注解

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHRequestMapping {
    String value() default "";
}

 

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHController {
    String value() default "";
}

 

import java.lang.annotation.*;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:36<br>
 * @Version 1.0<br>
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YXHComponentScan {
    String value();
}

5.工具类

package live.yanxiaohui.create.utils;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:52<br>
 * @Version 1.0<br>
 */

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 反射工具类 <br />
 * <br />
 * 提供了一系列的获取某一个类的信息的方法<br />
 * 包括获取全类名,实现的接口,接口的泛型等<br />
 * 并且提供了根据Class类型获取对应的实例对象的方法,以及修改属性和调用对象的方法等
 */
public class ReflectionUtils {

   
    /**
     * 从包package中获取所有的Class
     *
     * @param packageName
     * @return
     */

    public static List<Class<?>> getClasses(String packageName) {
        //第一个class类的集合
        List<Class<?>> classes = new ArrayList<Class<?>>();
        //是否循环迭代
        boolean recursive = true;
        //获取包的名字 并进行替换
        String packageDirName = packageName.replace('.', '/');
        //定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            //循环迭代下去
            while (dirs.hasMoreElements()) {
                //获取下一个元素
                URL url = dirs.nextElement();
                //得到协议的名称
                String protocol = url.getProtocol();

                if ("file".equals(protocol)) {
                    //获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    //以文件的方式扫描整个包下的文件 并添加到集合中
                    findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    //如果是jar包文件
                    //定义一个JarFile
                    JarFile jar;
                    try {
                        //获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        //从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        //同样的进行循环迭代
                        while (entries.hasMoreElements()) {
                            //获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            //如果是以/开头的
                            if (name.charAt(0) == '/') {
                                //获取后面的字符串
                                name = name.substring(1);
                            }

                            //如果前半部分和定义的包名相同
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf('/');
                                //如果以"/"结尾 是一个包
                                if (idx != -1) {
                                    //获取包名 把"/"替换成"."
                                    packageName = name.substring(0, idx).replace('/', '.');
                                }

                                //如果可以迭代下去 并且是一个包
                                if ((idx != -1) || recursive) {
                                    //如果是一个.class文件 而且不是目录
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        //去掉后面的".class" 获取真正的类名
                                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                                        try {
                                            //添加到classes
                                            classes.add(Class.forName(packageName + '.' + className));
                                        } catch (ClassNotFoundException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }


    /**
     * 以文件的形式来获取包下的所有Class
     *
     * @param packageName
     * @param packagePath
     * @param recursive
     * @param classes
     */

    public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) {
        //获取此包的目录 建立一个File
        File dir = new File(packagePath);
        //如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        //如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            //自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });

        //循环所有文件
        for (File file : dirfiles) {
            //如果是目录 则继续扫描
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);

            } else {
                //如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    //添加到集合中去
                    classes.add(Class.forName(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 6.用到的entity

package live.yanxiaohui.create.entity;

import java.lang.reflect.Method;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 11:17<br>
 * @Version 1.0<br>
 */
public class HandlerMethod {

    private final Object bean;

    private final Class<?> beanType;

    private final Method method;

    private final Object[] parameters;

    public HandlerMethod(Object bean, Class<?> beanType, Method method, Object[] parameters) {
        this.bean = bean;
        this.beanType = beanType;
        this.method = method;
        this.parameters = parameters;
    }

    public Object getBean() {
        return bean;
    }

    public Class<?> getBeanType() {
        return beanType;
    }

    public Method getMethod() {
        return method;
    }

    public Object[] getParameters() {
        return parameters;
    }
}

 

package live.yanxiaohui.create.entity;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 11:05<br>
 * @Version 1.0<br>
 */
public class MappingRegistry {
    private final Map<String, HandlerMethod> registry = new ConcurrentHashMap<String, HandlerMethod>();

    public void regist(String url, HandlerMethod handlerMethod){
        registry.put(url,handlerMethod);
    }

    public HandlerMethod getHandlerMethod(String url){
        return registry.get(url);
    }

}

7.控制器

package live.yanxiaohui.create.controller;

import live.yanxiaohui.create.annotation.YXHController;
import live.yanxiaohui.create.annotation.YXHRequestMapping;

/**
 * @Description todo
 * @Author: yanxh<br>
 * @Date 2020-04-09 09:42<br>
 * @Version 1.0<br>
 */
@YXHController
public class IndexController {

    @YXHRequestMapping("/")
    public String index(){
        return "index";
    }

    @YXHRequestMapping("/create")
    public String create(){
        return "create";
    }

    public String delete(){
        return "delete";
    }
}

8.新建视图 

 9.将项目放至tomcat服务器启动访问

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值