Servlet

Servlet 是运行在服务端的Java小程序,是sun公司提供的接口规范
用来处理客户端请求,响应给浏览器的动态资源
servlet的实质就是Java代码,通过java的API动态的向客户端输出内容
编译成字节码,放到服务器上执行。
编写程序时,不需要有main函数了。当前发送一个请求的时候,服务器就会按照一定规则调用编写的代码。

第一个servlet
public class FirstServlet implements Servlet{
    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("------init");
    }
    @Override
    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        System.out.println("------service");
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        httpResponse.getWriter().write("aaaaaaaaaaaaaaa");
    }
    @Override
    public void destroy() {
        System.out.println("------destroy");
    }
    @Override
    public ServletConfig getServletConfig() {
        System.out.println("------getServletConfig");
        return null;
    }
    @Override
    public String getServletInfo() {
        System.out.println("------getServletInfo");
        return null;
    }
}

web.xml文件中配置servlet
  <servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class>com.lz.servlet.FirstServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>myservlet</servlet-name>
    <url-pattern>/first</url-pattern>
  </servlet-mapping>

在浏览器中输入效果如图1:


图1

原理:在浏览器输入地址回车后。tomcat就会解析该地址,然后就会到web.xml中去匹配该地址,找到servle映射first,然后找到servlet的对应类名称,就会创建对应对象。内部机制是用反射实现的。

类的加载时机

当程序要使用某个类时,如果该类还未加载到内存中。系统会通过加载、连接
初始化三步来实现对这个类进行初始化。

  • 加载
    就是指将class文件读入到内存,并创建一个对象。任何类被使用时,系统都会创建对应的对象。
  • 连接
    验证是否有正确的内部结构,并和其他类协调一致;负责为类的静态成员分配内存,并设置默认值
  • 初始化
    初始化成员变量等

加载时机:创建类的实例,访问类的静态变量和赋值,调用类的静态方法,初始化某个类的子类,使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。

类加载器 classLoader

负责将.class文件加载到内存中,并生成对应的Class对象。
分类:

  • 根类加载器
    也被称为引导类加载器,负责Java核心类的加载,比如System,String等。在jdk中jre目录下的lib目录下rt.jar文件中
  • 扩展类加载器
    负责jre的扩展目录中的jar包加载,在jdk中jre目录的etx目录
  • 系统类加载器
    负责在jvm启动时加载来自java命令的class文件以及classpath环境变量所指定的jar包和类路径。
反射

运行状态下,可以获取任意类的所有属性和方法。
对于任意对象,都能够调用它的任意一个方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
想要使用反射,必须的要获取字节码文件。

获取字节码文件

  • 方式一 Class类中静态方法forName()
    Class c1 = Class.forName("com.lz.bean.Person");
  • 方式二 动态属性class
    Class c2 = Person.class;
  • 方式三 Object类的getClass()方法
    Person p = new Person();
    Class c3 = p.getClass();

代码1

        (获取字节码)
        Class c1 = Class.forName("com.lz.reflect.Person");
        通过字节码创建对象
        Person p = (Person) c1.newInstance();
        p.setName("phonegg");
        p.setEmail("phonegg@foxmail.com");
        p.show();
        
        通过有参数的构造器来创建对象
        Constructor c = c1.getConstructor(String.class, String.class);
        Person p2 = (Person) c.newInstance("gjj","gjj@qq.com");
        p2.show();

HttpServletRequest

@WebServlet("/request")
public class RequestServlet extends HttpServlet {

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        System.out.println("------------获取请求信息------------");
请求行
        获取请求方式
        String method = request.getMethod();
        System.out.println(method);
        
           获取请求Uri
        String requestUri = request.getRequestURI(); //从项目开始
        System.out.println(requestUri); 
        GET请求   uri  /Servlet/request     
        POST请求  uri  /Servlet/request     
        String requestUrl = request.getRequestURL().toString(); 
        System.out.println(requestUrl);
        GET请求   url  http://localhost:8080/Servlet/request
        POST请求  url  http://localhost:8080/Servlet/request
        
        请求参数
        String queryString = request.getQueryString();
        System.out.println(queryString);
        GET请求   queryString  name=aaa&age=1
        GET请求   queryString  null
        
        当前项目根路径
        String contextPath = request.getContextPath();
        System.out.println(contextPath);
        

