JavaWeb| 深入Servlet技术(三)

1


写在前面的话


       这篇文章应该是Servlet篇的结尾篇了,在这篇文章中,我会讲到重定向并且给大家演示一个小栗子,还会讲到请求转发和重定向的区别、网页的自动刷新以及Servlet线程安全问题。废话不多说了,继续往下看吧!


2


重定向这个东东


首先当然是来自百度百科的解释:


       重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。


也有另外一种解释:


      重定向是根据服务器返回状态码来实现的。如果此时服务器返回了一个301或者302的状态码,那么浏览器就会到新的网址重新请求该资源。服务器的响应中会带着这个新资源的地址。


可能你现在还是不懂重定向是啥,我给你举个简单的例子,当我们在某个网站登录时,我们点击登录之后就会跳转到个人中心之类的页面,此时就是因为发生了重定向。



给大家简单的画了张图:


JavaWeb| 深入Servlet技术(三)


我们通过一个实际的代码案例来让大家体验一下:


  <form action="test2" method="get">

  用户名:<input name="username" type="text">
    密码:<input name="password" type="password">
    <input type="submit" name="tijiao">
  </form>


这是我们的表单,模拟登录界面。



@WebServlet(name = "TestServlet2",urlPatterns = "/test2")
public class TestServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = "JAP";
        String password = "123456";

        String username = request.getParameter("username");
        String passWord = request.getParameter("password");

        if (name.equals(username)&&password.equals(passWord)){
            // HttpServletResponse.SC_MOVED_TEMPORARILY是302常量
            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
            response.setHeader("Location","https://www.baidu.com");
        }
    }
}


上面是我们的Servlet代码,其实非常的简单,我们运行案例之后,如果登录成功,那么页面会跳转至百度。


3


请求转发和重定向的区别


区别:


    ①重定向可以跳转至任何的网址,转发只能在服务器内部进行,就好比上面我们重定向至百度。


    ②重定向的地址栏是会发生变化的而转发不会


    ③重定向是两次请求,两次响应,转发是一次请求一次响应。


    ④重定向路径需要加工程名而转发路径不需要。


这里也给大家两张图:


JavaWeb| 深入Servlet技术(三)


4

网页的自动刷新和跳转

这里给大家的是一个小案例:



@WebServlet(name = "TestServlet3",urlPatterns = "/test3")
public class TestServlet3 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //防止乱码
        response.setContentType("text/html;charset=utf-8");
        // 设置自动刷新和跳转
        response.setHeader("refresh","3;url='index.jsp'");
        response.getWriter().println("3秒后自动跳转!");
    }
}


这三句简单的代码就可以实现页面的自动刷新和跳转


5

Servlet线程安全

    首先给大家一串代码,大家思考一下此时是否存在线程安全问题?



@WebServlet(name = "ThreadServlet",urlPatterns ="/thread")
public class ThreadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int i =0;
        i++;
    }
}


其实这里是没有线程安全问题的,为啥呢?因为doGet方法是由Service方法实现的,Service方法是一个多线程的方法,当多个客户端同时访问doGet方法时,它会为每个doGet方法创建一个int i的变量。


我们再来看看这串代码,我们将i变量变成成员变量,并且我们通过Thread的sleep方法模拟一个并发问题。



@WebServlet(name = "ThreadServlet",urlPatterns ="/thread")
public class ThreadServlet extends HttpServlet {
    int i =0;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        i++;
        try {
            Thread.sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(i+"");
    }
}


我们来看一下两个浏览器同时访问它会是一个怎样的结果:


JavaWeb| 深入Servlet技术(三)


可以看到,我们两个浏览器同时去访问它时,两个显示的都是2,这说明了当第一个线程运行i++并且睡眠5秒时,另一个线程此时访问进来了并且执行了i++,所以此时i变成了2,当休眠期过了,就在浏览器上显示了2.


咱们来总结一下:

当多个客户端并发访问同一个Servlet的时候,web服务器会为每一个客户端的访问创建一个线程,并且在这个线程上调用service方法,因此service方法内部如果访问同一个共享资源时,就可能引发线程安全问题


既然问题出来了,怎么去解决它呢?

1.同步代码块:



@WebServlet(name = "ThreadServlet",urlPatterns ="/thread")
public class ThreadServlet extends HttpServlet {
    int i =0;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        synchronized (this){
            i++;
            try {
                Thread.sleep(5*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(i+"");
        }
    }
}


通过加synchronized同步代码块,可以解决多线程安全问题,结果我就不展示了。其实这种方法也会给我们带来一些问题,例如,我们加了同步代码块后,我们第二次线程访问时需要等待第一个线程结束才能够进去访问,如果一个网站1000人去访问,那需要等很久很久,所以这给用户带来的体验也是不好的。


总结:为了减少这种线程安全问题的发生,唯一一种有效的方法就是少去使用一些共享的资源,最好都是一些局部资源,这样就可以去保证Servlet的线程安全问题。

上一篇:最全面的资料教你学C#


下一篇:资源分享 | 这次选了一些精品Python书籍给大家(pdf资源)