自定义框架(二)—— MVC 框架

一、MVC 概述

1、自定义MVC框架步骤

(1)自定义业务控制器,在业务控制器上添加@Controller@RequestMapping("请求地址")

@Controller
public class UserController {
	@RequestMapping("select")
	public String select() {
		System.out.println("执行查询");
		return "redirect:selectSuccess.html";
	}
	@RequestMapping("insert")
	public String insert() {
		System.out.println("执行新增");
		return "insertSuccess.html";
	}
}
  • @Controller 注解的作用是标记一个类为业务控制器
  • @RequestMapping 注解的作用是对方法以及执行该方法的请求进行映射

(2)在src下定义 mvc.xml 文件,该文件用于配置业务控制器的包名,便于框架扫描

<?xml version="1.0" encoding="UTF-8"?>
<mvc>
	<package>com.woniu.controller</package>
</mvc>

(3)定义监听器,读取 xml 文件获取包名,从服务器中获取该包中每一个类的类名

@WebListener
public class ContextListener implements ServletContextListener {
	@Override
	public void contextDestroyed(ServletContextEvent arg0) {
		// TODO Auto-generated method stub
	}

	@Override
	public void contextInitialized(ServletContextEvent event) {
		// 扫描某个 包中所有的类
		// 读取xml文件 获取用户存放业务控制器的包名
		SAXReader saxReader  =  new SAXReader();
		// 通过类加载器来获取类加载器加载java文件的根目录
		InputStream is  =  this.getClass().getClassLoader().getResourceAsStream("mvc.xml");
		try {
			Document document  =  saxReader.read(is);
			Element root  =  document.getRootElement();
			String pn = root.element("package").getText();
			//packageName将.替换为/
			String packageName = pn.replace(".", "/");
			//获取服务器java代码的跟路径
			URL url = this.getClass().getClassLoader().getResource("");
			String path = url.getPath();
			//去掉最前面的/
			path = path.substring(1);
			//将classes目录+包的目录组合成为包的绝对路径
			String pack = path+packageName;
			File dir = new File(pack);
			//取出该文件夹下所有的文件
			File[] files = dir.listFiles();
			//循环便利所有的文件 判断后缀是否是class后缀
			//定义键值对用于存储所有的请求地址映射信息
			Map<String,Mapping> map = new HashMap<String,Mapping>();
			for(File file:files) {
				if(file.getName().endsWith(".class")) {
					//条件成立说明该文件 是一个java类 扫描该java类上的注解
					//截取出文件的文件名不包括后缀
					String classPrefix = file.getName().substring(0,file.getName().lastIndexOf("."));
					String className = pn+"."+classPrefix;
					//获取类的信息
					Class c = Class.forName(className);
					//判断当前类上是否有@Controller注解
					if(c.isAnnotationPresent(Controller.class)) {
						//遍历所有的方法 
						Method[] methods = c.getDeclaredMethods();
						for(Method method:methods) {
							//检查方法上是否有@RequestMapping注解
							if(method.isAnnotationPresent(RequestMapping.class)) {
								//封装为Mapping对象
								Mapping mapping = new Mapping();
								mapping.setClassInfo(c);
								mapping.setMethod(method);
								mapping.setName(method.getAnnotation(RequestMapping.class).value());
								map.put(mapping.getName(), mapping);
							}
						}
					}
				}
			}
			//将键值对保存到application作用域中
			event.getServletContext().setAttribute("map", map);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

通过类名获取类的字节码信息,通过扫描每一个方法上的注解,将请求地址—类—方法形成一个映射信息封装为 Mapping 对象,将对象保存到一个键值对中,将键值对保存到 application 作用域中

(4)自定义过滤器或者是 Servlet,用来作为分发请求核心控制器

public class DisparcherServlet extends HttpServlet{
	@Override
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//获取请求地址
		String uri = request.getRequestURI();
		uri = uri.substring(uri.lastIndexOf("/")+1);
		String requestName = uri.substring(0,uri.lastIndexOf("."));
		//从application中取出键值对
		Map<String,Mapping> map = (Map<String, Mapping>) request.getServletContext().getAttribute("map");
		Mapping mapping = map.get(requestName);
		//通过映射信息中的classinfo实例化业务控制器
		Class c = mapping.getClassInfo();
		try {
			Object object = c.newInstance();
			Method method = mapping.getMethod();
			String result = (String) method.invoke(object, null);
			//判断业务控制器返回的字符串是否包含:
			if(result.indexOf(":") == -1) {
				request.getRequestDispatcher(result).forward(request, response);
			}else {
				if("redirect".equals(result.split(":")[0])) {
					response.sendRedirect(result.split(":")[1]);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

(5)在核心控制器中取出请求地址,通过请求地址名称获取该请求地址的映射信息,通过映射信息中的 classinfo 和 method 实现业务控制器方法的调用,在通过业务控制器返回的页面地址实现响应

public class Mapping {
	private String name;
	private Class classInfo;
	private Method method;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Class getClassInfo() {
		return classInfo;
	}
	public void setClassInfo(Class classInfo) {
		this.classInfo = classInfo;
	}
	public Method getMethod() {
		return method;
	}
	public void setMethod(Method method) {
		this.method = method;
	}
}

(6)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_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>mvc</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>servlet</servlet-name>
  	<servlet-class>com.woniu.mvc.servlet.DisparcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>servlet</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

(7)前端简单页面示例
① 发送请求

<body>
	<a href="select.do">测试查询</a>
	<a href="insert.do">测试新增</a>
</body>

② 查询成功返回页面 selectSuccess.html

<body>
	<h1>查询成功</h1>
</body>

③ 新增成功返回页面 insertSuccess.html

<body>
	<h1>新增成功</h1>
</body>
发布了104 篇原创文章 · 获赞 58 · 访问量 7518

猜你喜欢

转载自blog.csdn.net/baidu_27414099/article/details/104440504