手写注解实现SpringMVC底层原理【JAVA核心】

注:本文在转载基础上对代码以及注释进行了更新,确保代码正确可行

项目源码已经上传,欢迎下载~


1.项目整体架构

就建一个普通的java web项目就OK了,具体目录如下:


2.编码实现

2.1 自定义注解


(1).Controller注解

package com.heylink.annotation;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}
(2).Qualifier注解
package com.heylink.annotation;

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Qualifier {
    String value() default "";
}
 (3).RequestMapping注解
package com.heylink.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}
(4).Service注解
package com.heylink.annotation;

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)
public @interface Service {
    String value() default "";
}
2.2 Service模块


(1).MyService接口

package com.heylink.service;

import java.util.Map;

public interface MyService {

    int insert(Map map);

    int delete(Map map);

    int update(Map map);

    int select(Map map);
}
(2).MyServiceImpl类
package com.heylink.service.impl;

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

import java.util.Map;

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

    public int insert(Map map) {
        System.out.println("MyServiceImpl:" + "insert");
        return 0;
    }

    public int delete(Map map) {
        System.out.println("MyServiceImpl:" + "delete");
        return 0;
    }

    public int update(Map map) {
        System.out.println("MyServiceImpl:" + "update");
        return 0;
    }

    public int select(Map map) {
        System.out.println("MyServiceImpl:" + "select");
        return 0;
    }
}

 (3).SpringmvcService接口

package com.heylink.service;

import java.util.Map;

public interface SpringmvcService {

    int insert(Map map);

    int delete(Map map);

    int update(Map map);

    int select(Map map);
}
 (4).SpringmvcServiceImpl类
package com.heylink.service.impl;

import com.heylink.annotation.Service;
import com.heylink.service.SpringmvcService;

import java.util.Map;

/*
 * introductions:
 * created by Heylink on 2018/4/23 17:06
 */
@Service("SpringmvcServiceImpl")
public class SpringmvcServiceImpl implements SpringmvcService{

    @Override
    public int insert(Map map) {
        System.out.println("SpringmvcServiceImpl:" + "insert");
        return 0;
    }

    @Override
    public int delete(Map map) {
        System.out.println("SpringmvcServiceImpl:" + "delete");
        return 0;
    }

    @Override
    public int update(Map map) {
        System.out.println("SpringmvcServiceImpl:" + "update");
        return 0;
    }

    @Override
    public int select(Map map) {
        System.out.println("SpringmvcServiceImpl:" + "select");
        return 0;
    }
}

2.3 controller模块


(1).SpringmvcController类

package com.heylink.controller;

import com.heylink.annotation.Controller;
import com.heylink.annotation.Qualifier;
import com.heylink.annotation.RequestMapping;
import com.heylink.service.MyService;
import com.heylink.service.SpringmvcService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/*
 * introductions:
 * created by Heylink on 2018/4/23 17:24
 */
@Controller("test")
public class SpringmvcController {

    @Qualifier("MyServiceImpl")
    MyService myService;
    @Qualifier("SpringmvcServiceImpl")
    SpringmvcService springmvcService;

    @RequestMapping("insert")
    public String insert(HttpServletRequest request, HttpServletResponse response, String param) {
        myService.insert(null);
        springmvcService.insert(null);
        return null;
    }

    @RequestMapping("delete")
    public String delete(HttpServletRequest request, HttpServletResponse response, String param) {
        myService.delete(null);
        springmvcService.delete(null);
        return null;
    }

    @RequestMapping("update")
    public String update(HttpServletRequest request, HttpServletResponse response, String param) {
        myService.update(null);
        springmvcService.update(null);
        return null;
    }

    @RequestMapping("select")
    public String select(HttpServletRequest request, HttpServletResponse response, String param) {
        myService.select(null);
        springmvcService.select(null);
        return null;
    }
}
2.4 DispatcherServlet模块


package com.heylink.servlet;

import com.heylink.annotation.Controller;
import com.heylink.annotation.Qualifier;
import com.heylink.annotation.RequestMapping;
import com.heylink.annotation.Service;
import com.heylink.controller.SpringmvcController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.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;

/*
 * introductions:
 * created by Heylink on 2018/4/23 19:46
 */
