tomcat与servlet

tomcat

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0psTgLK1-1607248638548)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201105204542452.png)]

配置默认主页

  • 在web.xml中添加

       <welcome-file-list>
                <welcome-file>helloword2.html</welcome-file>
          </welcome-file-list>
    

配置虚拟目录

将web站点放在webapps之外的文件夹,需要在tomcat的server.xml中进行配置,

在host标签下添加;

 <Context path="/web1" docBase="D:\web1"/>

path表示的是访问时输入的web项目名,docBase表示的是站点目录的绝对路径

配置虚拟域名和主机(host标签)

目的:例子:我现在开发了4个网站,有4个域名。如果我不配置虚拟主机,一个Tomcat服务器运行一个网站,我就需要4台电脑才能把4个网站运行起来,多个不同域名的网站共存于一个Tomcat中


      <Host name="zhongfucheng" appBase="D:\web1">
                    <Context path="/web1" docBase="D:\web1"/>
      </Host>

配置端口

Connector 标签中的port

  <Connector port="8888" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

Servlet

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ME1cxZi0-1607248638554)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106191224987.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eUfYi3yi-1607248638556)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106191251650.png)]

作用

Servlet带给我们最大的作用就是能够处理浏览器带来HTTP请求,并返回一个响应给浏览器,从而实现浏览器和服务器的交互

写一个servlet

创建一个java模块,选在web application 【参考webuse项目】

方式一实现Servlet接口

实现Servlet接口,最原生的方式,需要实现init,service,destroy,servletconfig,getServletInfo五个方法,在service中写业务代码;然后配置web.xml

<servlet>
    <!--为servlet起名 -->
    <servlet-name>xx</servlet-name>
    <!--指定servlet的位置,有报名添加包名 -->
    <servlet-class>cc</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>xx</servlet-name>
<!--外界通过访问servlet的路径 -->
    <servlet-pattern>/cc</servlet-pattern>
</servlet-mapping>

#例如通过http://localhost:8080/cc

也可以通过注解:@WebServlet()来映射请求

方式二:继承HttpServlet

在上面我们实现Servlet接口,要实现5个方法。这样太麻烦了!而HttpServlet类已经实现了Servlet接口的所有方法,编写Servlet时,只需要继承HttpServlet,重写你需要的方法即可,并且它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大

servlet是单例的

load-on-startup 标签

如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建包含此标签的Servlet的实例对象、以及调用Servlet实例对象的init()方法

作用:

  1. 为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据
  2. 完成一些定时的任务【定时写日志,定时备份数据

ServletConfig

  • 可以用来读取web.xml中自己servlet中的inti参数

        <servlet>
            <servlet-name>myservlet2</servlet-name>
            <servlet-class>TestServlet2</servlet-class>
            <init-param>
                <param-name>name</param-name>
                <param-value>wang</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>myservlet2</servlet-name>
            <url-pattern>/tt</url-pattern>
        </servlet-mapping>
    
    public class TestServlet2 implements Servlet {
        ServletConfig config;
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
            this.config=servletConfig;
        }
    
        @Override
        public ServletConfig getServletConfig() {
            return this.config;
        }
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
                ServletConfig sf=this.getServletConfig();
                String name=sf.getInitParameter("name");
                servletResponse.getWriter().write(name);
        }
    

ServletContext

当Tomcat启动的时候,就会创建一个ServletContext对象。它代表着当前web站点

  1. ServletContext既然代表着当前web站点,那么所有Servlet都共享着一个ServletContext对象,所以Servlet之间可以通过ServletContext实现通讯
  2. ServletConfig获取的是配置的是单个Servlet的参数信息,ServletContext可以获取的是配置整个web站点的参数信息
  3. 利用ServletContext读取web站点的资源文件
Servlet之间实现通讯

ServletContext对象可以被称之为域对象

到这里可能有一个疑问,域对象是什么呢?其实域对象可以简单理解成一个容器【类似于Map集合】

实现Servlet之间通讯就要用到ServletContext的setAttribute(String name,Object obj)方法
第一个参数是关键字,第二个参数是你要存储的对象

#demo1
        //获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        String value = "zhongfucheng";

        //MyName作为关键字,value作为值存进   域对象【类型于Map集合】
        servletContext.setAttribute("MyName", value);

#demo2
    
        //获取ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //通过关键字获取存储在域对象的值
        String value = (String) servletContext.getAttribute("MyName");

        System.out.println(value);

读取资源的三种方式

方式一:放在src下,相当于获得绝对路径进行访问

ServletContextt对象是根据当前web站点而生成的,所以可以得知道绝对路径,文件放在src下,同一级目录,项目运行后会出现在classes目录下。


        //获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //调用ServletContext方法获取到读取文件的流
        InputStream inputStream = servletContext.getResourceAsStream("/WEB-INF/classes/zhongfucheng/web/1.png");

