Servlet技术

Servlet技术

Servlet规范是JavaEE规范中的一个部分,是用于扩展服务器功能的运行在服务器中的小型Java程序(即服务器端小应用程序),是实现了Servlet接口通过http接收和响应web客户端请求的类

静态和动态

  • 因人因时因地而发生变化

服务器

  • web服务器:定位资源—Apache
  • jsp/servlet容器:运行jsp或者servlet程序—Tomcat

Servlet概述

Servlet是一种用于扩展服务器功能的服务器端组件技术

  • 要求直接或者间接的实现Servlet接口
  • 运行在服务器端,执行结果是一个html文档

Servlet的特征

  • 一般采用单实例多线程的方式对外提供服务,一个客户请求对应一个线程
    • 如果在Servlet类中定义属性,则需要考虑线程安全问题

Servlet的优点

1.高效。在服务器上仅有一个Java虚拟机在运行,它的优势在于当多个来自客户端的请求进行访问时,Servlet为每个请求分配一个线程而不是进程。

  • 面试题:Servlet和CGI的区别
    • 早期的CGI采用的是一个用户对应一个线程的方式对外提供服务,所以比较浪费资源。而Servlet采用的就是多线程的方式对外提供服务,所以比较节约资源,而且方便相互通讯

2.方便。Servlet提供了大量的实用工具例程,例如处理很难完成的HTML表单数据、读取和设置HTTP头、处理Cookie和跟踪会话等。

3.跨平台。Servlet是用Java类编写的,它可以在不同的操作系统平台和不同的应用服务器平台下运行。

4.灵活性和可扩展性。采用Servlet开发的Web应用程序,由于Java类的继承性、构造函数等特点,使得其应用灵活,可随意扩展。

6.共享数据。Servlet之间通过共享数据可以很容易地实现数据库连接池。它能方便地实现管理用户请求,简化Session和获取前一页面信息的操作。而在CGI之间通信则很差。由于每个CGI程序的调用都开始一个新的进程,调用间通信通常要通过文件进行,因而相当缓慢。同一台服务器上的不同CGI程序之间的通信也相当麻烦。

7.安全。有些CGI版本有明显的安全弱点。即使是使用最新的标准和PERL等语言,系统也没有基本安全框架。而Java定义有完整的安全机制,包括SSL\CA认证、安全政策等规范。

Servlet缺陷

1、不能所见即所得的实现页面的开发和开发测试不方便。解决方案是JSP

##Servlet开发流程

1、定义Servlet类

  • 直接实现Servlet接口

    public class MyServlet implements Servlet{
        //需要定义5个Servlet接口中声明的方法
    }
    
  • 为了简化开发,针对非标准协议,提供了一个抽象父类GenericServlet

    • 采用的是适配器模式
    • 一般用于游戏的服务器端开发
    public class MyServlet extends GenericServlet{
        public void service(ServletRequest request,ServletResponse response)throws ServletException,IOException {
            
        }
    }
    
  • 为了进一步简化开发,针对http协议,提供了一个父类HttpServlet

    • 采用的是模板模式,主要提供的service方法的实现,所以一般不允许覆盖定义service
    • 内部实现实际上就是通过request.getMethod():String获取请求方法,然后根据对应的请求方法转发给对应的doXxxx方法
      • 例如/add.do?id=124&name=lisi
        • get请求使用?作为地址和参数的分割,例如add.do是请求地址,?之后的内容就是请求串
        • 多个请求参数之间采用&符号作为分割
        • 参数的格式为[名称=值]
    public class MyServlet extends HttpServlet{
        public void doXxx(HttpServletRequest request,HttpServletResponse response)throws ServletException,IOException{
            ...
        }
    }
    

2、使用web应用的核心配置文件/WEB-INF/web.xml映射当前Servlet

  • Servlet类中没有main方法,也就是说具体调用由服务器负责。具体执行时机默认为第一次用户请求时

    <servlet>
    	<servlet-name>名称</servlet-name>
        <servlet-class>对应的类的全名</servlet-class>
    </servlet>
    <servlet-mapping>
    	<servlet-name>对应上面定义的名称</servlet-name>
        <url-pattern>/请求地址.do</url-pattern>
    </servlet-mapping>
    

3、当用户在浏览器中输出xml中配置的请求路径,则由服务器负责按照一定的标准调用Servlet类中的定义方法

###Servlet常见错误

404错误:资源未找到

  • 在请求地址中的servlet的别名书写错误
  • 虚拟项目名称拼写错误

