从Servlet到SpringMVC - 草稿

这个月老师教servlet,我去年就学完了,不过对于servlet,我有一点疑问

servlet的“缺点”?

在使用Servlet时,总是会写那几个固定的代码

@WebServlet("/UserListServlet")
public class UserListServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        this.doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)  {
        //...
    }
}

doGet,doPost这两个方法一直都要写?

在http协议中,定义了一系列的请求方式规范

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法

这好像与servlet无关,对于一个用户的增删改查操作要创建多个servlet类

SpringToolSuite4_OWRbNQqwDW.png

实际上,HttpServlet类中远远不止这两个方法:

idea64_9o1YHx3kmN.png

service()源代码是这样的:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }
                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }

可终究是有限的,比如获取用户信息,get方法就被占用,其他方法使用的意义不同,如果再来一个根据名称查询用户,根据id查询用户,该如何实现这两个接口功能?

这里的方法使用意义不同,指的是http请求方式规范
get表示请求资源,post表示提交资源,put表示更新资源,delete表示删除资源

只能创建新的servlet来应对,而这只是为了实现用户功能模块

一个复杂的项目何止这么一点功能?那岂不是得创建成百上千的servlet?

能不能一个类就能放所有用户功能方法,对外还能访问?

这是有解决方法的

反射解决

我们创建一个类,继承HttpServlet

这一步和正常的servlet没有什么不同,但是我们不实现doGetdoPost方法

覆盖父类的service()方法

public class BaseServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求路径 假如:"/项目名/user/add"
        String requestURI = req.getRequestURI();
        //获取请求的方法名 结果:"add"
        String methodName = requestURI.substring(requestURI.lastIndexOf("/") + 1);
        try {
            //获取方法对象
            Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            //执行方法 等于调用当前对象的add方法
            method.invoke(this, req,resp);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            //应该返回首页
            e.printStackTrace();
        }
    }
}

service()方法在servlet中被访问时默认调用的

可以看到,它内部实现了对于不同请求方式调用不同方法的代码

这一段代码对于反射知识点要求不高,但是对于java面对对象的知识要求扎实

请看下面例子:

msedge_d0FRUOOdwQ.png

蜂鸟类继承鸟类,鸟类觅食,的方法

注意: 觅食方法会调用这两个方法

所以蜂鸟也有这3个方法了

因为蜂鸟吃法比较独特,所以覆盖了父类(鸟类)的方法

创建一个蜂鸟,运行它的觅食方法,jvm虚拟机会在当前蜂鸟类中寻找觅食方法,没有找到就去父类中找,(父类没有就继续往父类的父类上找,直到找到)觅食方法,找到然后运行

所以,程序来到了鸟类觅食代码块里执行,觅食代码块调用了this.飞,this.吃

重点来了,当前的this对象实际是蜂鸟对象,所以jvm会重新回到蜂鸟对象中查找,发现没有方法,去父类中找,找到就执行.

方法蜂鸟类已经覆盖了,所以jvm在蜂鸟类中找到方法,然后执行了蜂鸟的的方法

BaseServlet就比作鸟类,只要有其他类继承它,service方法中就会反射调用本类中与请求路径中的名称一样的方法

使用效果,只需要继承这个类,并在映射路径的最后加上/*表示拦截所有相关请求即可

/**
 * 处理用户相关的请求
 */
@WebServlet("/user/*")
public class UserServlet extends BaseServlet {
    //添加用户功能
    public void regist(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //...
    }
    //登录功能
    public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //...
    }
    //查找当前用户功能
    public void findOne(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //从session中获取登录用户
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        User u = (User) user;
        writeValue(u,response);
    }
    //退出登录功能
    public void exit(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        session.invalidate();
        //跳转到登录页面
        response.sendRedirect(request.getContextPath() + "/login.html");
    }
    //激活账号功能
    public void active(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //....
    }
}

我可以这样访问

请求方式随意

localhost:8080/项目名/user/regist,它会访问到上面第7行的方法

localhost:8080/项目名/user/login,它会访问到上面第11行的方法

localhost:8080/项目名/user/findOne,它会访问到上面第15行的方法

localhost:8080/项目名/user/exit,它会访问到上面第23行的方法

localhost:8080/项目名/user/active,它会访问到上面第30行的方法

一个用户功能模块就在一个类里就能完成了,完成一个简单项目只需要几个servlet类即可

idea64_PoaKAexGFA.png

代码变得更好维护和管理了

SpringMVC的核心原理

不过,这些功能是有人实现了

它就是大名鼎鼎的spring家族中的springMVC

核心原理也是差不多,定义一个DispatcherServlet类,也是继承HttpServlet,但是拦截所有访问请求,并针对调用方法

我们只需要写一个类,加上一个2个注解就能使用了

在需要对外访问接口的方法上加上@RequestMapping注解或它的子注解们(@PostMapping,@GetMapping,@PutMapping….)

@Controller
@RequestMapping("/users")
public class UserController {
    @PostMapping()
    public Object Add(){
        //...
    }
    @GetMapping
    public Object CurrentUser(){
        //...
    }
    @GetMapping("/id")
    public Object findById(String id){
        //...
    }
    @GetMapping("/name")
    public Object findByName(String name){
        //...
    }
}

请求方式post: localhost:8080/项目名/users 访问到第5行的方法

请求方式get: localhost:8080/项目名/users 访问到第8行的方法

请求方式get: localhost:8080/项目名/users/id 访问到第13行的方法

请求方式get: localhost:8080/项目名/users/name 访问到第17行的方法

这只是springMVC功能的冰山一角,在这里也不过多描述

实际上,配置SpringMVC才是初学者较为麻烦的一件事

而在springboot中将配置这一难题直接解决,本身集成了Spring,SpringMVC,在其中定义对外功能接口写法也是如上面的SpringMVC一样

学无止境啊

--519计算机ZL原创编写,转载请表明出处

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容