方式二:放在web目录下,直接访问


        //获取到ServletContext对象
        ServletContext servletContext = this.getServletContext();

        //调用ServletContext方法获取到读取文件的流
        InputStream inputStream = servletContext.getResourceAsStream("2.png");

可以将servletContext所在的位置理解为站点的web目录的位置

方式三:放在src下通过类加载器访问


        //获取到类装载器
        ClassLoader classLoader = Servlet111.class.getClassLoader();

        //通过类装载器获取到读取文件流
        InputStream inputStream = classLoader.getResourceAsStream("/zhongfucheng/web/1.png");

可以把classLoader所在位置理解为src目录的位置,src也成类目录。

servlet之HttpServletResponse

http响应由状态行、实体内容、消息头、一个空行组成。HttpServletResponse对象就封装了http响应的信息。

  • 由于tomcat不支持中文编码,所以涉及中文的输出时,不仅要设置将中文编码成utf-8的格式,而且还要设置浏览器解码时也使用utf-8

    #
    可手动改写
    #
    或者通过<meta http-equiv='content-type' content='text/html;charset=UTF-8'>
    #
     或者通过response.setHeader("Content-Type", "text/html;charset=UTF-8");
    
  • 而response.setContentType(“text/html;charset=UTF-8”);把这两件事都干了

    就是说,response.setContentType("text/html;charset=UTF-8");把response.setCharacterEncoding("UTF-8")的事情也干了!
    
实现文件下载(3步)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zdTU83t-1607248638560)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201105232700101.png)]

  • 找到文件,读取文件产生输入流

        //获取到资源的路径
            String path = this.getServletContext().getRealPath("/download/1.png");
    
            //读取资源
            FileInputStream fileInputStream = new FileInputStream(path);
    
            //获取到文件名,路径在电脑上保存是\\形式的。
            String fileName = path.substring(path.lastIndexOf("\\") + 1);
    
  • 告诉浏览器,我要下载这个文件

      //设置消息头,告诉浏览器,我要下载1.png这个图片
              response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"))
                  //URLEncoder.encode(fileName, "UTF-8")用来防止中文乱码
    
    响应头:
    Content-Disposition: attachment; filename=aaa.zip【服务器告诉浏览器以下载方式打开数据】
    
  • 读取输入流,回送到浏览器,产生输出流

          //把读取到的资源写给浏览器
            int len = 0;
            byte[] bytes = new byte[1024];
            ServletOutputStream servletOutputStream = response.getOutputStream();
    
            while ((len = fileInputStream.read(bytes)) > 0) {
                servletOutputStream.write(bytes, 0, len);
            }
    
            //关闭资源
            servletOutputStream.close();
            fileInputStream.close();
    

    解释:

          //获取到OutputStream流
            ServletOutputStream servletOutputStream = response.getOutputStream();
    
            //向浏览器输出数据
            servletOutputStream.print("aaaa");
    
实现自动刷新

          response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("3秒后跳转页面.....");

        //三秒后跳转到index.jsp页面去,web应用的映射路径我设置成/,url没有写上应用名
        response.setHeader("Refresh", "3;url='/index.jsp'");

解释:

Refresh: 1;url=http://www.it315.org【服务器告诉浏览器要定时刷新】
缓存

像股票类型的网站应该实时更新,所有应该禁止缓存

  //浏览器有三消息头设置缓存,为了兼容性!将三个消息头都设置了
        response.setDateHeader("Expires", -1);
        response.setHeader("Cache-Control","no-cache");
        response.setHeader("Pragma", "no-cache");


        //这里为了看效果
        PrintWriter printWriter = response.getWriter();
        printWriter.print("你好啊" + new Date().toString());
压缩数据

GZIP类来对数据压缩

而用GZIPOutputStream写数据的时候,是把数据写到ByteArrayOutputStream上的,等会还要把数据取出来,再写给浏览器,于是就不能以匿名内部类的方式给GZIPOutputStream,必须把ByteArrayOutputStream定义出来

        response.setContentType("text/html;charset=UTF-8");

        String ss = "fsdfhsdfhuisdhfusdhfuids" +
                "fsdfdsfsdfsdfdsfdafdsfhsdjfhsdjkfhkjds" +
                "fdsfjdslkfjsldkfjsdlkfjsdkfsdjkff" +
                "fsjdfjdsklfjdsklfjkldsfjlksdjflksdjflkds" +
                "dsjfklsdjflsdjfkldsfkjsdkfjsldkfjsdlfk" +
                "fdsjlkfjdslkfjsdlkfjlkasjflk";
     //创建GZIPOutputStream对象,给予它ByteArrayOutputStream
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);

        //GZIP对数据压缩,GZIP写入的数据是保存在byteArrayOutputStream上的
        gzipOutputStream.write(ss.getBytes());

        //gzipOutputStream有缓冲,把缓冲清了,并顺便关闭流
        gzipOutputStream.close();
          //将压缩的数据取出来
        byte[] bytes = byteArrayOutputStream.toByteArray();

        //将压缩的数据写给浏览器
        response.getOutputStream().write(bytes);
