手写SpringMVC框架

springmvc在项目中的作用

  1. 处理请求,把请求分发到不同的类和方法中
  2. ioc 依赖注入,创建实例。配置的方式和annotation的方式,今天实现annotation的方式
  3. aop 动态代理,事物控制,让程序员只专注于写自己的业务代码

步骤

  1. 定义包结构

    com.annotation
    com.controller
    com.service
    com.service.impl
    com.servlet
    
  2. 自定义注解

    元注解的作用就是负责注解其他注解
        1.@Target, :作用目标,作用在方法上、变量上、还是类上
        2.@Retention,用于描述注解的生命周期: 
           SOURCE:在源文件中有效
           CLASS:在class文件中有效
           RUNTIME:在运行时有效
        3.@Documented,javadoc此类的工具文档化
    自定义4个注解:Controller,Service,RequestMapping,Qualifier
    注解就是存储信息的,跟xml功能差不多,存储了信息后,程序读到注解上面的信息做相应的操作
    

    3、包扫描

    包扫描的目的
    根据basepackage,就是基包(代码中指的是com),扫描下面的子包以及子包下的类
    拿到包下的所有类文件后,我们就可以得到文件名
    有包名有文件名,我们就可以通过反射new出这些类的实例
    目的就是扫描基包下的所有的类文件,根据文件获取类的完整类名:包名+类名
    通过文件的方式去扫描
    

    4、把所有的类new出实例后,我们就要把类中的依赖关系注入进去

    拿到类的Class对象
    拿到field对象
    拿到field上面的annotation对象
    根据annotation对象拿annotation对象的属性
    把属性当key拿到map中的实例
    然后field。set把实例设置进去
    

    5、建立一个url与类中方法的映射关系

    其实是同样的做法
    拿到类的Class对象
    拿到Method对象
    拿到Method上面的annotation对象
    把url和method对象存到map中
    

代码

自定义注解

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE }) // 注解在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface Controller {
    String value() default "";
}

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.FIELD }) // 注解在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface Qualifier {
    String value() default "";
}

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.METHOD }) // 注解在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface ResquestMapping {
    String value() default "";
}

package com.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE }) // 注解在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented // 可以不写 用来文档打包的
public @interface Service {
    String value() default "";
}

包扫描 IOC 和 handlerMapping

package com.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

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

import com.annotation.Controller;
import com.annotation.Qualifier;
import com.annotation.ResquestMapping;
import com.annotation.Service;

