手动实现简单MVC框架

SpringMVC大致原理

在这里插入图片描述

目录结构

在这里插入图片描述

pom文件

<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>org.example</groupId>
  <artifactId>CustomSpringMvc</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>CustomSpringMvc 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>11</maven.compiler.source>
    <maven.compiler.target>11</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.9</version>
    </dependency>
  </dependencies>

  <build>
    <finalName>CustomSpringMvc</finalName>
    <plugins>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>11</source>
          <target>11</target>
          <encoding>utf-8</encoding>
          <!--告诉编译器,编译的时候记录下形参的真实名称-->
          <compilerArgs>
            <arg>-parameters</arg>
          </compilerArgs>
        </configuration>
      </plugin>

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

mvc.properties

scanPackage=mvcdemo.demo

注解

Controller

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

Service

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

Autowired

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

RequestMapping

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

Controller

@Controller
@RequestMapping("/testController")
public class TestController {
    @Autowired
    private TestService testService;
    @RequestMapping("/test")
    public void test(String name){
        testService.query(name);
    }
}

Service

public interface TestService {
     void query(String name);
}

Impl

@Service
public class TestServiceImpl implements TestService {
    @Override
    public void query(String name) {
        System.out.println("姓名:"+name);
    }
}

Handler

public class Handler {
    private Object controller;
    private Method method;
//  url
    private Pattern pattern;
//  存放参数的位置,标记是参数顺序
    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<>();
    }
}

DispatherServlet

public class DispatherServlet extends HttpServlet {

    private Properties properties=new Properties();
    private List<String> classNameList=new ArrayList();
    private Map<String,Object> IOC=new HashMap<>();
    private List<Handler> handlerMapping=new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
//        加载配置文件 mvc.properties
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        doLoadConfig(contextConfigLocation);
//        扫描类,扫描注解
        doScan(properties.getProperty("scanPackage"));
//        初始化Bean对象
        doInstance();
//        依赖注入
        doAutowired();
//      构造一个handlerMapping处理器映射器,将配置好的url和method建立映射关系
        intiHandlerMapping();
        System.out.println("初始化完成!");

    }