生成随机图片,用来产生验证码
  • 要生成一张图片,java提供了BufferedImage类供我们使用

  • 好的,现在我们在内存中创建了一张图片,接着,我们要把图片写给浏览器了把图片写给浏览器,java又提供了图片流【ImageIO】给我们使用

  //在内存中生成一张图片,宽为80,高为20,类型是RGB
        BufferedImage bufferedImage = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);

        //获取到这张图片
        Graphics graphics = bufferedImage.getGraphics();

        //往图片设置颜色和字体
        //把白色填充整张图片
        graphics.setColor(Color.white);
        graphics.fillRect(0, 0, 80, 20);
        graphics.setFont(new Font(null, Font.BOLD, 20));

        //往图片上写数据,先写个12345,横坐标是0,纵坐标是20【高度】
        graphics.drawString("12345", 0, 20);
		
        //要往浏览器写一张图片,那要告诉浏览器回送的类型是一张图片
        response.setHeader("ContentType", "jpeg");
        
        //java提供了图片流给我们使用,这是一个工具类
        //把图片传进去,类型是jpg,写给浏览器
        ImageIO.write(bufferedImage, "jpg", 		response.getOutputStream());
  • 好的,我们的图片数字不**可能是人工写的,数字应该是随机产生的!**这个就简单了。现在我要生成7位的随机数,生成随机数的方法如下
    private String makeNum() {

        Random random = new Random();

        //这样就会生成0-7位的随机数,现在问题又来了,如果随机数不够7位呢?如果不够7位,我们加到7位就行了
        int anInt = random.nextInt(9999999);

        //将数字转成是字符串
        String num = String.valueOf(anInt);

        //判断位数有多少个,不够就加
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < 7 - num.length(); i++) {
            stringBuffer.append("0");
        }

        return stringBuffer.append(num).toString();
        
    }
  • 如果要生成中文,就找中文映射表即可
重定向

使用response.sendRedirect("/index.jsp");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OGDK3l4V-1607248638562)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106002520416.png)]

从图上看,我们看到了两个状态码,一个是302。一个是200。302状态码在http协议中代表的是临时重定向

  • 很容易看出重定向是通过302状态码和跳转地址实现的。于是乎,我们设置http消息头就可以实现重定向跳转

    //设置状态码是302
            response.setStatus(302);
    
            //HttpServletResponse把常用的状态码封装成静态常量了,所以我们可以使用SC_MOVED_TEMPORARILY代表着302
            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
    
            //跳转的地址是index.jsp页面
            response.setHeader("Location", "/index.jsp");
    
  • 其实sendRedirect()方法就是对setStatus()和setHeader()进行封装,原理就是setStatus()和setHeader()

servlet之HTTPServletRequest

简单来说,要得到浏览器信息,就找HttpServletRequest对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bc7g6ZiD-1607248638564)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106191519858.png)]

应用
1. 防盗链

根据请求头的Referer来判断

Referer: http://www.it315.org/index.jsp【浏览器告诉服务器,客户机是从那个页面来的---反盗链】

测试:


        //获取到网页是从哪里来的
        String referer = request.getHeader("Referer");

        //如果不是从我的首页来或者从地址栏直接访问的,
        if ( referer == null || !referer.contains("localhost:8080/zhongfucheng/index.jsp") ) {

            //回到首页去
            response.sendRedirect("/zhongfucheng/index.jsp");
            return;
        }

        //能执行下面的语句,说明是从我的首页点击进来的,那没问题,照常显示
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("路飞做了XXXXxxxxxxxxxxxxxxxx");
post提交表单,request获得数据
  • 首先我们来看一下post方法是怎么进行参数传递的。当我们点击提交按钮的时候,数据封装进了Form Data中http请求中把实体主体带过去了【传输的数据称之为实体主体】,既然request对象封装了http请求,所以request对象可以解析到发送过来的数据,于是只要把编码设置成UTF-8就可以解决乱码问题了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BQeX5Hp8-1607248638565)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106194247275.png)]

因此当遇到post方式提交的数据出现乱码时通过request.setCharacterEncoding(“UTF-8”);就能解决。