500错误:内部服务器错误

  • ClassNotFoundException,检查web.xml中Servlet类的全限定类名称拼写是否正确
  • 因为service方法体的代码执行错误导致的,可以根据提示信息对方法体中的代码进行更改

405错误:请求方式不支持

  • 请求方式和servlet中的方法不匹配所导致的

Caused by: java.net.BindException: Address already in use: JVM_Bind

  • 多次重复启动服务器,导致服务器端口号冲突

####Servlet与JSP区别

简单的说,SUN首先发展出Servlet,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。 后来SUN推出了类似于ASP的镶嵌型的JSP,把JSP标签镶嵌到HTML语句中,这样,就大大简化和方便了网页的设计和修改。很多网络语言如ASP,PHP,JSP都是镶嵌型的SCRIPT语言

1、JSP在本质上就是SERVLET,但是两者的创建方式不一样

2、Servlet完全是JAVA程序代码构成,擅长于流程控制和事务处理,通过Servlet来生成动态网页很不直观

3、JSP由HTML代码和JSP标签构成,可以方便地编写动态网页

因此在实际应用中采用Servlet来控制业务流程,而采用JSP来生成动态网页.在struts框架中,JSP位于MVC设计模式的视图层,而Servlet位于控制层.

1、JSP是Servlet技术的扩展,本质上就是Servlet的简易方式

2、JSP编译后是“类servlet”

3、Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件

4、JSP侧重于视图,Servlet主要用于控制逻辑

Servlet实例对象的生命周期

Servlet遵循严格的生命周期,在每个Servlet实例的生命中有三种类型的事件,这三种事件分别对应于由Servlet引擎所唤醒的三个方法:

  • init()。当Servlet第一次被装载时,Servlet引擎调用这个Servlet的init()方法,只调用一次。如果某个Sevlet需要特殊的初始化需要。那么Servlet编写人员可以重写该方法来执行初始化任务。这是一个可选的方法。如果某个Servlet不需要初始化,那么默认情况下将调用它父类的init方法。系统保证,在init方法成功完成以前,是不会调用Servlet去处理任何请求的
  • service()。这是Servlet最重要的方法,是真正处理请求的地方。对于每个请求,Servlet引擎将调用Servlet的service方法,并把Servlet请求对象和Servlet响应对象最为参数传递给它
  • 常驻内存
  • destroy()。这是相对于init的可选方法,当Servlet即将被卸载时由Servlet引擎来调用,这个方法用来清除并释放在init方法中所分配的资源。

###Servlet技术的要点:

  • 当前应用中的所有Servlet接口实现类的实例对象,只能由服务器负责创建,开发人不能不能手动创建

  • 默认情况下以单实例多线程的方式对外提供服务

  • Servlet实例一旦创建则常驻内存,只有当服务器资源不足而导致当前Servlet对象被调度或者服务器关闭时才会被销毁

  • 默认情况下,服务器接收到对于当前Servlet接口实现类的第一次请求时自动创建这个Servlet接口实现类的实例对象

    • 可以手动配置要求服务器在启动时自动创建某个Servlet接口实现类对象

##核心对象

API和SPI 分散关注

ServletConfig对象

对应的是Servlet的配置参数信息,由服务器提供对应的具体实现。具体实现是在服务器调用init方法时传入到Servlet实例中

public void init(ServletConfig config) throws ServletException;

config对象由服务器提供实现,并在服务器调用该方法时传入Servlet实例

public interface ServletConfig {
    public ServletContext getServletContext(); //用于获取Servlet上下文对象
    public String getInitParameter(String name);  //用于获取当前Servlet对象的配置参数
    public Enumeration<String> getInitParameterNames();//用于获取当前Servlet对象的所有配置参数的迭代器
}
Servlet的配置 web.xml
<servlet>
        <servlet-name>showServlet</servlet-name>
        <servlet-class>com.yan.action.ShowServlet</servlet-class>
    <!-- 初始化参数 -->
        <init-param>
            <param-name>name</param-name>
            <param-value>yanjun</param-value>
        </init-param>
        <init-param>
            <param-name>age</param-name>
            <param-value>123</param-value>
        </init-param>
</servlet>

在servlet中获取初始化参数

init方法定义

init(ServletConfig)是Servlet接口中定义的方法,需要自行编程处理config对象,因为Servlet接口中还有一个方法时和config对象相关的public ServletConfig getServletConfig();

