用spring mvc框架的模拟实现来学习反射和注解

本文通过模拟Spring MVC框架的核心部分,探讨反射和注解在框架中的应用。详细介绍了Controller、Service、Qualifier及HandlerMapping注解的使用,并提供了一步步构建请求映射的流程。内容包括全包扫描、注解处理、类实例化以及请求URL与方法的映射。通过此模拟实现,读者可以深入理解Spring MVC的内部工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、springmvc架构
这里写图片描述

2、相应注解详解
2.1、Controller注解 Controlle类上面的注解
2.2、Service注解 业务层对象上面的注解
2.33、Qualitify注解 在controller层里面,扫面含有此注解的字段,然后根据字段注入Service bean对象
2.44、HandlerMapping注解 处理方法上面的注解

3、编写流程(这里我们主要写框架的核心部分,所以不能做到像springmvc那样详尽):

1、先定义上面四个注解
2、全包扫描(在init()方法里面完成)
3、将扫描到含有Controller和service注解的类保存到一个map集合(classMap),key为当前注解的value值,value为当前类的实例对象。
4、将扫描到的Qualitify注解的字段,注入相应的值,值为classMap.get(key),其中key的值是扫描到Qualitify的value值。因为此key就是对应service注解的实例对象。
5、最后将要扫描Controller注解,然后在扫描handlerMapping注解,这一步为了构建请求地址,并且通过请求地址可以找到对应的方法的method对象和Controller注解对应类的实例对象。
首先,找到controller对象,通过集合(classmap)调用get(controller注解的value)方法得到对应的Controller类的实例对象,接下来扫描的是方法上面的注解,如果方法上面有HandlerMapping注解,然后构建ServletContext(项目名称,例如/上下文路径/资源具体路径)后面的url,如何构建呢?即Controller注解的value值加上HandllerMapping注解的value值,然后,在将此url作为key,然后扫描到的方法的Method对象作为value保存到一个Map(methodMap)集合,并且将此url作为key,controller注解对应的实例对象作为value,保存在classMap集合中。
6、此时就可以通过请求的url后面的路径映射到一个相应的method对象并且得到相应的controller类的实例。

代码部分:
包结构:
这里写图片描述

代码:

/**
 * Controller注解
 * 
 * */
@Target({ElementType.TYPE})//作用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}
/**
 * Quatifier注解
 * 
 * */
@Target({ElementType.FIELD})//作用在字段上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Quatifier {
    String value() default "";
}
/**
 * RequestMapping注解
 * 
 * */
@Target({ElementType.METHOD})//作用在方法上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}
/**
 * Servic注解
 * 
 * */
@Target({ElementType.TYPE})//作用在类上
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}
@Controller("SpringMvcController")
public class SpringMvcController {
    /**
     * 把UserService实例对象注入进来
     * */
    @Quatifier("UserServiceImpl")
    private UserService us;

    @RequestMapping("insert.action")  
    public String insert(HttpServletRequest request, HttpServletResponse response, String param) {  
        us.insert(null);  
        try {
            request.getRequestDispatcher("/index.jsp").forward(request, response);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ServletException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;  
    }  

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

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

    @RequestMapping("select.action")  
    public String select(HttpServletRequest request, HttpServletResponse response, String param) {  
         us.select(null);  
        return null;  
    }  
}
public interface UserService {
     int insert(Map map);  

     int delete(Map map);  

     int update(Map map);  

     int select(Map map);  
}
@Service("UserServiceImpl")
public class UserServiceImpl implements UserService{

    @Override
    public int insert(Map map) {
        System.out.println("调用了插入方法");
        return 0;
    }

    @Override
    public int delete(Map map) {
        System.out.println("调用了删除方法");
        return 0;
    }

    @Override
    public int update(Map map) {
        System.out.println("调用了更新方法");
        return 0;
    }

    @Override
    public int select(Map map) {
        System.out.println("调用了查询方法");
        return 0;
    }

}

核心控制器,只需在web.xml文件中配置它就可以了。

public class DispatcherServlet extends HttpServlet{

