Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目

Posted ChinaRainbowSea

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目相关的知识,希望对你有一定的参考价值。

Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目

每博一文案

有句谚语说:“一怒之下踢石头,只有痛着脚趾头。”
比一件糟糕的事情更可拍的,是你用糟糕的态度去面对它。看过一个很有意思的故事:
有个男人清早洗漱的时候,把自己的手表放在了桌子上。他的儿子不小心把手表碰倒地上摔坏了,男人
气得儿子揍了一顿,还埋怨妻子没看好儿子,两个人吵了起来。
男人气急败坏地摔门出去,路上想起有一份重要文件忘记带了,他匆忙回家取。可没有人在家,
他只得打电话让妻子回来送钥匙。妻子赶回家时,不小心撞翻了路边的一个小吃摊,赔了一笔钱。
男人没等到妻子回家,因为迟到也遭受了罚款。
费斯汀格法则认为:“10%的生活,由发生在你身上的事情组成,而另外的90%,则取决于你做出的反应。”
这个故事中。摔坏手表就是其中10%,而后面的一系列的事情则是另外的90%,是由埋怨引起的。
面对人生得失时,一颗平常心,比一百种智慧更有力量。
人这一生,得与失,都是常态。不是此处得,彼处失,就是彼处得,此处失,得得失失,失失得得,才构成了“人间事”。
正如席慕容的那首诗《写给幸福》:“挫折会来,也会过去。热泪会流下来,也会收起。没有什么
可以让我气馁的,因为我有着长长的一生。”
别为昨日忧愁,别为琐事烦忧,因为真正的人间清醒,是努力活着。
余生,愿你所有快乐,无需假装;愿你此生尽兴,赤诚善良。
                                   ——————  《一禅心灵庙语》

文章目录

1. web.xml 的缺点分析

分析 oa项目中的 web.xml文件 具体的可以移步至:🔜🔜🔜 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客

  • 现在只是一个单标的CRUD,没有复杂的业务逻辑,很简单的一丢丢功能。web.xml文件中就有如此多的配置信息。如果采用这种方式,对于一个大的项目来说,这样的话 web.xml文件会非常庞大,有可能最终会达到几十兆。
  • web.xml文件中进行 servlet信息的配置,显然开发效率比较低,每一个都需要配置一下。
  • 而且在web.xml文件中的配置是很少被修改的,所以这种配置信息能不能直接写到java类当中呢?可以的。

Servlet3.0版本之后,推出了各种Servlet基于注解式开发。优点是什么?

  • 开发效率高,不需要编写大量的配置信息。直接在 java 类上使用注解进行标注。

  • web.xml文件体积变小了。

  • 并不是说注解有了之后,web.xml文件就不需要了:

    • 有一些需要变化的信息,还是要配置到web.xml文件中。一般都是 注解+配置文件 的开发模式。
    • 一些不会经常变化修改的配置建议使用注解。一些可能会被修改的建议写到配置文件中。

2. @WebServlet 注解

注解对象的使用格式:

@注解名称(属性名=属性值, 属性名=属性值, 属性名=属性值....)
// 如果注解当中的属性赋值的类型是数组,格式如下
@注解名称(属性名=属性值1,属性值2,属性值3,属性名=属性值)
// 如果注解当中还有注解的赋值如下:
@注解名称(属性名=属性值,注解名称(属性名=属性值,属性名=属性值),属性名=属性值)

想要了解更多的注解信息的内容,大家可以移步至:🔜🔜🔜 Java “框架 = 注解 + 反射 + 设计模式” 之 注解详解_ChinaRainbowSea的博客-CSDN博客

如下是 @WebServlet 注解基于 Tomcat 10 的源码:


package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet 

    /**
     * @return name of the Servlet
     */
    String name() default "";

    /**
     * A convenience method, to allow extremely simple annotation of a class.
     *
     * @return array of URL patterns
     * @see #urlPatterns()
     */
    String[] value() default ;

    /**
     * @return array of URL patterns to which this Filter applies
     */
    String[] urlPatterns() default ;

    /**
     * @return load on startup ordering hint
     */
    int loadOnStartup() default -1;

    /**
     * @return array of initialization params for this Servlet
     */
    WebInitParam[] initParams() default ;

    /**
     * @return asynchronous operation supported by this Servlet
     */
    boolean asyncSupported() default false;

    /**
     * @return small icon for this Servlet, if present
     */
    String smallIcon() default "";

    /**
     * @return large icon for this Servlet, if present
     */
    String largeIcon() default "";

    /**
     * @return description of this Servlet, if present
     */
    String description() default "";

    /**
     * @return display name of this Servlet, if present
     */
    String displayName() default "";


2.1 @WebServlet注解当中常用的一些属性的说明

WebServlet 当中存在着不少的属性,这里我们只介绍一些常用的属性。其他的大家感兴趣的可以去学习。

  • name 属性的作用:用来指定 Servle t的名字。等同于web.xml 当中的 : 。如下图所示的

