自定义mvc

功能介绍

基于servlet完成mvc基本功能

1)@RequestMapping的请求路径映射handler

2)@Service,@controller与@Autowire,对象管理与注入

1、结果展示

在这里插入图片描述

2、引入依赖
<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.68</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.8.1</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><!-- 编译插件 配置编译java文件时保留方法参数名称(默认不保留方法参数名),以便通过反射调用方法-->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <compilerArgument>-parameters</compilerArgument>
          <encoding>UTF-8</encoding>
          <source>11</source>
          <target>11</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
3、注解开发
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}
---------
    
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value() default "";
}
-------
    
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    String value() default "";
}
------
    
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
    String value() default "";
}
4、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>

  <servlet>
    <servlet-name>dispatcherServlet</servlet-name><!-- 此处的前端控制器为自己手动编写的dispatcherServlet -->
    <servlet-class>com.goschen.mvcframework.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>configPath</param-name><!-- 设置配置文件,此处用的properties文件 -->
      <param-value>config.properties</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

5、编写DispatcherServlet核心逻辑

1)DispatcherServlet继承自HttpServlet并在init中完成初始化逻辑,在service中完成url匹配handler并实现调用的逻辑

2)初始化逻辑主要包括(扫描包路径并存储全限定类名, 解析注解并是实例化bean,处理依赖关系,处理映射关系)

import com.goschen.annotation.Autowired;
import com.goschen.annotation.Controller;
import com.goschen.annotation.RequestMapping;
import com.goschen.annotation.Service;
import com.goschen.pojo.Handler;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

public class DispatcherServlet extends HttpServlet {

    // 用于管理实例化的Bean对象
    Map<String, Object> ioc = new HashMap<>();
    // 扫描到的类全限定类名
    List<String> classNameList = new ArrayList<>();
    // 维护url与Handler的映射关系
    Map<String, Handler> handlerMappingMap = new HashMap<>();

