DispatcherServlet 中央调度器的由来,手写简单的DispatcherServlet

DispatcherServlet 中央调度器的由来,手写简单的DispatcherServlet,第1张

Servlet 版本一:

在不使用 DispatcherServlet 之前我们通常在 web.xml 文件中注册 Servlet 接口实现类来完成对客户请求的处理,且每一个请求对应一个 Servlet接口实现类


    Demo01Servlet
    com.Servlet.www.Demo01Servlet


    Demo01Servlet
    /demo01

同时每个Servlet 接口实现类都需要继承 HttpServlet 并重写 doGet 和 doPost 方法,

public class Demo01Servlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws  ServletException, IOException {
        //处理get请求,获取参数
        String user = req.getParameter("user");
	}
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理post请求,获取参数
		String user = req.getParameter("user");
	}
}

如果这样配置,如果 Servlet 的数目少还可以接受,否则会导致配置繁琐且 Servlet的数目过多,我们会写一大堆的Sevlet 类以及 getParameter("") 来获取参数。有没有更好的方法呢??

Servlet 版本二:

为了避免 Servlet 的数目过多,我们把对同一个表的不同 *** 作请求都对应一个 Servlet,如:IndexServlet /AddUserServlet /EditUserServlet /DelUserServlet /UpdateUserServlet -> 合并成 UserServlet,同时把请求都指向 UserServlet,通过在请求体中添加 operate 键值对指明请求方法使用 switch-case 通过 operate 的值来决定调用 UserServlet 中的哪一个方法。

String operate = req.getParameter("operate");
方法一:通过switch方法:适用于方法少的 Servlet 类
switch(operate){
	case"index":   index(req,resp);  break;
	case"add":     add(req,resp);    break;
	case"del":     del(req,resp);    break;
	case"edit":    edit(req,resp);   break;
	case"update":  update(req,resp); break;
	default:       throw  new RuntimeException("operate错误");
}
Servlet 版本三:

在上一个版本中, UserServlet 中充斥着大量的 switch-case,如果项目的业务规模扩大,那么会有很多的 Servlet 方法,也就意味着会有很多的 switch-case,这是一种代码冗余。所以我们可以在 UserServlet 中使用反射技术,通过反射获取 UserServlet 的所有方法,并一一于 operate 的值进行对比,如果operate 的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,如果找不到对应的方法,则抛异常

String  operate = req.getParameter("operate");
Method[] methods = this.getClass().getDeclaredMethods();
label:while(true){
	for(Method method : methods){
		String name = method.getName();
		if(operate.equals(name)){
			try{
				method.invoke(this,req,resp);
				break label;
			}catch(IllegalAccessException e){
				e.printStackTrace();
			}catch(InvocationTargetException e){
				e.printStackTrace();
			}
		}
	}
	throw new RuntimeException("operate错误");
}
DispatcherServlet :

在 Servlet 版本三中我们使用了反射技术,但是其实还是存在一定的问题:每一个Servlet中都有类似的反射技术的代码。存在代码冗余,因此继续抽取,设计了中央控制器类:DispatcherServlet

DispatcherServlet 这个类的工作分为两大部分:

1.根据URL定位到能够处理这个请求的 controller组件:
从URL中提取servletPath : /user-> user
String  servletPath = req.getServletPath();
servletPath = servletPath.substring(1);

根据 user 找到对应的组件: UserController , 这个对应的依据我们存储在applicationContext.xml中



	

通过DOM技术我们去解析 applicationContext.xml 文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件

private Map beanMap = new HashMap<>();
@Override
public void init(){
try{
	InputStream applicationContext = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
	DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
	Documentdocument = documentBuilder.parse(applicationContext);
	NodeList beansNodeList = document.getElementsByTagName("bean");
	
	for(int i=0; i

 根据获取到的 operate 的值定位到我们 UserController中需要调用的方法

Object obj = beanMap.get(servletPath);

Stringoperate=req.getParameter("operate");
if(Utils.isEmpty(operate)){
	operate="index";
}
try{
	Method method=obj.getClass().getDeclaredMethod(operate,HttpServletRequest.class,HttpServletResponse.class);
	if(method!=null){
		method.setAccessible(true);
		method.invoke(obj,req,resp);
	}else{
		throw new RuntimeException("operate错误");
	}
}catch(NoSuchMethodExceptione){
	e.printStackTrace();
}catch(IllegalAccessExceptione){
	e.printStackTrace();
}catch(InvocationTargetExceptione){
	e.printStackTrace();
}
2.调用Controller组件中的方法:

这个时候我们要考虑一个问题,如果方法需要参数怎么办?

我们可以通过反射来获取要调用的方法的参数签名信息,Parameter[] parameters = method.getParameters();

通过parameter.getName()获取参数的名称,参数名称默认是arg0...样式,因为在编译字节码文件时,舍弃了参数的名称,此时需要在构建执行部署——>编译器——>java编译器中输入parameters;

准备了Object[] parameterValues 这个数组用来存放对应参数的参数值。

另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型。

@Override
protected void service( HttpServletRequest req , HttpServletResponse resp ) throws ServletException , IOException{
	req.setCharacterEncoding("UTF-8");
	//获取servlet路径
	String servletPath = req.getServletPath();
	servletPath = servletPath.substring(1);
	//通过servlet路径获取相应的对象
	Object obj = beanMap.get(servletPath);
	String operate = req.getParameter("operate");
	if(Utils.isEmpty(operate)){
		operate="index";
	}
	try{
		//通过servlet对象获取servlet对象全部方法
		Method[] declaredMethods = obj.getClass().getDeclaredMethods();
		for(Method method: declaredMethods){
			if(operate.equals(method.getName())){
				if(method!=null){
					//获取请求参数
					Parameter[] parameters = method.getParameters();
					Object[] parametersValues = new Object[parameters.length];
					for(int i=0;i

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/872183.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)