public class DispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    // 定义基包下的文件的全限定名集合
    List<String> packageNames = new ArrayList<>();
    // 定义实例化对象的map key为注解的value value为类的实例
    Map<String, Object> instanceMap = new HashMap<>();
    // 定义路径与方法映射集合
    Map<String, Object> handlerMap = new HashMap<>();

    @Override
    public void init() throws ServletException {
        /* 包扫描 */
        // 根据basepackage,就是基包(代码中指的是com),扫描下面的子包以及子包下的类
        // 拿到包下的所有类文件后,我们就可以得到文件名
        // 有包名有文件名,我们就可以通过反射new出这些类的实例
        // 目的就是扫描基包下的所有的类文件,根据文件获取类的完整类名:包名+类名
        // 通过文件的方式去扫描
        System.out.println(this.getClass().getClassLoader().getResource("/"));// null
        System.out.println(this.getClass().getClassLoader().getResource(""));// file:/D:/Users/workspace-sts-3.7.1.RELEASE/SpringMVC/target/classes/
        System.out.println(this.getClass().getClassLoader().getResource("com"));// file:/D:/Users/workspace-sts-3.7.1.RELEASE/SpringMVC/target/classes/com
        System.out.println(DispatcherServlet.class.getClassLoader().getResource("/com"));// null
        scanPackage("com");// spring中 是在xml中配置的
        try {
            // 拿到类的Class对象
            filterAndInstance();
            // 依赖注入
            // 拿到field对象
            // 拿到field上面的annotation对象
            // 根据annotation对象拿annotation对象的属性
            // 把属性当key拿到map中的实例
            // 然后field。set把实例设置进去
            ioc();
            // 建立路径方法映射
            handlerMap();

            // 打印实例化对象的map
            for (Entry<String, Object> entry : instanceMap.entrySet()) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
            }

            // 打印路径与方法映射集合map
            for (Entry<String, Object> entry : handlerMap.entrySet()) {
                System.out.println(entry.getKey() + ":" + entry.getValue());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @Title: ioc
     * @Description: 路径方法映射
     */
    private void handlerMap() {
        if (instanceMap.isEmpty()) {
            return;
        }
        // 遍历 实例集合 取得方法 包含 RequestMapping 注解
        for (Entry<String, Object> entry : instanceMap.entrySet()) {
            if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
                Controller controllerAn = entry.getValue().getClass().getAnnotation(Controller.class);
                String ctvalue = controllerAn.value();
                Method[] methods = entry.getValue().getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(ResquestMapping.class)) {
                        ResquestMapping annotation = method.getAnnotation(ResquestMapping.class);
                        String rmvalue = annotation.value();
                        handlerMap.put("/" + ctvalue + "/" + rmvalue, method);
                    }
                }
            }
        }
    }

    /**
     * @Title: ioc
     * @Description: 属性依赖注入
     */
    private void ioc() throws IllegalArgumentException, IllegalAccessException {
        // 如果 map 为空 说明没有实例化对象 返回
        if (instanceMap.isEmpty()) {
            return;
        }
        // 遍历 实例集合
        for (Entry<String, Object> entry : instanceMap.entrySet()) {
            if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
                // 获取 当前实例的字段
                Field[] fields = entry.getValue().getClass().getDeclaredFields();
                for (Field field : fields) {
                    field.setAccessible(true);
                    // 如果包含指定注解 那么通过该注解的 value 向正在遍历的实例化对象中注入 指定对象
                    if (field.isAnnotationPresent(Qualifier.class)) {
                        Qualifier annotation = field.getAnnotation(Qualifier.class);
                        String value = annotation.value();
                        field.set(entry.getValue(), instanceMap.get(value));

                    }
                }
            }
        }
    }

    /**
     * @Title: filterAndInstance
     * @Description: 定义过滤方法 添加需要实例化的类到instanceMap
     */
    private void filterAndInstance() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (packageNames.isEmpty()) {
            return;
        }

        for (String className : packageNames) {
            // 反射 获取对象 className去掉.class
            Class<?> clazz = Class.forName(className.replace(".class", ""));
            // 根据注解判断是否需要实例化
            if (clazz.isAnnotationPresent(Controller.class)) {
                Object instance = clazz.newInstance();
                Controller annotation = clazz.getAnnotation(Controller.class);
                String key = annotation.value();
                instanceMap.put(key, instance);
            } else if (clazz.isAnnotationPresent(Service.class)) {
                Object instance = clazz.newInstance();
                Service annotation = clazz.getAnnotation(Service.class);
                String key = annotation.value();
                instanceMap.put(key, instance);
            }
        }
    }

    /**
     * @Title: scanPackage
     * @Description: 定义包扫描方法
     */
    private void scanPackage(String basepackage) {
        // 将基包中的.转换为/ 才能遍历其下的文件
        URL url = this.getClass().getClassLoader().getResource(replaceTo(basepackage));
        String pathFile = url.getFile();// 得到绝对路径的字符串
        File file = new File(pathFile);
        String[] files = file.list();

        for (String path : files) {
            File eachFile = new File(pathFile + "/" + path);
            if (eachFile.isDirectory()) {
                // 递归迭代
                scanPackage(basepackage + "." + eachFile.getName());
            } else {
                // 是文件 添加其全类名
                // 返回 com.xxx.class
                System.out.println("初始加载基包下的所有文件: " + basepackage + "." + eachFile.getName());
                packageNames.add(basepackage + "." + eachFile.getName());
            }
        }
    }

    /**
     * @Title: replaceTo
     * @Description: 将路径中的 "." 转换为 "/"
     */
    private String replaceTo(String path) {
        return path.replaceAll("\\.", "/");
    }

    @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 {
        String uri = req.getRequestURI();
        // 获取工程名字
        String contextPath = req.getContextPath();
        // 截取uri 去掉工程名字 /index/insert
        String path = uri.replace(contextPath, "");

        Method method = (Method) handlerMap.get(path);

        try {
            // path.split("/")=["",index,insert]
            method.invoke(instanceMap.get(path.split("/")[1]), new Object[] { req, resp, null });
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

controller 和 service

package com.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.annotation.Controller;
import com.annotation.Qualifier;
import com.annotation.ResquestMapping;
import com.service.IndexService;
import com.service.MyService;

//设计这里的index 也是相当于springMVC中RequestMapping的作用
@Controller("index")
public class IndexController {
    @Qualifier("myServiceImpl")
    private MyService myService;
    @Qualifier("indexServiceImpl")
    private IndexService indexService;

    @ResquestMapping("insert") // 不能带有"/" 设计不带
    public String insert(HttpServletRequest request, //
            HttpServletResponse response, //
            String param) {
        // URL: http://localhost:8080/SpringMVC/index/update
        // URI: /SpringMVC/index/update
        System.err.println("URL: " + request.getRequestURL());
        System.err.println("URI: " + request.getRequestURI());

        myService.insert();
        indexService.insert();
        return null;
    }

    @ResquestMapping("delete") // 不能带有"/" 设计不带
    public String delete(HttpServletRequest request, //
            HttpServletResponse response, //
            String param) {
        System.err.println("URL: " + request.getRequestURL());
        System.err.println("URI: " + request.getRequestURI());

        myService.delete();
        indexService.delete();
        return null;
    }

    @ResquestMapping("update") // 不能带有"/" 设计不带
    public String update(HttpServletRequest request, //
            HttpServletResponse response, //
            String param) {
        System.err.println("URL: " + request.getRequestURL());
        System.err.println("URI: " + request.getRequestURI());

        myService.update();
        indexService.update();
        return null;
    }

    @ResquestMapping("select") // 不能带有"/" 设计不带
    public String select(HttpServletRequest request, //
            HttpServletResponse response, //
            String param) {
        System.err.println("URL: " + request.getRequestURL());
        System.err.println("URI: " + request.getRequestURI());

        myService.select();
        indexService.select();
        return null;
    }

}
package com.service;

public interface IndexService {
    int insert();

    int delete();

    int update();

    int select();
}

package com.service;

public interface MyService {
    int insert();

    int delete();

    int update();

    int select();
}
package com.service.impl;

import com.annotation.Service;
import com.service.IndexService;

@Service("indexServiceImpl")
public class IndexServiceImpl implements IndexService {

    @Override
    public int insert() {
        System.out.println("IndexServiceImpl的insert");
        return 0;
    }

    @Override
    public int delete() {
        System.out.println("IndexServiceImpl的delete");
        return 0;
    }

    @Override
    public int update() {
        System.out.println("IndexServiceImpl的update");
        return 0;
    }

    @Override
    public int select() {
        System.out.println("IndexServiceImpl的select");
        return 0;
    }

}

package com.service.impl;

import com.annotation.Service;
import com.service.MyService;

@Service("myServiceImpl")
public class MyServiceImpl implements MyService {

    @Override
    public int insert() {
        System.out.println("MyServiceImpl的insert");
        return 0;
    }

    @Override
    public int delete() {
        System.out.println("MyServiceImpl的delete");
        return 0;
    }

    @Override
    public int update() {
        System.out.println("MyServiceImpl的update");
        return 0;
    }

    @Override
    public int select() {
        System.out.println("MyServiceImpl的select");
        return 0;
    }

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <!-- 自定义前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>com.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!-- 1:*.do *.action 拦截以.do结尾的请求 (不拦截 jsp png jpg .js .css) 2:/ 拦截所有请求 (不拦截.jsp) 建议使用此种 方式 (拦截 .js.css .png) (放行静态资源) 3:/* 拦截所有请求(包括.jsp) 此种方式 不建议使用 -->
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

OK 大功告成!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值