理解 Servlet

理解 Servlet

这是一个简单的 Servlet 例子,实现了登录,退出,登录用户可以进行 Hello World 服务和 Echo 服务。

web.xml

首先 web.xml 是 Java Web 项目的一个重要的配置文件,但是 web.xml 文件并不是 Java Web 工程必须的。

web.xml 文件是用来配置:欢迎页、servlet、filter 等。当你的web工程没用到这些时,你可以不用 web.xml 文件来配置你的 Web 工程。

web.xml 位于 web/WEB-INF/web.xml(如果你不是用 IDE 自动生成 web.xml 文件,请注意路径为 WEB-INF,而不是 WEB-INFO)

web.xml 加载顺序

首先简单讲一下,web.xml的加载过程。当启动一个 WEB 项目时,容器包括(JBoss、Tomcat等)首先会读取项目web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常地被启动起来。

1. 启动WEB项目的时候,容器首先会去它的配置文件web.xml读取两个节点:  <listener></listener> 和 <context-param></context-param>。

2. 紧接着,容器创建一个ServletContext(application),这个WEB项目所有部分都将共享这个上下文。

3. 容器以 <context-param></context-param> 的 name 作为键, value 作为值,将其转化为键值对,存入 ServletContext。

4. 容器创建 <listener></listener> 中的类实例,根据配置的class类路径 <listener-class></listener-class> 来创建监听。

    举例:你可能想在项目启动之前就打开数据库,那么这里就可以在 <context-param> 中设置数据库的连接方式(驱动、url、user、password),在监听类中初始化数据库的连接。

    在监听类中会有 contextInitialized(ServletContextEvent args) 初始化方法,启动 Web 应用时,系统调用Listener的该方法,在这个方法中获得:

    ServletContext application = ServletContextEvent.getServletContext();
    application.getInitParameter("context-param的键");
    
    它还有销毁方法 contextDestroyed(ServletContextEvent args),关闭 Web 应用时,系统调用 Listener 的该方法,用于关闭应用前释放资源,比如:说数据库连接的关闭。


5. 接着容器会读取 <filter></filter> 根据指定的类路径来实例化过滤器。

6. 以上都是在WEB项目还没有完全启动起来的时候就已经完成了的工作。如果系统中有 Servlet,则 Servlet 是在第一次发起请求的时候被实例化的,而且一般不会被容器销毁,它可以服务于多个用户的请求。所以,Servlet的初始化都要比上面提到的那几个要迟。

总的来说,web.xml的加载顺序是: -> -> -> 。其中,如果 web.xml 中出现了相同的元素,则按照在配置文件中出现的先后顺序来加载。

对于某类元素而言,与它们出现的顺序是有关的。以 为例,web.xml 中当然可以定义多个 ,与 相关的一个元素是 ,注意,对于拥有相同 的 和 元素而言, 必须出现在 之后,否则当解析到 时,它所对应的 还未定义。web 容器启动初始化每个 时,按照 出现的顺序来初始化的,当请求资源匹配多个 时, 拦截资源是按照 元素出现的顺序来依次调用 doFilter() 方法的。 同 类似,此处不再赘述。