        System.out.println("----------请求头信息------------");
请求头
        获取所有请求头名称
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            System.out.println(" name: " + headerNames.nextElement());
        }
        
        System.out.println("----------------------");

        获取指定的请求头
        String header = request.getHeader("referer"); //获取访问来源
        System.out.println(" referer: "+header);
        
//      if (header.equals("http://localhost:8080/Servlet/request.html")) {
//          response.sendRedirect("/Servlet/request.html");
//      }
//      
        System.out.println("----------------------");
        
        获取所有请求头名称和值
        Enumeration<String> headerNamesValues = request.getHeaderNames();
        while (headerNamesValues.hasMoreElements()) {
            String name = headerNamesValues.nextElement();
            String value = request.getHeader(name);
            System.out.println(" name: " + name+ ", value:"+value);
        }

          System.out.println("---------请求体-------------");
请求体
        获取请求参数
        String name = request.getParameter("name");
        int age = Integer.parseInt(request.getParameter("age"));
        String sex = request.getParameter("sex");
        System.out.println("name:"+name+", age:"+age+", sex:"+sex);
        String[] hobby = request.getParameterValues("hobby");
        System.out.println("爱好:"+Arrays.toString(hobby));
        
        System.out.println("----------------------");

        获取所有请求参数名称
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            System.out.println(parameterNames.nextElement());
        }
        
        System.out.println("----------------------");

        获取所有请求参数键值对
        Map<String, String[]> param = request.getParameterMap();
        for (Map.Entry<String, String[]> entry : param.entrySet()) {
            String key = entry.getKey();
            String[] values = entry.getValue();
            System.out.println("key="+key+", valuse="+Arrays.toString(values));
        }
    }
}

form表单提交中文乱码

//发送form请求时,会对请求参数进行编码,格式为ISO8859-1 不支持中文

  • 单个修改 拿到单个参数修改
String sex = request.getParameter("sex");
System.out.println(sex);
byte[] bytes = sex.getBytes("ISO8859-1");
String sexUtf = new String(bytes, "utf-8");
System.out.println(sexUtf);
  • 统一修改 通过request修改所有,只能用于POST请求
request.setCharacterEncoding("UTF-8");

HttpServletResponse

@WebServlet("/response")
public class ResponseServlet extends HttpServlet {

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

        默认编码为 ISO 8859-1 并不支持中文
        response.setCharacterEncoding("UTF-8");
        告诉浏览器 使用编码格式
        response.setHeader("Content-Type", "text/html;charset=UTF-8");
        第一要写在最前面,否则可能仍为乱码。
        response.getWriter().write("你真好");

        设置响应状态码
        response.setStatus(302);
        
        添加响应头
        response.addHeader("name", "lz");
        response.addIntHeader("age", 20);
        response.addDateHeader("my-date", new Date().getTime());
        
        修改响应头
        response.setHeader("name", "lxf");
        
        请求重定向:
        response.setHeader("location", "/Servlet/login");
        response.sendRedirect("/Servlet/login");
        
        定时重定向  各3秒后重定向
        response.setHeader("refresh", "3;url=/Servlet/login");

        请求转发
        RequestDispatcher dispatcher = request.getRequestDispatcher("/s2");
        dispatcher.forward(request, response);

    }
}

重定向 和 转发 对比

  • 1 写法不同

    重定向: 重定向是通过response来操作的
    response.setHeader("location", "/Servlet/login");
    response.sendRedirect("/Servlet/login");
    定时重定向 各3秒后重定向
    response.setHeader("refresh", "3;url=/Servlet/login");
    转发: 转发是通过request来操作
    RequestDispatcher dispatcher = request.getRequestDispatcher("/s2");
    dispatcher.forward(request, response);

  • 2 地址栏变化不同

    重定向: 地址栏发生变化
    转发: 地址栏不变

  • 3 访问服务器次数不同

    重定向: 重定向是在客户端完成操作的,浏览器访问了服务器 两次
    转发: 转发是在服务器端完成操作的,浏览器访问了服务器 一次

  • 4 作用范围不同

    重定向: 可以重定向其他网址
    转发: 只能在当前项目进行操作

ServletContext

@WebServlet("/context")
public class ContextServlet extends HttpServlet {

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

        ServletContext context = getServletContext();
        System.out.println(context);
        获取WebContent下文件
        String aPath = context.getRealPath("a.text");
        System.out.println("a.path--"+aPath);
        