//    url和method映射关系
    private void intiHandlerMapping() {
        if (IOC == null) {
            return;
        }
        for (Map.Entry<String,Object> entry:IOC.entrySet()){
//          获取class
            Class<?> aClass = entry.getValue().getClass();
//          判断是否是controller
           if(!aClass.isAnnotationPresent(Controller.class)){
               continue;
           }
//          RequestMapping
            if (aClass.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
//               获取url
                String value = annotation.value();
                Method[] methods = aClass.getMethods();
                for (int i = 0; i < methods.length; i++) {
                    if(methods[i].isAnnotationPresent(RequestMapping.class)){
                        RequestMapping annotationMethod = methods[i].getAnnotation(RequestMapping.class);
                        String annMethValue = annotationMethod.value();
//                        url路径
                        String path = value+annMethValue;
//                        封装为一个handler
                        Handler handler = new Handler(entry.getValue(), methods[i], Pattern.compile(path));
//                      获取参数的位置信息
                        Parameter[] parameters = methods[i].getParameters();
                        for (int j = 0; j < parameters.length; j++) {
                            Parameter parameter = parameters[j];
                            String name = parameter.getName();
                            handler.addParamIndexMapping(name,j);
                        }
                        handlerMapping.add(handler);
                    }
                }
            }
        }
    }

    //  依赖注入
    private void doAutowired() {
        if(IOC.size()==0){
            return;
        }
//     遍历ico
       for(Map.Entry<String,Object> entry: IOC.entrySet()){
           Object value = entry.getValue();
//         获取对象属性
           Field[] declaredFields = value.getClass().getDeclaredFields();
           for (int i = 0; i <declaredFields.length; i++) {
//             属性是否使用Autowired注解
               if (declaredFields[i].isAnnotationPresent(Autowired.class)) {
                   Object o = IOC.get(declaredFields[i].getType().getName());
                   if (null!=o) {
//                     打开强制赋值权限
                       declaredFields[i].setAccessible(true);
                       try {
                           declaredFields[i].set(value,o);
                       } catch (IllegalAccessException e) {
                           e.printStackTrace();
                       }
                   }
               }
           }
       }
    }

    //  实例化对象
    private void doInstance() {
        if (classNameList.size()==0){
            return;
        }
        for (String className:classNameList){
            // 反射
            try {
                Class<?> aClass = Class.forName(className);
//              Controller
                if (aClass.isAnnotationPresent(Controller.class)) {
                    String simpleName = aClass.getSimpleName();
                    Object o = aClass.newInstance();
                    IOC.put(simpleName,o);
                }
//                Service
                if (aClass.isAnnotationPresent(Service.class)) {
                    Service service = aClass.getAnnotation(Service.class);
                    Object o = aClass.newInstance();
//                  判断Service是否设置value,如果设置了value,key为value
                    if(!"".equals(service.value())){
                        IOC.put(service.value(),o);
                    }
//                  如果该类实现了接口,将接口全限定名称设置为key
                    Class<?>[] interfaces = aClass.getInterfaces();
                    if(interfaces.length>0){
                        for (int i = 0; i < interfaces.length; i++) {
                            Class<?> anInterface = interfaces[i];
                            String name = anInterface.getName();
                            IOC.put(name,o);
                        }
                    }
//                  将实现类的名称开头小写设置为key
                    String simpleName = aClass.getSimpleName();
                    String s = lowerFirst(simpleName);
                    IOC.put(s,o);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    private String lowerFirst(String simpleName) {
        char[] chars = simpleName.toCharArray();
        if('A' <= chars[0] && chars[0] <= 'Z') {
            chars[0] += 32;
        }
        return String.valueOf(chars);
    }

    //  扫描类
    private void doScan(String scanPackage) {
//      将路径转换为磁盘上的路径 mvcdemo.demo ——> mvcdemo/demo
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
//      获取文件
        File file = new File(path);
        File[] files = file.listFiles();
//      遍历获取包下所有类的全限定类名
        for (File fe:files){
//          子包
            if(fe.isDirectory()){
                // 递归
                doScan(scanPackage+"."+fe.getName());
            }else if(fe.getName().endsWith(".class")){
                String name = fe.getName().replaceAll(".class", "");
                String className = scanPackage + "." + name;
                classNameList.add(className);
            }
        }
    }


    //  加载配置文件
    private void doLoadConfig(String contextConfigLocation) {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//      根据url获取当前请求的handler
        Handler handler = getHandler(req);
        if(handler==null){
            resp.getWriter().write("404 not found");
           return;
        }
//      获取参数类型数组和参数数组的长度
        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
//      根据数组长度创建一个数组
        Object[] paramValues = new Object[parameterTypes.length];
//      获取传递过来的参数
        Map<String, String[]> parameterMap = req.getParameterMap();
//      遍历request中的参数
        for (Map.Entry<String,String[]> entry:parameterMap.entrySet()){
//          将参数[1,2] 转换为 1,2
            String[] value = entry.getValue();
            String join = StringUtils.join(value, ",");
//          如果匹配就填充数据
            if(!handler.getParamIndexMapping().containsKey(entry.getKey())){
                continue;
            }
//          找到参数对应的位置,赋值
            Integer integer = handler.getParamIndexMapping().get(entry.getKey());
            paramValues[integer]=join;
        }
        try {
            handler.getMethod().invoke(handler.getController(),paramValues);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    private Handler getHandler(HttpServletRequest rfq) {
        if (handlerMapping.isEmpty()) {
            return null;
        }
        String requestURI = rfq.getRequestURI();
        for (Handler handler : handlerMapping) {
            if (null != handler.getPattern()) {
                Matcher matcher = handler.getPattern().matcher(requestURI);
                if (matcher.matches()) {
                    return handler;
                }
            }
        }
        return null;
    }
}

WEB.xml

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>mvc</servlet-name>
    <servlet-class>mvcdemo.servlet.DispatherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>properties/mvc.properties</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvc</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

测试结果

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值