简单实现springmvc中的请求处理

Posted 镇屌

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单实现springmvc中的请求处理相关的知识,希望对你有一定的参考价值。

自定义MVC框架中的一些元素

一些注解

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

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

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

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

处理对象封装

public class Handler {

    private Object controller;//Controller对应的类
    private Method method;//执行业务的方法
    private Pattern pattern;//uri
    private Map<String,Integer> paramIndexMapping;//参数和位置的映射

    public Handler(Object controller, Method method, Pattern pattern) {
        this.controller = controller;
        this.method = method;
        this.pattern = pattern;
        this.paramIndexMapping = new HashMap<>();
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }

    public Map<String, Integer> getParamIndexMapping() {
        return paramIndexMapping;
    }

    public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
        this.paramIndexMapping = paramIndexMapping;
    }
}

自定义DisptchServelet处理代码

public class DispatchServelet extends HttpServlet {
    private Properties properties = new Properties();
    private List<String> classNames = new ArrayList<>();
    private Map<String,Object> ioc =new HashMap<>();
    private List<Handler> handlerMapping = new ArrayList<>();
    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
        //扫描相关的类,扫描注解
        doScan(properties.getProperty("scanPackage"));
        //初始化bean,基于注解
        doInstance();
        //实现依赖注入
        doAutoWired();
        //实现处理器映射器,将url和method进行关联
        initHandlerMapping();
        System.out.println("mvc 初始化完成");
        
    }
    //执行的是方法和url方法映射
    private void initHandlerMapping() {
        if (ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String,Object> entry :ioc.entrySet()){
            Class<?> aClass = entry.getValue().getClass();
            if (!aClass.isAnnotationPresent(Controller.class)){
                continue;
            }
            String baseUrl = "";
            if (aClass.isAnnotationPresent(RequestMapping.class)){
                RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);
                baseUrl=requestMapping.value();
            }
            Method[] methods= aClass.getMethods();
            for (int i=0;i<methods.length;i++){
                Method method = methods[i];
                if (!method.isAnnotationPresent(RequestMapping.class)){
                    continue;
                }
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                String methodUrl = requestMapping.value();
                String url = baseUrl+methodUrl;
                Handler handler = new Handler(entry.getValue(),method, Pattern.compile(url));
                Parameter[] parameters = method.getParameters();
                for (int j=0;j<parameters.length;j++){
                    Parameter parameter = parameters[j];
                    if (parameter.getType().equals(HttpServletRequest.class)||parameter.getType().equals(HttpServletResponse.class)){
                        handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),j);
                    }else {
                        handler.getParamIndexMapping().put(parameter.getName(),j);
                    }
                }
                //完成方法和url的映射关系
                handlerMapping.add(handler);
            }
        }
    }
    //执行注入部分,同样是做的ioc的部分功能
    private void doAutoWired() {
        if (ioc.isEmpty()){
            return;
        }
        for (Map.Entry<String,Object> entry :ioc.entrySet()){
            Field[] declareFields = entry.getValue().getClass().getDeclaredFields();
            for (int i=0;i<declareFields.length;i++){
                Field declareField = declareFields[i];
                if (!declareField.isAnnotationPresent(Autowired.class)){
                    continue;
                }
                Autowired autowired = declareField.getAnnotation(Autowired.class);
                String beanName = autowired.value();
                if ("".equals(beanName.trim())){
                    beanName=declareField.getType().getName();
                }
                declareField.setAccessible(true);
                try {
                    //直接将这个字段的值设置为ioc中已经示例化的类,
                    // 即是完成了ioc中的实例化交给容器来管理的情况
                    declareField.set(entry.getValue(),ioc.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    //执行的是符合要求的类的初始化,实际上是实现的一部分ioc的功能
    private void doInstance()  {
        if (classNames.size()==0){
            return;
        }
        try {
            for (int i=0;i<classNames.size();i++){
                String className = classNames.get(i);
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(Controller.class)){
                    String simpleName = clazz.getSimpleName();
                    String lowerFirst = lowerFirst(simpleName);
                    Object o = clazz.newInstance();
                    //因为controller无别名,所以简单设置成首字母小写就行
                    ioc.put(lowerFirst,o);
                }else if (clazz.isAnnotationPresent(Service.class)){
                    Service service = clazz.getAnnotation(Service.class);
                    String beanName =service.value();
                    if (!"".equals(beanName.trim())){
                        ioc.put(beanName,clazz.newInstance());
                    }else {
                        beanName = lowerFirst(clazz.getSimpleName());
                        ioc.put(beanName,clazz.newInstance());
                    }

                    Class<?>[] interfaces = clazz.getInterfaces();
                    for (int j=0;j<interfaces.length;j++){
                        Class<?> ainterface = interfaces[j];
                        System.out.println(ainterface.getName());
                        //将实现类和接口进行绑定
                        ioc.put(ainterface.getName(),clazz.newInstance());
                    }
                }else {
                    continue;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }


    private  String lowerFirst(String className){
       char[] chars = className.toCharArray();
       if (\'A\'<chars[0]&&chars[0]<\'Z\'){
           chars[0]+=32;
       }
       return new String(chars);
    }
    private void doScan(String basePackage) {
        //获取到指定包下的所有类的类名
        String scanPackagePath= Thread.currentThread().getContextClassLoader().getResource("").getPath()+basePackage.replaceAll("\\\\.","/");
        File pack = new File(scanPackagePath);
        File [] files = pack.listFiles();
        for (File file:files){
            if (file.isDirectory()){
                doScan(basePackage+"."+file.getName());
            }else if (file.getName().endsWith(".class")){
                String className = basePackage+"."+file.getName().replaceAll(".class","");
                classNames.add(className);
            }
        }
    }
    //实现加载web.xml中配置的文件的路径
    private void doLoadConfig(String contextConfigLocation) {
        InputStream inputStream =this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Handler handler = getHander(req);
        if (handler==null){
            resp.getWriter().write("404 not found");
            return;
        }
        Class<?> [] parameterTypes = handler.getMethod().getParameterTypes();
        Object[] paraValues = new Object[parameterTypes.length];

        Map<String,String[]> parameterMap = req.getParameterMap();
        for (Map.Entry<String,String[]> param:parameterMap.entrySet()){
            String value = StringUtils.join(param.getValue(),",");
            if (!handler.getParamIndexMapping().containsKey(param.getKey())){
                continue;
            }
            //对应实际参数的位置
            Integer index = handler.getParamIndexMapping().get(param.getKey());
            paraValues[index]=value;

        }
        //对应上req,和resp参数的位置
        int reqIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        paraValues[reqIndex]=req;
        int respIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        paraValues[respIndex]=resp;

        try {
            //实际执行的是controller中的方法
            handler.getMethod().invoke(handler.getController(),paraValues);
            System.out.println("执行controller方法成功");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    private Handler getHander(HttpServletRequest req) {
        if (handlerMapping.isEmpty()){
            return null;
        }
        String url =req.getRequestURI();
        for (Handler handler:handlerMapping){
            Matcher matcher = handler.getPattern().matcher(url);
            if (!matcher.matches()){
                continue;
            }
            return handler;
        }
        return null;
    }

}

测试代码

public interface DemoService {

    String getName(String name);
}



@Service("demoService")
public class DemoServiceImpl implements DemoService {
    @Override
    public String getName(String name) {
        return name;
    }
}

@Controller
@RequestMapping("/demo")
public class DemoController {
    @Autowired
    private DemoService demoService;
    @RequestMapping("/query")
    public String query(HttpServletRequest req, HttpServletResponse resp,String name){
        return demoService.getName(name);
    }
}

web.xml配置

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>com.zhao.mvcframework.servelet.DispatchServelet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>mvc.properties</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

mvc.properties的配置

scanPackage=com.zhao.mvcdemo

pom文件的配置

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.zhao</groupId>
  <artifactId>mvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>mvc Maven Webapp</name>
  <!-- FIXME change it to the project\'s website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.10</version>
    </dependency>
  </dependencies>

  <build>
      <plugins>
        <plugin>
          <groupId>org.apache.tomcat.maven</groupId>
          <artifactId>tomcat7-maven-plugin</artifactId>
          <version>2.2</version>
          <configuration>
            <port>8080</port>
            <path>/</path>
          </configuration>

        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
              <source>1.8</source>
              <target>1.8</target>
              <encoding>utf-8</encoding>
              <compilerArgs>-parameters</compilerArgs>
            </configuration>
        </plugin>
      </plugins>
  </build>
</project>

测试时访问具体的http://localhost:8080/demo/query?name=zhaozhen 无问题
file
代码地址为https://github.com/zhendiao/deme-code/tree/main/mvc

以上是关于简单实现springmvc中的请求处理的主要内容,如果未能解决你的问题,请参考以下文章

SpringMvc实现的简单原理

spring mvc 第二天注解实现springmvc Handler处理ajax简单请求 的配置

代码片段 - Golang 实现简单的 Web 服务器

Spring注解处理Ajax请求-JSON格式[系统架构:Spring+SpringMVC+MyBatis+MySql]

简单使用拦截器

springmvc框架中的核心对象DispatcherServlet