/**
* @return name of the Servlet
*/
String name() default "";  // default 表示该属性的默认值为 "" 空字符串
  • urlPatterns 属性的作用:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :。如下图所示的.

注意: 需要注意的是:urlPatterns 所赋值的字符串映射的url 路径要带 / 开始的

/**
* @return array of URL patterns to which this Filter applies
*/
String[] urlPatterns() default ;  // 是一个字符串数组的类型,因为一个Servlet 可以有多个映射路径 url
// 注意 urlPatterns 属性值和 web.xml 当中的  <url-pattern> 带 "/" 开始

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
@WebServlet(name="Test",urlPatterns = "/test","/test2","/test3")
public class TestWebServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到本Servlet Name
        String name = getServletName();
        writer.println("注解当中的name 值也就是web.xml中的<servlet-name>的值: " + name + "<br>");

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");


    


  • value 属性的作用: 和 urlPatterns 属性的作用是一样的:用来指定 Servlet 的映射路径 url 。可以指定多个字符串(多个url)。等同于web.xml 当中的 :。
  • 可以使用模糊查询
@WebServlet("/dept/*")  // 可以使用模糊查询,* 任意字符串 ;表示只要是 /dept/xxx的任意都可以访问该Servlet

这里为什么要设置两个作用一样的属性值呢?

因为 是一个 Servlet 当中最常用,而且是必须要有的。如果注解当中只对一个的属性赋值,并且该属性名名为 value的话。那么这个 value 的属性名就可以省略不写。 这里再说一点就是如果一个注解当中属性类型为数组,但是该数组只赋一个值的话,可以省略数组的 花括号。

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = "/test")
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略
@WebServlet("/test") // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");




    


  • initParams 属性作用:表示的是 Servlet对象的配置信息对象的信息,一个Servlet 就有一个 Servlet 配置对象的信息,封装在了 标签当中的 标签当中设置。如下图所示

注意: initPatams属性的类型是 WebInitParam这个注解 数组。

/**
* @return array of initialization params for this Servlet
*/
WebInitParam[] initParams() default ;

如下是 @WebInitParam的源码:

该@WebInitParam 注解当中的 name 表示:XXX的值,而 value 表示 : XXX/param-value> 的值。

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam 

    /**
     * @return name of the initialization parameter
     */
    String name();

    /**
     * @return value of the initialization parameter
     */
    String value();

    /**
     * @return description of the initialization parameter
     */
    String description() default "";


举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

// 注意: urlPatterns 属性值以 "/" 开始
//@WebServlet(value = "/test")
// 可以省略为如下方式: 注解当只对名为 value属性名赋值,可以省略 value 属性名,数组如果只有一个值,可以省略
@WebServlet(value = "/test", initParams = @WebInitParam(name = "user", value = "root"),
        @WebInitParam(name = "password", value = "11235813")) // 同样url 映射路径是以 "/" 开始的
public class TestWebServlet extends HttpServlet 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 
        // 设置在浏览器端显示的格式类型,以及字符集编码
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter writer = response.getWriter();

        // 获取到该类当中 web.xml 中的 url-pattern 的值
        // 如果有多个的话,获取到的是你使用的那一个(在浏览器地址栏上显示的那一个url)
        String servletPath = request.getServletPath();
        writer.println("该类注解当中的urlPatterns也就是web.xml 中的 url-pattern的值:" + servletPath + "<br>");


        // 获取到初始化参数,对应的一个Servlet 标签当中的 <init-param> 标签当中设置
        String username = getInitParameter("user"); // 根据对应的配置的 name 获取到对应的 value 值
        writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 user的值: " + username + "<br>");

        String password = getInitParameter("password");
        writer.println("该类注解当中initParams的值也就是本Servlet当中的 配置对象的为 password的值: " + password + "<br>");


    


  • loadOnStartUp属性的作用:用来指定在服务器启动阶段是否加载该Servlet的构造器(默认是加载Servlet 是不会立即就调用其中的构造器的,而是访问的时候才会调用该Servlet的构造器)。等同于:整数值 其中数值越小,优先被调用执行。
/**
* @return load on startup ordering hint
 */
int loadOnStartup() default -1;

举例:

package com.RainbowSea.servlet;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;


@WebServlet(value = "/test",loadOnStartup = 1) 
public class TestWebServlet extends HttpServlet 
    
    public TestWebServlet() n
        System.out.println("TestWebServlet 的构造器执行");
    


3. 使用模板方法设计模式优化oa项目

注意:建议: 如果你阅读到这里时,非常感谢您的大力支持,如果还要继续阅读的话,建议先移步至:🔜🔜🔜 使用“纯”Servlet做一个单表的CRUD操作_ChinaRainbowSea的博客-CSDN博客 博客,方便后续的内容上的理解阅读。