    private static final long serialVersionUID = 1L;
    //保存扫描到的所有包名
     List<String> packageNames = new ArrayList<String>();  
    //所有类的实例,key是注解的value,value是注解对应的类的实例对象
    Map<String, Object> instanceMap = new HashMap<String, Object>();
    /**
     * 处理器映射到的方法对象集合
     * key是地址栏上项目上下文后面接上的地址
     * value是对应的方法对象也就是Method对象
     * */
    Map<String, Object> handerMap = new HashMap<String, Object>();
    @Override
    public void init() throws ServletException {
        //扫描包中的文件 
        scanPackage("com.bjsxt.springmvc");

        try {
            //将注解和对应的实例对象保存在集合当中
            filterAndInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //建立映射关系
        handderMap();
        //依赖注入
        ioc();
    }
    //建立映射关系
    private void handderMap() {
        if (instanceMap.size() <= 0) {
            return;
        }
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            //判断类上是否有Controller注解,在做下一步操作
            if (entry.getValue().getClass().isAnnotationPresent(Controller.class)) {
                Controller controller = (Controller)entry.getValue().getClass().getAnnotation(Controller.class);
                String cvalue = controller.value();
                //拿到controller注解对应的对象
                Object ctlObj = instanceMap.get(cvalue);
                Method[] methods = entry.getValue().getClass().getMethods();
                for (Method method : methods) {
                    //迭代方法,判断方法上面是否有requestMapping注解
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        String value = ((RequestMapping)method.getAnnotation(RequestMapping.class)).value();
                        String urlKey = "/"+cvalue+"/"+value;
                        //将地址和方法对象建立关系
                        handerMap.put(urlKey, method);
                        //将地址和对象也建立映射关系。
                        instanceMap.put(urlKey, ctlObj);
                    }else{
                        continue;
                    }
                }

            }
        }
    }
    //属性的注入
    private void ioc() {
        //如果集合为空,直接跳出此方法的执行
        if (instanceMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            //迭代所有注解
            for (Field field : fields) {
                field.setAccessible(true);
                //判断字段上面是否有Quatifier注解
                if (field.isAnnotationPresent(Quatifier.class)) {
                    //拿到注解
                    Quatifier quatifier = field.getAnnotation(Quatifier.class);
                    //拿到注解对应的value值
                    String value = quatifier.value();
                    try {
                        //注入相应的值
                        field.set(entry.getValue(),instanceMap.get(value));
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }
    /**
     * 扫描包下的所有文件
     * */
    private void scanPackage(String packagePath) {
        String loc = "/"+replaceTo(packagePath);
        System.out.println("扫描的地址:"+loc);
        URL url = this.getClass().getClassLoader().getResource(loc);
        System.out.println("url"+url);
        String filePath = url.getFile();
        File f = new File(filePath);
        String[] fileList = f.list();
        //迭代fileList
        for (String path : fileList) {
            File eachFile = new File(filePath+"/"+path);
            //判断是否是一个目录
            if (eachFile.isDirectory()) {
                //到这里说明他是一个目录,我们还需要对子目录进行扫描
                scanPackage(packagePath+"."+eachFile.getName());
            }else{ 
                //获得加后缀名的完整包名
                String packName = packagePath+"."+eachFile.getName();
                //去除文件后缀名,可得到包名+类名
                String pName = packName.substring(0, packName.lastIndexOf(".class"));
                packageNames.add(pName);
                System.out.println("所有的包名加类名:"+packageNames);
            }
        }


    }
    //扫描
     private void filterAndInstance()throws Exception {  
         if (packageNames.size() <=0) {
            return;
        }
         for (String className : packageNames) {
             //加载实例
            Class<?> cName = Class.forName(className.replace(".class", "").trim());
            //判断是否有Controller注解
            if (cName.isAnnotationPresent(Controller.class)) {
                Object instance = cName.newInstance();
                //获得注解对象
                Controller controller = (Controller) cName.getAnnotation(Controller.class);
                String key = controller.value();
                //把注解的value的值作为key,对应的实例对象作为value
                instanceMap.put(key, instance);
                //判断是否有Service注解
            }else if(cName.isAnnotationPresent(Service.class)){
                Object instance = cName.newInstance();
                Service service = (Service)cName.getAnnotation(Service.class);
                String key = service.value();
                instanceMap.put(key, instance);
            }
            else{
                continue;
            }
        }
     }
    //将所有的 . 转换成 /
    private String replaceTo(String packagePath) {
        return packagePath.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 url = req.getRequestURI();
        System.out.println("uri的路径"+url);
        //获得上下文路径,通常是项目名
        String context = req.getContextPath();
        //去掉上下文
        String path = url.replace(context, "");
        System.out.println(path);
        Method m = (Method) handerMap.get(path);
        System.out.println(m);
        //打印地址栏上去掉contextPath之后的url
        System.out.println(path);
        try {
            //获得路径对应到的controller对象
            Object obj = instanceMap.get(path);
            System.out.println(obj);
            //通过反射调用方法
            m.invoke(obj, req,resp,null);
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace(); 
        }


    }

}

web.xml

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>com.bjsxt.springmvc.servlet.DispatcherServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Anguser

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值