例子

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
     version="4.0">

    <!-- 定定义了 web 应用的名称,可以在 http://localhost:8080/manager/html 中查看 -->
    <display-name></display-name>
    
    <!--  告诉servlet/JSP容器,Web容器中部署的应用程序适合在分布式环境下运行。-->
    <distributable></distributable>
    
    <!-- <welcome-file-list> 元素可以包含一个或多个 <welcome-file> 子元素。如果在第一个 <welcome-file> 元素中没有找到指定的文件,Web 容器就会尝试显示第二个,以此类推。-->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

    <!-- <context-param>元素含有一对参数名和参数值,用作应用的Servlet上下文初始化参数,参数名在整个Web应用中必须是惟一的,在web应用的整个生命周期中上下文初始化参数都存在,任意的Servlet和jsp都可以随时随地访问它。-->
    <!-- <param-name>子元素包含有参数名,而<param-value>子元素包含的是参数值。-->
    <!-- 作为选择,可用<description>子元素来描述参数。-->
    <!-- 其他: [1] -->
    <context-param>
        <param-name>key</param-name>
        <param-value>value</param-value>
    </context-param>
    
    <!-- 设置 session 超时 -->
    <!-- 默认 30 minutes,设置在容器 /conf/web.xml 文件中-->
    <!-- 该元素值必须为整数,以分钟为单位。如果 session-timeout元素的值为零或负数,则表示会话将永远不会超时-->
    <session-config>
        <session-timeout>120</session-timeout>
    </session-config>

    <filter>
        <filter-name>filterHelloWorld</filter-name>
        <filter-class>com.wyy.filter.FilterHelloWorld</filter-class>
        <!-- 通过 FilterConfig 获取 -->
        <init-param>
            <param-name>key</param-name>
            <param-value>value</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>filterHelloWorld</filter-name>
        <url-pattern>/helloworld</url-pattern>
        <!-- 通过 servlet name 配置-->
        <servlet-name>helloworld</servlet-name>
        <!-- 类型 -->
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <listener>
        <listener-class>com.wyy.listener.ListenerApp</listener-class>
    </listener>

    <servlet>
        <!-- 为Servlet指定一个文本描述。-->
        <description></description>
        <servlet-name>universal</servlet-name>
        <servlet-class>com.wyy.control.UniversalServlet</servlet-class>
        <!-- 为 Servlet 指定一个图标,在图形管理工具中表示该 Servlet。-->
        <icon></icon>
        <!-- 用来指定应用中JSP文件的完整路径。这个完整路径必须由 / 开始。-->
        <jsp-file></jsp-file>
        <!-- 如果 load-on-startup 元素存在,而且也指定了 jsp-file 元素,则 JSP 文件会被重新编译成 Servlet,同时产生的 Servlet 也被载入内存。<load-on-startup> 的内容可以为空,或者是一个整数。这个值表示由Web容器载入内存的顺序。-->
        <!-- 举个例子:如果有两个Servlet元素都含有 <load-on-startup> 子元,则 <load-on-startup> 子元素值较小的 Servlet 将先被加载。如果 <load-on-startup> 子元素值为空或负值,则由Web容器决定什么时候加载 Servlet。如果两个Servlet的 <load-on-startup> 子元素值相同,则由Web容器决定先加载哪一个Servlet。<load-on-startup>1</load-on-startup> 表示启动容器时,初始化Servlet。-->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>universal</servlet-name>
        <!-- url-pattern 要么以斜杠开头,要么以 *.扩展名结尾  例如:/* 或 *.do -->
        <!-- 不能同时出现这两种方式。例如 /*.do 是非法的 -->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

</web-app>

[1]:配置 Spring,必须需要 ,而 可有可无,如果在 web.xml 中不写 配置信息,默认的路径是 /WEB-INF/applicationontext.xml,在WEB-INF目录下创建的 xml 文件的名称必须是 applicationContext.xml。如果是要自定义文件名可以在 web.xml 里加入 contextConfigLocation 这个 contex t参数:在
里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。

Hello World 服务

我们先来写一个简单的服务,用户请求 /helloworld,页面返回 “hello word”。

HelloWorldServlet.java

public class HelloWorldServlet extends HttpServlet {

    String message = "";

    @Override
    public void init() throws ServletException {
        super.init();
        message = "hello word";
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(message.getBytes());
        outputStream.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

web.xml

<servlet>
    <servlet-name>helloworld</servlet-name>
    <servlet-class>HelloWorldServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>helloworld</servlet-name>
    <url-pattern>/helloworld</url-pattern>
</servlet-mapping>

Hello World 服务非常简单,用户请求 /helloworld,服务返回一个 “hello world” 字符串。

我们还可以编写一个 Echo 服务,用户请求 /echo ,服务返回用户 say 参数的值,用户的完整请求地址为 /echo?say=good,服务返回 “good” 字符串

Login 服务

接下来,我们写一个登录服务,我们假设用户都非常不重视安全,设置的用户名与密码相同,用户请求 /login,我们比较用户的 username 参数与 password 是否相等,相等则登录成功,否则失败。

User.java

public class User {

    private String name;
    private String password;
    private Integer age;
    private boolean isLogin;

    public User(String name, String password, Integer age) {
        this.name = name;
        this.password = password;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public boolean isLogin() {
        return isLogin;
    }

    public void setLogin(boolean login) {
        isLogin = login;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}

LoginServlet.java

public class LoginServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        OutputStream outputStream = response.getOutputStream();

        String name = request.getParameter("name");
        String password = request.getParameter("password");

        if(name != null && password != null && name.equals(password)){
            HttpSession session = request.getSession();

            User user = new User(name, password, (int)(Math.random() * 100));
            user.setLogin(true);
            session.setAttribute(name, user);

            Cookie cookie = new Cookie("user", name);
            cookie.setMaxAge(60 * 5);
            response.addCookie(cookie);

            outputStream.write("login success".getBytes());
        } else {
            outputStream.write("login failure".getBytes());
        }
        outputStream.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

web.xml

<servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>LoginServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

我们还必须写一个退出登录,让用户退出。请求地址: /outLogin

未登录用户拦截

现在我们编写一个 Filter,只让登录的用户访问 Hello World 服务

IsLoginFiller.java

public class IsLoginFiller implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("IsLoginFiller init");
        System.out.println("打印 web.xml 中配置的参数:");
        Enumeration<String> enumerations = filterConfig.getInitParameterNames();
        while (enumerations.hasMoreElements()) {
            String name = enumerations.nextElement();
            System.out.print(name + ":" + filterConfig.getInitParameter(name) + ",");
        }
        System.out.println();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletResponse.setContentType("text/html");
        OutputStream outputStream = servletResponse.getOutputStream();
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        boolean flag = false;
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if("user".equals(cookie.getName())){
                HttpSession session = request.getSession();
                User user = (User) session.getAttribute(cookie.getValue());
                if(user != null && user.isLogin()){
                    filterChain.doFilter(servletRequest, servletResponse);
                    flag = true;
                    break;
                }
            }
        }
        if(!flag){
            outputStream.write("please login".getBytes());
        }
        outputStream.close();
    }

    @Override
    public void destroy() {
        System.out.println("IsLoginFiller destroy");
    }
}

web.xml

<filter>
    <filter-name>isLogin</filter-name>
    <filter-class>IsLoginFiller</filter-class>

    <!--没有实际作用-->
    <init-param>
        <param-name>desc</param-name>
        <param-value>is login</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>isLogin</filter-name>
    <servlet-name>helloworld</servlet-name>
</filter-mapping>

从配置文件读取用户信息

我们的用户幡然醒悟,不再用简单的用户名和密码,现在我们将用户信息保存到 user.properties 文件中

AppListener.java

public class AppListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("App start");
        ServletContext servletContext = servletContextEvent.getServletContext();
        InputStream inputStream = null;
        Map<String, User> userMap = new HashMap<>();
        String path = servletContext.getInitParameter("path");
        File file = new File(path);
        Properties properties = new Properties();
        if(file.exists() && file.isFile()){
            try {
                inputStream = new FileInputStream(file);
                properties.load(inputStream);

                String name = properties.getProperty("username");
                String password = properties.getProperty("password");
                String age = properties.getProperty("age");
                if(name != null && password != null && age != null){
                    User user = new User(name, password, Integer.valueOf(age));
                    userMap.put(name, user);
                }
                servletContext.setAttribute("userMap", userMap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("App close");
    }
}

user.properties

username=good
password=idea
age=12

web.xml

<context-param>
    <param-name>path</param-name>
    <param-value>user.properties</param-value>
</context-param>

<listener>
    <listener-class>demo.AppListener</listener-class>
</listener>

LoginServlet.java

public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        OutputStream outputStream = response.getOutputStream();
        ServletContext servletContext = request.getServletContext();
        Map<String, User> userMap = (Map<String, User>) servletContext.getAttribute("userMap");

        String name = request.getParameter("name");
        String password = request.getParameter("password");
        User user = userMap.get(name);

        if(name != null && password != null && user != null && password.equals(user.getPassword())){
            HttpSession session = request.getSession();

            //user = new User(name, password, (int)(Math.random() * 100));
            user.setLogin(true);
            session.setAttribute(name, user);

            Cookie cookie = new Cookie("user", name);
            cookie.setMaxAge(60 * 5);
            response.addCookie(cookie);

            outputStream.write("login success".getBytes());
        } else {
            outputStream.write("login failure".getBytes());
        }
        outputStream.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

Thanks

https://blog.csdn.net/a376298333/article/details/79121548
https://www.cnblogs.com/linhuaming/p/9464356.html
https://blog.csdn.net/believejava/article/details/43229361#
https://blog.csdn.net/qq_19782019/article/details/80292110
https://blog.csdn.net/chenkun2016/article/details/79498618
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值