上面的**@WebServlet** 注解解决了配置文件的问题。但是现在的oa项目仍然存在一个比较臃肿的问题。

  • 一个单标的CRUD,就写了6个Servlet。如果一个复杂的业务系统,这种开发方式,显然会导致类爆炸。(类的数量太大。)

  • 怎么解决这个类爆炸问题?可以使用模板方法设计模式(定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。“父类定义骨架,子类实现某些细节。”)。具体的模板方法设计模式,大家可以移步至:🔜🔜🔜 23种设计模式之 : 模板方法设计模式_ChinaRainbowSea的博客-CSDN博客

怎么解决类爆炸问题?

  • 以前的设计是一个请求一个 Servlet类。1000个请求对应1000个Servlet类。导致类爆炸。
  • 可以这样做:一个请求对应一个方法。一个业务对应一个Servlet类。比如:处理部门相关业务的对应一个DeptServlet。处理用户相关业务的对应一个UserServlet。处理银行卡卡片业务对应一个CardServlet

思想:

模板方法:父类定义骨架,子类实现某些细节。而这里我们将骨架定义为一个核心的方法也就是这里重写的父类中的 protected void service(HttpServletRequest request, HttpServletResponse response)的方法,需要注意的是:重写的 service 就没有 405 错误的提示了。

这里我们使用@WebServlet 注解的方式,进行一个 url 映射路径的配置。

该核心方法思路是: 通过浏览器地址栏上访问的不同的 url ,对应不同的功能访问。我们就可以使用request.getServletPath()获取到浏览器地址栏上的 url 的字符串,再根据获取到的不同的 url 字符串进行一个功能上的匹配equals 对应不同的功能,我们使用方法将该功能实现。

可以为 value 属性值设置为 模糊查询

@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet

具体代码如下:

package com.RainbowSea.oa;

import com.RainbowSea.DBUtil.DBUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


/**
 * 采用模板方法设计模式,重新设计一个 OA 系统
 */

//@WebServlet(value = "/dept/list","/dept/save","/dept/edit","/dept/detail","/dept/delete","/dept/modify"

//@WebServlet("/dept/list", "/dept/save", "/dept/edit", "/dept/detail", "/dept/delete", "/dept/modify")
@WebServlet("/dept/*") // 可以使用模糊查询,* 任意字符串,表示/dept/xxx的任意都可以访问该Servlet
public class DeptServlet extends HttpServlet 

    // 模板方法
    // 重写其中的 servlet 方法(并没有重写其中的doGet()方法,405错误没有了)

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException 

        // 获取到该对应请求地址栏上的 url ,也就是一个servet对应当中的 url 映射路径
        // 这里是对应浏览器地址栏上的 url
        String servletPath = request.getServletPath();  // 返回的 url 的开头是带了 "/" 的

        if ("/dept/list".equals(servletPath)) 
            doList(request, response);
         else if ("/dept/save".equals(servletPath)) 
            doSave(request, response);
         else if ("/dept/edit".equals(servletPath)) 
            doEdit(request, response);
         else if ("/dept/detail".equals(servletPath)) 
            doDetail(request, response);
         else if ("/dept/delete".equals(servletPath)) 
            doDel(request, response);
         else if ("/dept/modify".equals(servletPath)) 
            doModify(request, response)

      提到Servlet的配置,大多数人想到的应该都是在web.xml中配置吧。有没有更简洁的方式呢?今天就学到了採用注解的方式配置Servlet。

此方式尽管简便。但当然也存在问题。


      採用注解的有点:你能够用一句简单的语句,替代一大段的配置文件内容。

      比方你能够在java文件里加入以下简单的一句话:

@WebServlet(name="servlet", urlPatterns={"/*"})
      来替代你在web.xml中配置的这一大段话:

<servlet>
  	<servlet-name>servlet</servlet-name>
  	<servlet-class>完整类名</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>servlet</servlet-name>
  	<url-pattern>/*</url-pattern>
  </servlet-mapping>


      如此看来。是不是通过注解的方式能够非常简答的把web.xml文件替换掉,或者说淘汰掉web.xml。

答案当然是否定的。配置文件web.xml在有些情况下是必须的,比方设置Web应用的安全属性等等。因此,注解并不能全然的代替web.xml,它不过使web.xml文件更加的简洁。


      既然我们有两种方式去配置Servlet属性,有没有想过假设两种方式都放在了同一个项目中(实际中没人会这么逗*吧。可是既然是做学问就应该自己想到),那么会是什么情况。 经过实际检验,当两者反复,但配置的属性不同一时候。Web容器会以Web部署文件web.xml中的信息为准。

由此也能看出web.xml的重要性。







以上是关于Servlet注解的使用,简化配置 以及,使用模板方法设计模式优化oa项目的主要内容,如果未能解决你的问题,请参考以下文章

注解WebServlet配置Servlet报404错误的原因

@WebFilter("")配置servlet访问出现404的原因

SpringBoot初识

Servlet3.0提供的@WebServlet注解引用参数详情介绍

Servlet3.0学习总结——使用注解标注Servlet

你了解过Servlet3.0吗?