public class MyServlet implements Servlet{
	private ServletConfig config;
	public void init(ServletConfig config){
		this.config=config;
	}
	public ServletConfig getServletConfig(){
		return config;
	}
	......
}

实际上HttpServlet已经提供了以上的实现

public abstract class HttpServlet extends GenericServlet {
	private transient ServletConfig config;  //父类提供
	
	public void init(ServletConfig config) throws ServletException {
		this.config = config;
		this.init();   //提供一个扩展点
    }
    public void init() throws ServletException {

    }

自定义Servlet接收初始化配置参数

    @Override
    public void init() throws ServletException {
        //对应web.xml中init-param标签中定义的参数
        String ss = this.getServletConfig().getInitParameter("name");  //根据预先知道的名称获取对应的配置参数值
        /*
         <init-param>  配置初始化参数,在一个servlet标签中可以定义多个
            <param-name>name</param-name>   参数名称
            <param-value>yanjun</param-value>  参数值
        </init-param>
         */
        System.out.println(ss);
        //迭代访问所有的配置参数
        Enumeration<String> en = this.getServletConfig().getInitParameterNames();
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            String val = this.getServletConfig().getInitParameter(key);
            System.out.println(key + ":" + val);
        }
    }

配置自动加载

默认情况下,当第一次客户端访问对应地址时,Servlet才会被加载并执行初始化操作。也可以考虑到执行效率要求服务器启动时自动加载并初始胡特定的Servlet

    <servlet>
        <servlet-name>showServlet</servlet-name>
        <servlet-class>com.yan.action.ShowServlet</servlet-class>
       <load-on-startup>1</load-on-startup>  应该是大于等于0的整数,值越小越优先加载
    </servlet>

###ServletRequest对象

在servlet接口中所定义的service方法中出现

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
  • 用于封装客户端的请求信息
  • 是由服务器提供对应的实现类

当服务器调用Servlet实例的service方法时传入对应的接口实现的实例,其中包含服务器所接受到的所有客户端请求信息。一般在具体开发中针对http协议使用的是ServletRequest接口的子接口HttpServletRequest

public interface HttpServletRequest extends ServletRequest
主要方法

1、请求参数

get:使用地址的方式传递数据,实际上使用的是协议头,上限2k

  • http://localhost:8080/test/del.do?id=123&name=lisi其中id=123&name=lisi就是请求参数

post:使用协议体传递数据,默认格式还是id=123&name=lisi,只是不在地址栏中出现,一般用于敏感数据传递或者上传大于2k的数据

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

按照名称可以获取对应的传递数据,获取的数据都是String或者String[]类型

使用一个名称传递多个数据

/abc.do?hobby=111&hobby=333&hobby=444

<input type="checkbox" name="hobby" value="111">足球
<input type="checkbox" name="hobby" value="222">篮球
<input type="checkbox" name="hobby" value="333">弹球
<input type="checkbox" name="hobby" value="444">其它

服务器获取数据的方法:

String[] hobby=request.getParameterValues("hobby");
  • 如果使用getParameter则只能获取第一个数据,后续数据丢失

注意:实际上使用一个名称传递一个值,也可以使用getParameterValues,只是获取的是一个长度为1的字符串数组

特殊方法:一般很少用,除非开发表现层框架

Map<String, String[]> map= request.getParameterMap();  //获取所有的请求参数,其中请求参数名称为key,对应的值为String[]

Map<String,String[]> map=request.getParameterMap();
        for(Map.Entry<String,String[]> en:map.entrySet()){
            System.out.println(en.getKey());
            for(String temp:en.getValue()){
                System.out.println("\t\t"+temp);
            }
        }
        
        
Enumeration<String> names=request.getParameterNames();   //获取所有请求参数名称的迭代器
        while(names.hasMoreElements()){  
            String key=names.nextElement();   //通过迭代器对象获取每个请求参数名称
            String value=request.getParameter(key);  //根据名称获取对应的值
            System.out.println(key+"--->"+value);
        }       

2、以流的方式处理请求信息

InputStream is=request.getInputStream();
BufferedReader br=request.getReader();

上传文件

1、要求使用post提交数据,必须设置enctype=“multipart/form-data”

<form action="test.do" method="post" enctype="multipart/form-data"> 注意表单中的post必须设置,因为默认为get;必须设置enctype,因为默认值application/x-www-form-urlencoded
    <input type="file" name="photo"/>
    <input type="submit" value="上传文件"/>
</form>