        获取WEB-INF下文件  拼接路径
        String bPath = context.getRealPath("WEB-INF/b.txt");
        System.out.println("bPath----"+bPath);

        
        获取src下的文件 先加载该文件
        String cPath = this.getClass().getClassLoader().getResource("c.txt").getPath();
        System.out.println(cPath);
    }

}
servlet中以二进制读取图片
@WebServlet("/img")
public class ImgServlet extends HttpServlet {

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        String path = getServletContext().getRealPath("noti.jpg");
        
        输入流
        FileInputStream in = new FileInputStream(path);
        
        输出流
        ServletOutputStream out = response.getOutputStream();
        
        byte[] b = new byte[1024];
        int len = 0;
        while ((len = in.read(b)) != -1) {
            System.out.println(len);
            System.out.println(Arrays.toString(b));
            out.write(b, 0, len);
        }

    }
}

servlet 实现下载
@WebServlet("/download")
public class DownLoadServlet extends HttpServlet {

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

提取需要下载的文件名
        String filename = request.getParameter("filename");
        System.out.println(filename);
        //默认会对参数进行 ISO8859-1 编码, 中文名称的文件就会乱码
        
转回为二进制位
        byte[] bname = filename.getBytes("ISO8859-1");
        再使用utf-8进行编码
        filename = new String(bname, "utf-8");
        System.out.println(filename);
        
设置MIME类型, 即文件类型尾缀
        String mimeType = getServletContext().getMimeType(filename);
        response.setContentType(mimeType);
        
告诉浏览器以附件打开
        response.setHeader("Content-Disposition", "attachment;"); 
        修改下载是文件名称
        response.setHeader("Content-Disposition", "attachment;filename="+filename); 

根据不同浏览器进行中文编码, 因为不同浏览器默认编码不同
        String agent = request.getHeader("User-Agent");
        String encodedName = "";
        if (agent.contains("MSIE")) {
            如果是IE浏览器
            encodedName = URLEncoder.encode(filename, "utf-8");
            System.out.println(encodedName);
            encodedName = encodedName.replace("+", " ");
            System.out.println(encodedName);
        } else if (agent.contains("Firefox")) {
            如果是火狐浏览器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            encodedName = "=?utf-8?B?"+base64Encoder.encode(filename.getBytes("utf-8"))+"?=";
        } else {
            其他浏览器编码
            encodedName = URLEncoder.encode(filename, "utf-8");
        }
        response.setHeader("Content-Disposition", "attachment;filename="+encodedName); 

        以流形式读取
        String path = getServletContext().getRealPath(filename);
        FileInputStream in = new FileInputStream(path);
        ServletOutputStream out = response.getOutputStream();
        
        byte[] b = new byte[1024];
        int len = 0;
        while ((len = in.read(b)) != -1) {
            out.write(b, 0, len);
        }
        关闭流是否资源
        in.close();
    
    }
}

Cookie

        Cookie age = new Cookie("age", "100");      
        
        //设置cookie的保存时间
        age.setMaxAge(60);
        //设置cookie携带路径。。。在我工程路径下都可以携带cookie
//      age.setPath("/Cookie_Session");
        //默认情况下,url请求头中会携带父级级路径中的cookie和同级路径的cookie
        response.addCookie(age);
        
        //删除cookie的保存时间修改为0即可。
//      age.setMaxAge(0);
        
        
        //获取指定cookie 比较cookie名字
        Cookie[] cookies = request.getCookies();
        if (cookies!=null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("age")) {
                    System.out.println(cookie.getValue());
                }
            }           
        }

Session

session 的创建

第一次执行request.getSession()时创建。

session 的销毁
  • 服务器关闭
  • session过期。默认30分钟,最后一次操作计时
  • 手动销毁 session.invalidate();
  • 浏览器关闭,并不一定会销毁该session,为什么访问不到session,是因为依赖JSESSIONID查找session,然而这个ID是存储在Cookie中的,在关闭浏览器后再次访问时就不会携带该ID,会重新创建session。

        session 判断是否存在的依据
    通过cookie中携带的JSessionId判断
    
        HttpSession session = request.getSession();
        String name = (String) session.getAttribute("name");
        Integer age = (Integer) session.getAttribute("age");
        
        session.invalidate();
        
        System.out.println("姓名:"+name+", 年龄:"+age);

Cookie Session 对比

  • cookie存储在浏览器端, session存储在服务器端。
  • cookie 生命周期是从创建计时, session 的生命周期是从最后一次操作计时。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容