    @Override
    public void init(ServletConfig config){

        try {
            // 扫描路径并存储全限定类名
            doScan(config);

            // 解析注解,并是实例化bean
            doResolveComponent();

            //处理依赖关系
            doAutoWired();

            //处理映射关系
            doHandlerMapping();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) {

        try {

            Object result = null;
            String requestURI = req.getRequestURI();
            req.setCharacterEncoding("UTF-8");
            resp.setContentType("text/html;charset=utf-8");
            Handler handler = handlerMappingMap.get(requestURI);
            if(handler!=null){

                Object controller = handler.getController();
                Method method = handler.getMethod();
                Map<String, Integer> paramMapping = handler.getParamMapping();
                Object[] paramArr = new Object[paramMapping.size()];

                Integer requestIndex = paramMapping.get("request");
                Integer responseIndex = paramMapping.get("response");
                if(requestIndex!=null){
                    paramArr[requestIndex] = req;
                }
                if(responseIndex!=null){
                    paramArr[responseIndex] = resp;
                }

                Map<String, String[]> apiParameterMap = req.getParameterMap();
                for (Map.Entry<String, String[]> paramEntry : apiParameterMap.entrySet()) {

                    String paramName = paramEntry.getKey();
                    String[] paramValueArr = paramEntry.getValue();

                    Integer paramIndex = paramMapping.get(paramName);
                    paramArr[paramIndex] = new String(paramValueArr[0].getBytes("ISO8859-1"),"UTF-8");
                }

                result = method.invoke(controller, paramArr);

            }else {

                result = "404 NOT FOUNT";
            }

            resp.getWriter().write(String.valueOf(result));

        }catch (Exception e){

            try {
                resp.getWriter().write("500 SERVER INTERNAL ERROR");
            } catch (IOException ioException) {}
        }



    }

    /** 处理映射关系*/
    private void doHandlerMapping() {

        for (Map.Entry<String, Object> singletonMap : ioc.entrySet()) {

            Class<?> aClass = singletonMap.getValue().getClass();
            if(aClass.isAnnotationPresent(RequestMapping.class)){

                RequestMapping handlerRequestMapping = aClass.getAnnotation(RequestMapping.class);
                String rootUrl = handlerRequestMapping.value();

                Method[] declaredMethods = aClass.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {

                    declaredMethod.setAccessible(true);
                    RequestMapping methodRequestMapping = declaredMethod.getAnnotation(RequestMapping.class);
                    if(methodRequestMapping != null){

                        Handler handler = new Handler();
                        String methodUrl = methodRequestMapping.value();
                        Parameter[] parameters = declaredMethod.getParameters();
                        Map<String,Integer> paramMapping = new HashMap<>();

                        for (int i = 0; i < parameters.length; i++) {

                            Parameter parameter = parameters[i];
                            String name = parameter.getName();
                            paramMapping.put(name,i);
                        }

                        handler.setUrl(rootUrl+methodUrl);
                        handler.setController(singletonMap.getValue());
                        handler.setMethod(declaredMethod);
                        handler.setParamMapping(paramMapping);
                        handlerMappingMap.put(handler.getUrl(),handler);
                    }
                }
            }
        }

    }

    /** 处理依赖关系*/
    private void doAutoWired() throws IllegalAccessException {

        for (Map.Entry<String, Object> singletonObjectMap : ioc.entrySet()) {

            Object singletonObject = singletonObjectMap.getValue();
            Field[] declaredFields = singletonObject.getClass().getDeclaredFields();
            for (Field declaredField : declaredFields) {

                declaredField.setAccessible(true);
                Autowired autowired = declaredField.getAnnotation(Autowired.class);
                if(autowired != null){

                    String value = autowired.value();
                    if("".equals(value)){
                        value = declaredField.getName();
                    }

                    Object o = ioc.get(value);
                    if(o != null){
                        declaredField.set(singletonObject,o);
                    }
                }
            }
        }
    }

    /** 解析注解注解,并是实例化bean*/
    private void doResolveComponent() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        for (String referenceStr : classNameList) {

            String value = null;
            Class<?> aClass = Class.forName(referenceStr);

            if(aClass.isAnnotationPresent(Service.class)){
                Service service = aClass.getAnnotation(Service.class);
                value = service.value();
            }

            if(aClass.isAnnotationPresent(Controller.class)){
                Controller service = aClass.getAnnotation(Controller.class);
                value = service.value();
            }

            if(value != null){

                if("".equals(value)){
                    value = lowerCaseFirstChar(aClass.getSimpleName());
                }

                Object o = aClass.getConstructor().newInstance();
                ioc.put(value.trim(),o);
            }


        }

    }

    /** 扫描路径并存储全限定类名*/
    private void doScan(ServletConfig config) throws IOException {

        String configPath = config.getInitParameter("configPath");
        InputStream resourceAsStream = DispatcherServlet.class.getClassLoader().getResourceAsStream(configPath);
        Properties properties = new Properties();
        properties.load(resourceAsStream);

        String componentScan = properties.getProperty("componentScan");
        String absolutePath = DispatcherServlet.class.getClassLoader().getResource("").getPath();

        File rooFile = new File(absolutePath+componentScan);
        recursionFile(rooFile);


    }

    /** 递归文件扫描*/
    private void recursionFile(File rooFile){
        if(rooFile.isDirectory()){
            File[] files = rooFile.listFiles();
            for (File file : files) {
                recursionFile(file);
            }
        }else if(rooFile.getName().endsWith("class")){
            String classAbsolutePath = rooFile.getAbsolutePath();
            classNameList.add(classAbsolutePath.substring(classAbsolutePath.indexOf("classes")+8,classAbsolutePath.length() - 6).replace("\\","."));
        }
    }

    /** 小写首字母*/
    private String lowerCaseFirstChar(String name){
        return name.substring(0,1).toLowerCase()+name.substring(1);
    }

}

@Data
public class Handler {

    private String url;

    private Object controller;

    private Method method;

    private Map<String,Integer> paramMapping = new HashMap<>();

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值