实现一个简单的MVC框架——SmartMVC

实现一个简单的MVC框架——SmartMVC

1.SmartMVC是什么?

是一个类似于SpringMVC的web应用开发框架,其核心是一个通用的控制器。
利用SmartMVC可以开发基于MVC架构的web应用。

2.SmartMVC的架构

在这里插入图片描述

3.主要组件及其关系

在这里插入图片描述

4.具体实施

该项目主要实现功能:欢迎页面和简单注册登录功能

准备阶段

  • 新建一个maven项目—smartMVC,生成web.xml文件,部署到Tomcat上

  • 前端.jsp页面,放在webapp/WEB-INF/目录下,用户无法直接访问

  • 导入依赖包

    <dependencies>
    		<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
    		<dependency>
    			<groupId>dom4j</groupId>
    			<artifactId>dom4j</artifactId>
    			<version>1.6.1</version>
    		</dependency>
    	</dependencies>
    
  • 自定义注解

    package core.annotation;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RequestMapping {
    	public String value();
    }
    
  • 处理器类(Controller):

    负责业务逻辑的处理。对于比较复杂的业务,也可以调用其它的类来处理。

    package controller;
    
    import core.annotation.*;
    
    public class HelloController {
    	
    	@RequestMapping("/hello.do")
    	public String hello(){
    		return "hello";
    	}
    	
    }
    
    package controller;
    
    import javax.servlet.http.HttpServletRequest;
    
    import core.annotation.RequestMapping;
    
    public class LoginController {
    	
    	@RequestMapping("/toLogin.do")
    	public String toLogin() {
    		return "login";
    	}
    	
    	@RequestMapping("/login.do")
    	public String login(HttpServletRequest request) {
    		
    		//获得用户名和密码
    		String username = request.getParameter("username");
    		System.out.println("username:" + username);
    		String pwd = request.getParameter("pwd");
    		if("root".equals(username) && "123".equals(pwd)) {
    			//登录成功
    			return "success";
    		}else {
    			//登录失败
    			request.setAttribute("erro_info", "用户名或密码错误");
    			return "login";
    		}
    	}
    }
    
    
  • web.xml配置文件,容器启动时,前端控制器实例化

    <servlet>
        <description></description>
        <display-name>DispatchServlet</display-name>
        <servlet-name>DispatchServlet</servlet-name>
        <servlet-class>core.web.DispatchServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>DispatchServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    
  • smartmvc.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
    	<bean class="controller.HelloController"></bean>
    	<bean class="controller.LoginController"></bean>
    </beans>
    
  • 核心部分——前端控制器(DispatchServlet)

    读取smartmvc.xml配置文件当中配置的处理器类名,然后将处理器实例化。

    接下来,将处理器实例交给HandlerMapping来处理。

    package core.web;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import core.common.Handler;
    import core.common.HandlerMapping;
    
    public class DispatchServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	private HandlerMapping hmapping;
    
    	public void init() throws ServletException {
    		SAXReader reader = new SAXReader();
    		InputStream in = getClass().getClassLoader().getResourceAsStream("smartMVC.xml");
    		try {
    			Document doc = reader.read(in);
    			Element root = doc.getRootElement();
    			List<Element> list = root.elements();
    			List<Object> beans=new ArrayList<>();
    			for(Element ele: list){
    				String className = ele.attributeValue("class");
    				Class<?> c = Class.forName(className);
    				Object obj = c.newInstance();
    				beans.add(obj);
    			}
    			hmapping=new HandlerMapping();
    			hmapping.process(beans);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		request.setCharacterEncoding("utf-8");
            //获得请求资源路径
    		String uri = request.getRequestURI();
    		System.out.println(uri);
            //获得应用名
    		String contextPath = request.getContextPath();
    		System.out.println(contextPath);
            //将请求资源路径中的应用名除掉,生成请求路径
    		String path = uri.substring(contextPath.length());
    		System.out.println("path:"+path);
            //依据请求路径,获得对应的处理器
    		Handler handler = hmapping.getHandler(path.trim());
    		System.out.println("handler:"+handler);
    		if(handler==null){
    			response.sendError(404);
    			return;
    		}
    		Method mt = handler.getMt();
    		Object obj = handler.getObj();
    		Object result=null;
    		try {
                /*
    			 * 调用处理器的方法之前,先看
    			 * 处理器的方法是否带参,如果带有
    			 * 参数(当前版本只支持request和response),
    			 * 则将相应的参数值传递给处理器对应的方法。
    			 */
    			Class<?>[] types = mt.getParameterTypes();
    			if(types.length==0){
    				result = mt.invoke(obj);
    			}else{
    				Object[] params=new Object[types.length];
    				for (int i = 0; i < types.length; i++) {
    					if(types[i]==HttpServletRequest.class){
    						params[i]=request;
    					}
    					if(types[i]==HttpServletResponse.class){
    						params[i]=response;
    					}
    				}
    				result=mt.invoke(obj, params);
    			}
    			System.out.println(result);
                //获得视图名
    			String viewName=result.toString();
                /*
    			 * 处理视图名。
    			 * 默认转发到"/WEB-INF/" + 视图名 
    			 * +".jsp"。
    			 */
    			request.getRequestDispatcher("/WEBINF/"+viewName+".jsp").forward(request, response);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
  • 映射处理器类(HandlerMapping):

    负责提供请求路径与处理器的对应关系。

    package core.common;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    
    import core.annotation.RequestMapping;
    
    public class HandlerMapping {
    	/*
    	 * maps用于存放请求路径与处理器的对应关系。
    	 * 为了方便利用java反射去调用处理器的方法,
    	 * 将处理器和Method对象封装成了Handler对象。
    	 */
    	private Map<String,Handler> maps=new HashMap<String,Handler>();
    	
        /**
    	 * 依据请求路径,返回对应的Handler对象。
    	 */
    	public Handler getHandler(String path){
    		return maps.get(path);
    	}
    	
    	/**
    	 * 负责建立请求路径与处理器的对应关系。
    	 * 比如"/hello.do"由HelloController的hello方法
    	 * 来处理。
    	 */
    	public void process(List<Object> beans) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    		for(Object obj:beans){
                //获得class对象
    			Class c = obj.getClass();
                //获得所有方法
    			Method[] methods = c.getDeclaredMethods();
    			for(Method mt:methods){
                    //获得加在方法前的@RequestMapping
    				RequestMapping requestmapping = mt.getAnnotation(RequestMapping.class);
                    //获得@RequestMapping的value属性值
    				String value = requestmapping.value();
    				System.out.println(value);
                    //将请求路径与处理器的对应关系保存下来
    				maps.put(value, new Handler(obj,mt));
    			}
    		}
    		Set<Entry<String, Handler>> set = maps.entrySet();
    		for(Entry<String,Handler> e:set){
    			String key = e.getKey();
    			Handler value = e.getValue();
    			System.out.println(key+":"+value);
    		}
    	}
    }
    
  • 类Handler,封装了处理器实例和相应的Method对象。

    package core.common;
    
    import java.lang.reflect.Method;
    
    public class Handler {
    	private Object obj;
    	private Method mh;
    	
    	public Handler(Object obj, Method mh) {
    		this.obj = obj;
    		this.mh = mh;
    	}
    	
    	public Object getObj() {
    		return obj;
    	}
    	public void setObj(Object obj) {
    		this.obj = obj;
    	}
    	public Method getMh() {
    		return mh;
    	}
    	public void setMh(Method mh) {
    		this.mh = mh;
    	}
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值