来这里我们来分析一下乱码的原因,在前面的博客中我已经介绍了,Tomcat服务器默认编码是ISO 8859-1,而浏览器使用的是UTF-8编码。浏览器的中文数据提交给服务器,Tomcat以ISO 8859-1编码对中文编码,当我在Servlet读取数据的时候,拿到的当然是乱码。而我设置request的编码为UTF-8,乱码就解决了。

但是当我们修改为get方式提交数据,有会出现乱码。使用request.setCharacterEncoding(“UTF-8”);也没用

#分析原因
    因为get方式,它的数据是从消息行带过去的,没有封装到request对象里面,所以使用request设置编码是无效的。
  • 要解决get方式乱码问题也不难,我们既然知道Tomcat默认的编码是ISO 8859-1,那么**get方式由消息体经过tomcat之后肯定经过ISO 8859-1编码了,那么我们只要获得原始的二进制数据,最后解码为utf-8就行了。

    
            //此时得到的数据已经是被ISO 8859-1编码后的字符串了,这个是乱码
            String name = request.getParameter("username");
    
            //乱码通过反向查ISO 8859-1得到原始的数据
            byte[] bytes = name.getBytes("ISO8859-1");
    
            //通过原始的数据,设置正确的码表,构建字符串
            String value = new String(bytes, "UTF-8");
    
    
  • 好的,成功解决掉乱码问题了。

  • 除了手工转换,get方式还可以改Tomcat服务器的配置来解决乱码,但是不推荐使用,这样不灵活。

  • 我们都知道Tomcat默认的编码是ISO 8859-1,如果在Tomcat服务器的配置下改成是UTF-8的编码,那么就解决服务器在解析数据的时候造成乱码问题了

  • 在8080端口的Connector上加入URIEncoding="utf-8",设置Tomcat的访问该端口时的编码为utf-8,从而解决乱码,这种改法是固定使用UTF-8编码的

    <Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" URIEncoding="utf-8"/>
转发
  • 重定向response.sendRedirect("/chen?id=2&name=chen")可以通过在资源尾部添加参数提交数据给服务器,那么转发可以向服务器提交数据吗?答案是肯定的,曾经说过Servlet之间可以通过ServletContext实现通讯,ServletContext也能称之为域对象。而request也可以称之为域对象,只不过ServletContext的域是整个web应用,而request的域仅仅代表一次http请求
        //以username为关键字存zhongfucheng值
        request.setAttribute("username", "zhongfucheng");

        //获取到requestDispatcher对象
        RequestDispatcher requestDispatcher = request.getRequestDispatcher("/Servlet222");

        //调用requestDispatcher对象的forward()实现转发,传入request和response方法
        requestDispatcher.forward(request, response);

因为转发是同一个http请求,因此需要将request和response作为参数传入

获取:

 //获取到存进request对象的值
        String userName = (String) request.getAttribute("username");

        //在浏览器输出该值
        response.getWriter().write("i am :"+userName);

果在调用forward方法之前,在Servlet程序中写入的部分内容已经被真正地传送到了客户端,forward方法将抛出IllegalStateException异常。 也就是说:不要在转发之前写数据给浏览器

        OutputStream outputStream = response.getOutputStream();
        outputStream.write("--------------------------------------------".getBytes());

        //关闭流,确保让数据到浏览器中
        outputStream.close();
        
        //跳转
        request.getRequestDispatcher("/Foot").forward(request, response);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cbW4LhRD-1607248638566)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106200149347.png)]

因为转发是在服务器进行,所以URI不用写web应用名,而重定向相当发起了一项新的请求需要些web应用名

include()

我们在写网页的时候,一般网页的头部和尾部是不需要改变的。如果我们多个地方使用Servlet输出网头和网尾的话,需要把代码重新写一遍。而使用RequestDispatcher的include()方法就可以实现包含网头和网尾的效果了

        request.getRequestDispatcher("/Head").include(request, response);

        response.getWriter().write("--------------------------------------------");


        request.getRequestDispatcher("/Foot").include(request, response);

//跳转
request.getRequestDispatcher("/Foot").forward(request, response);


[外链图片转存中...(img-cbW4LhRD-1607248638566)]

因为转发是在服务器进行,所以URI不用写web应用名,而重定向相当发起了一项新的请求需要些web应用名

### include()

我们在写网页的时候,一般网**页的头部和尾部是不需要改变的**。如果我们**多个地方使用Servlet输出网头和网尾的话,需要把代码重新写一遍**。而使用RequestDispatcher的**include()方法就可以实现包含网头和网尾的效果了**。

    request.getRequestDispatcher("/Head").include(request, response);

    response.getWriter().write("--------------------------------------------");


    request.getRequestDispatcher("/Foot").include(request, response);

``

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjYxlvYa-1607248638568)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201106200504958.png)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值