2、jspSmartUp组件是一种早期流行使用的上传下载工具,适合于上传小型的文件,具有灵活性简单高效。

 @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter pw=response.getWriter();

        SmartUpload su=new SmartUpload();//创建上传组件对象
        su.initialize(this.getServletConfig(),request,response);//初始化上传组件
        su.setMaxFileSize(2*1024*1024);  //设置允许上传的文件最大大小,单位字节,一般设置为2M
        su.setAllowedFilesList("jpg,png,gif");//设置允许上传的文件后缀类型
        try {
            su.upload();//上传文件,将文件从客户端上传到服务器,如果上传文件不符合要求,则包异常
            //SmartUpload组件支持多文件上传
            Files files=su.getFiles();//获取所有的上传文件
            if(files!=null && files.getCount()>0){//判断上传文件个数是否大于0
                File file=files.getFile(0);//获取第一个上传文件
                System.out.println(file.getFileName());

                String ss=request.getRealPath("");  //获取当前目录的绝对路径,一般推荐通过application对象获取,不建议使用request对象获取
                System.out.println(ss);

                file.saveAs("upload/"+file.getFileName());  //使用的是相对路径,相对于ss的路径

            }
            pw.println("上传上传,点<a href='abc.html'>这里</a>继续上传")
        } catch (Exception e){
            e.printStackTrace(pw);
            pw.println("<a href='abc.html'>重新上传</a>");
        }
        pw.flush();
        pw.close();
    }

下载处理

1、列表显示所有的可下载内容

 @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out=response.getWriter();
        String ss=request.getRealPath("upload/");  //获取upload文件夹的绝对路径
        File ff=new File(ss);
        if(ff.exists()){
            File[] fs=ff.listFiles();
            if(fs!=null && fs.length>0){
                for(File tmp:fs)
                	out.println("<a href='down.do?fname="+tmp.getName()+"'><img src='upload/"+tmp.getName()+"' width='80' height='60'></a>");
            }
        }
        out.flush();
        out.close();
    }

2、下载处理要点:

告知浏览器以附件的方式打开响应数据

响应头的名称Content-Disposition,对应的值为attachment;filename=FileName.txt

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fname=request.getParameter("fname");
        String ss=request.getRealPath("upload/");
        File ff=new File(ss,fname);
        InputStream is=new BufferedInputStream(new FileInputStream(ff));

        response.setHeader("Content-Disposition","attachment;filename=abc.png");
        OutputStream outputStream=response.getOutputStream();

        byte[] buffer=new byte[8192];
        int len=0;
        while((len=is.read(buffer))>0)
            outputStream.write(buffer,0,len);
        outputStream.close();
        is.close();
    }

3、请求头参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iAt88KoA-1617888480370)(images/request请求.png)]

编程实现获取请求头数据

String val=request.getHeader("Accept");  //根据名称获取指定的请求头数据,数据类型一般有3种,默认String,还有int和date
        //获取所有的请求头名称的迭代器
Enumeration<String> names= request.getHeaderNames();
while(names.hasMoreElements()){
            String name=names.nextElement();
            String value=request.getHeader(name);
            System.out.println(name+"-->"+value);
}

实际显示内容

host-->localhost:8080
connection-->keep-alive
upgrade-insecure-requests-->1
user-agent-->Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Edg/89.0.774.45
accept-->text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site-->none
sec-fetch-mode-->navigate
sec-fetch-user-->?1
sec-fetch-dest-->document
accept-encoding-->gzip, deflate, br
accept-language-->zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
cookie-->JSESSIONID=C25DED076B79C8246177CF7E2C937089

ServletResponse对象

用于封装服务器的响应信息

1、设置响应头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xxpzgxue-1617888480380)(images/response响应.png)]

是由服务器传递给客户端浏览器的额外数据

response.setHeader("Content-Disposition","attachment;filename=abc.png");  //告知浏览器,以附件的方式打开响应数据

Expires: 日期数据   用于设置响应内容类型的缓存过期时间
Date now=new Date();
        //例如允许缓存1个月
now=new Date(now.getTime()+1*30*24*60*60*1000);
response.setDateHeader("Expires",now.getTime());

//是否开启缓存
响应头名称Cache-Control,值no-cache表示不缓存数据
写法2:Pragma的值为no-cache时表示禁用缓存,这是一个逐步需要放弃的写法

//禁用缓存
response.setHeader("Pragma","no-cache");  //针对http1.0禁用缓存
response.setHeader("Cache-Control","no-cache"); //针对http1.1禁用缓存
response.setDateHeader("Expires",0);  //表示立即过期

作业:

单表的CRUD

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值