public class DispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    List<String> packageNames = new ArrayList<>();
    // 所有类的实例,key是注解的value,value是所有类的实例
    Map<String, Object> instanceMap = new HashMap<>();
    Map<String, Object> handlerMap = new HashMap<>();

    public DispatcherServlet() {
        super();
    }

    public void init(ServletConfig config) throws ServletException {
        //包扫描,获取包中的文件
        scanPackage("com.heylink");
        //根据文件名查找controller与service的类,键为文件路径,值为类对象
        try {
            filterAndInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //对得到的controller键值对进行处理,键为方法路径,值为方法对象
        handleMap();
        //实现注入
        ioc();
    }

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

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException{
        String url = req.getRequestURI();
        /*
        *获取当前项目根地址
        * 比如你现在的url是127.0.0.1:8080/simplespringmvc/test/insert
        * tomcat配置的当前项目访问地址是127.0.0.1:8080/simplespringmvc
        * request.getContextPath()得到的就是127.0.0.1:8080/simplespringmvc
        * */
        String context = req.getContextPath();
        //同上,类似于拿到test/insert 相对路径
        String path = url.replace(context, "");
        //根据  /test/insert  就能拿到方法对象(注意有两个斜杠)
        Method method = (Method) handlerMap.get(path);
        //拿到 test对应的对象——也就是我们@Controller注解值为test的SpringmvcController类
        Object controllerObj = instanceMap.get(path.split("/")[1]);
        //因为是简单模拟,我们也知道就一个SpringmvcController,所以可以强转
        SpringmvcController controller = (SpringmvcController)controllerObj;

        try {
            method.invoke(controller, new Object[]{req, resp, null});
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    //实现依赖注入,其实就是给注解过的属性进行注入
    private void ioc() {
        if (instanceMap.size() <= 0) {
            return;
        }
        //遍历每一个键值对
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            //对每一个实例都去遍历其属性  !!切记这里要用getDeclaredFields
            for (Field field : entry.getValue().getClass().getDeclaredFields()) {
                //设置,可访问私有属性
                field.setAccessible(true);
                //对属性进行判断,是否有注解Qualifier
                if (field.isAnnotationPresent(Qualifier.class)){
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    //拿到注解的类名
                    String iocClazz = qualifier.value();
                    //将entry.getValue()这个对象中的field属性对象设为新值
                    try {
                        field.set(entry.getValue(), instanceMap.get(iocClazz));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        //???
        SpringmvcController springmvcController = (SpringmvcController) instanceMap.get("test");
        System.out.println(springmvcController);
    }

    //处理映射关系 controller/requestMapping将路径与对应的具体方法进行映射
    private void handleMap() {
        if (instanceMap.size() <= 0) {
            return;
        }
        //遍历每一个键值对
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            //寻找controller注解的类
            if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
                //拿到controller注解的value
                Controller controller = entry.getValue().getClass().getAnnotation(Controller.class);
                String firstPath = controller.value();
                //拿到该类下所有requestMapping注解的方法与RequestMapping中的值
                for (Method method : entry.getValue().getClass().getMethods()) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        String secondPath = requestMapping.value();
                        //将相对路径作为键,将方法对象作为值添加到map中
                        handlerMap.put("/" + firstPath + "/" + secondPath, method);
                    }
                }
            }
        }
    }

    //根据获得的文件进行过滤(选择Controller与Service注解的类)和实例化
    private void filterAndInstance() throws Exception {
        if (packageNames.size() <= 0) {
            return;
        }

        for (String pName : packageNames) {
            String cName = pName.replace(".class", "").trim();
            Class<?> clazz = Class.forName(cName);
            if (clazz.isAnnotationPresent(Controller.class)) {
                //如果是被Controller注解
                Object instance = clazz.newInstance();
                Controller controller = clazz.getAnnotation(Controller.class);
                String key = controller.value();
                instanceMap.put(key, instance);
            } else if (clazz.isAnnotationPresent(Service.class)) {
                //如果是被Service注解
                Object instance = clazz.newInstance();
                Service service = clazz.getAnnotation(Service.class);
                String key = service.value();
                instanceMap.put(key, instance);
            }
        }
    }

    //扫描包
    private void scanPackage(String packageName) {
        URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(packageName));
        String filePath = url.getFile();
        File file = new File(filePath);
        String[] childrenFileList = file.list();
        for (String path : childrenFileList) {
            File childrenFile = new File(filePath + path);
            if (childrenFile.isDirectory()) {
                //递归查找所有子文件
                scanPackage(packageName + "." + childrenFile.getName());
            }else {
                //将子文件添加到数组表中
                packageNames.add(packageName + "." + childrenFile.getName());
            }
        }
    }

    // 将所有的.转义获取对应的路径
    private String replaceTo(String path) {
        //将.转为/
        return path.replaceAll("\\.", "/");
    }
    
}

2.5 web模块


(1).web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://java.sun.com/xml/ns/javaee"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
          version="2.5">
        <servlet>
            <servlet-name>simpleServlet</servlet-name>
            <servlet-class>com.heylink.servlet.DispatcherServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>simpleServlet</servlet-name>
            <url-pattern>/*</url-pattern>
        </servlet-mapping>

</web-app>

3.运行代码

3.1 配置Tomcat

3.2 运行项目


3.3 输入网址

在浏览器中输入以下网址

http://localhost:8080/test/insert
http://localhost:8080/test/delete
http://localhost:8080/test/update
http://localhost:8080/test/select

3.4 查看控制台输出

结果正确


4. 思路介绍

(1).首先我们需要扫描包中的所有文件

(DispatcherServlet->init(ServletConfig config)->scanPackage("com.heylink"))

也就是含有注解的文件。然后将该包下的所有文件都存入packageNames集合中。

(2).这时我们拿到了包下所有的文件,但我们只需要含有我们指定注解的那部分文件,因此需要过滤出我们想要的文件即可,并且在过滤的过程中,我们可以将过滤出来的类通过Class.forName来直接实例化并储存起来。存放到instanceMap集合中,并为其设置对应的key值,该key值就是类注解的value。

(3).然后遍历instanceMap集合中的所有对象,获取指定注解的对象,并通过反射获取该对象的所有的方法,遍历所有的方法,将指定注解的方法存入handerMap,key为拼接字符串("/" + 对象变量名 + "/" + 方法名),value为方法(Method)。

(4).然后我们可以遍历instanceMap集合中的所有对象,通过反射获取对象的所有属性值(字段)集合,然后遍历属性值集合,将属性值含有指定注解的,通过Field的set方法为该属性值赋值,这时就将对象注入了。(也就是往Controller中注入Service对象)

(5).最后就可以通过反射的invoke方法调用某个对象的某个方法。(此时对象存于instanceMap,而方法都存于handerMap)

转载自 ->http://www.cnblogs.com/Shock-W/p/6617068.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值