JavaEE笔记整理


第一次课

在手机上记的,内容非常简略,不过好在第一节课本身也没提供什么有价值的新内容,大部分时间都是在复习小学期的内容。

异常

  • 检查异常

  • 运行异常

    编译器不做任何检查,可由码力完全避免

抽象与灵活性的正相关性

第二次课

  • Client -> Server (Request)
  • Server -> Client (Servlet -> Response)
protocol: Host Addr / File Addr
      protocol  Host Addr   File Addr
e.g.: https://  rama.works  /prototype

使用Servlet Response,配合PrintWriter来输出数据

服务器Push技术,区别与以前的基于请求的连接

HTTP, Content-Type: MIME TYPE (考试不考)

text/html
text/plain
text/xml
text/json
application/pdf
application/octet-stream -> download

必考

servlet概念,方法(destroy(), new, init(), service()),生命周期

getParameter(String name)
init(ServletConfig config)
public class Temp implements Servlet {

    private ServletConfig config;

    public void init(ServletConfig config) {
        this.config = config;
    }

    public void destroy() {
        this.config.destroy();
    }

    public void service(ServletRequest res, ServletResponse, response) 
    throws ServletException, IOException {
        // emitted
    }
}

Query String -> ServletRequest Parameter -> getParameter()

getParameter送分题

getOutputStream获得输出

请求状态

404 Not Found

403 Forbidden

200 OK

第三次课

正确的代码顺序(见注释)

// first declare package
package io.github.medioqrity;

// second import native packages
import java.util.*;
import java.io.*;

// third import Java EE packages
import javax.servlet.*;
import javax.servlet.http.*;

// 3rd party packages
import org.apache.struts.action.*;

// your packages
import io.github.medioqrity.somepackage;

// your classes
public class HelloServlet extends HttpServlet {

    public void service(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
        // ...
    }

}

其中HttpServlet是适配器类,这样就不用像这样把所有接口方法都实现了:

public class SampleServlet implements Servlet { 

    private ServletConfig config;

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        // ...
    }

    @Override
    public void service(...) {
        // ...
    }
}

要记住init()方法的参数,考试要考。

当然,在HttpServlet中,从private ServletConfig ...public void init()都已经被写好了。

初始化数据库连接的话,在public void init()中进行初始化,不要覆盖public void init(ServletConfig config)。否则不要忘记super.init()

使用public void service()分发各种请求:

public class SampleServlet extends HttpServlet {

    public void service(ServletRequest req, ServletResponse res) 
    throws ServletException, IOException {
        HttpServletRequest hreq = (HttpServletRequest) req;
        HttpServletResponse hres = (HttpServletResponse) res;
        method = res.getMethod();
        if ("GET".equals(method)) {
            doGet(hreq, hres);
        } else if ("POST".equals(method)) {
            doPost(hreq, hres);
        }
    }

}

还是不太清楚public void service()default void service()的区别

关于为什么不要使用out.close();

注意PrintWriter out = response.getWriter(),显然这个PrintWriter并不是你创建的:它只是一个引用。

所以自然应该是HttpServletResponse类会负责将它关闭。

Tomcat应用程序发布

  • tomcat manager
  • 待补充

websocket & sse

  • websocket -> 双工
  • sse -> 服务器单向往客户端

websocket + sse = 服务器端到客户端

使用Annotation

自学

  • HTML语法
  • Javascript
  • Annotation
  • javax.servlet.http.HttpSession
  • javax.servlet.annotation.*

第四次课

HttpSession

会话进程

id -> getId()

怎么确定客户已经走了 -> timeout,可以通过在web.xml中加入如下代码更改:

<session-config>
    <session-timeout> 15 </session-timeout> <!-- in minutes -->
</session-config>

最有用的就是在session中存储数据,于是:

  • getAttribute(String name)
  • setAttribute(String name, Object value)
  • removeAttribute(String name)

在Servlet中使用HttpSession

public class SessionExampleServlet extends HttpServlet {
    public void service(HttpServletRequest req,
                        HttpServletResponse res)
    throws ServletException, IOException {
        
        HttpSession session = req.getSession(); // obviously

        // if this is the first time you visit
        Cart cart = new Cart(); // create a new cart for you
        session.setAttribute("cart", cart);

        // or you already have the cart
        Cart cart = (Cart) session.getAttribute("cart");
        cart.add("aaa", 123);
    }
}

值得注意的是,session是从HttpServletRequest中取得的。

getSession()方法中,如果session对象还没有被创建的话就创建一个:

Returns the current session associated with this request, or if the request does not have a session, creates one

Session技术的实现

Cookie

~~今天也是好天气~

主流,存放在HTML Header中。

如果浏览器把Cookie关闭的话,那么req.getSession()每次都会创建全新的Session

读取

HttpServletRequest中获取Cookies。注意可能会有多块小饼干

Cookie[] getCookies()
Returns an array containing all of the Cookie objects the client sent with this request.

写入

HttpServletResponse中写入Cookies。

void addCookie(Cookie cookie)
Adds the specified cookie to the response.


之前提到的读取和写入都是必考。虽然这样标注看起来非常功利,我想其实真心想学和应试准备并不冲突。

在使用Cookie之后就不用再使用getSession()了,因为Cookie本身就要从Session中获取。

URL Rewriting

隐藏域

Request Dispatcher

分发请求。

(以下例子待验证是否正确)

public class TestServlet extends HttpServlet {

    public void service(HttpServletRequest req, HttpServletResponse res)
    throws ServletException, IOException {

        RequestDispatcher rd = req.getRequestDispatcher("/servlet/b");
        rd.forward();
        rd.include(req, res);

        res.getWriter().println("你好,你吃了吗?");
    }

}

回去自己补课

data share

Filter

Filter Config

第一次上机作业

总的来说就是实现一个搜索引擎,名为"软院找人"。当然因为数据量相当小,所以其实效率方面不用特别担心。

首先是定义学生的信息,包括姓名,学号,电话,QQ,以及邮箱,一共5个字段。

学号是主码。不过话说回来,既然是用txt或者xlsx存储,那应该也用不着数据库吧…

不限制内存的使用,但是一定要确保性能。

必须支持模糊搜索,但不必支持到subsequence的程度(substring即可)

存储结构的设计

为了确保查询效率,我思考了很多实现方案。但是既然要模糊搜索,那么肯定要涉及到字串匹配了。

如果不是模糊搜索的话,直接5个字典就完事了。

字串匹配,最经典的KMP,还有一些其他的启发式算法,再怎么样复杂度也都是 O ( n + m ) \mathcal{O}(n + m) O(n+m)的水准。为了能够把所有信息搜出来,显然每次查询都要把所有的学生数据都跑一遍。这样,每次查询的复杂度就是 O ( ∑ i s i + ∣ s ∣ × m ) \mathcal{O}(\sum_i s_i + |s| \times m) O(isi+s×m),其中sum是所有学生信息的长度总和,而 ∣ s ∣ |s| s是学生信息的条数。

这等于每次查询都要把每个表都跑一遍才行,我反正是觉得这样不太好…

所以最后我觉得还是应该考虑使用Trie树。当然肯定就有人会感到好奇,Trie树怎么实现模糊查询呢?

以前在自学后缀数组的时候,学到了一个非常重要的概念:

所有的substring都是某个后缀的某个前缀

听起来有点拗口,总归来个简单例子帮助理解:

对于字符串abcabedcfabed是后缀abedcf的前缀,cabcabedcf的一个前缀。

于是要利用只能查询前缀的Trie树来查询SubString,只需要把字符串的每个后缀都放进Trie树里就好了。

查询效率迷思

作为一个ACM半途而废的菜鸡,分析时间复杂度的功夫一直不到家。不过尝试总归还是要尝试的!

采用Trie树的话,插入一条长度为 ∣ s ∣ |s| s的数据的时间复杂度是 O ( ∣ s ∣ 2 ) \mathcal{O}(|s|^2) O(s2)级别的。而查询一条长度为 ∣ q ∣ |q| q的关键字,并且以关键字末端节点为根节点的子树里一共有 n n n个节点的话,则时间复杂度是 O ( ∣ q ∣ + n ) \mathcal{O}(|q| + n) O(q+n)的。

听起来很拗口,总归上图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXMnwGQQ-1574695269976)(JavaEE笔记整理\Trie.png)]

这张图中黑色的结点表示根据关键字hhkb,从根节点出发所走的路径。灰色三角形表示该节点的其他子树。红色节点表示关键字末端节点。红色三角形表示关键字末端节点的所有子树。

那么对于关键字hhkb ∣ q ∣ = 4 |q| = 4 q=4 n n n等于红色三角形中,所有节点的个数。

我想,采用这种方法,跟暴力KMP匹配的效率相比,关键就是那些灰色三角形: 无脑KMP则会导致试图匹配灰色三角形中的字符串,而Trie树根本不会考虑去那些子树里查询答案。

也就是说,我相信采用Trie树来存储数据并用于处理查询的效率能够比KMP要高,就是因为Trie树直接抛弃了那些灰色三角形,自带剪枝效果。

但是实际效率到底是不是真的比无脑KMP要快呢?

一点代码

Trie.java:

package io.github.medioqrity;

import java.util.*;

import io.github.medioqrity.TrieNode;

public class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    /**
     * insert a string into the trie tree
     */
    public void insert(String name, int index, int start_index) {
        TrieNode currentNode = root;
        for (int i = start_index; i < name.length(); ++i) {
            if (!currentNode.hasNext(name.charAt(i))) // if there is no such node
                currentNode.setNext(name.charAt(i), new TrieNode());
            currentNode = currentNode.getNext(name.charAt(i));
        }
        currentNode.setFlag(true);
        currentNode.addIndex(index);
    }

    /**
     * returns a list of integer that contains the index that satisfies
     * the query
     */
    public void query(String name, Set<Integer> result) {
        TrieNode currentNode = root;
        for (int i = 0; i < name.length(); ++i) {
            if (!currentNode.hasNext(name.charAt(i)))
                return; // nothing found
            currentNode = currentNode.getNext(name.charAt(i));
        }
        dfs(currentNode, result);
    }

    /**
     * recursively get all possible results
     */
    private void dfs(TrieNode currentNode, Set<Integer> result) {
        if (currentNode.getFlag()) {
            for (int i : currentNode.getIndexes()) {
                result.add(i);
            }
        }
        for (TrieNode nextNode : currentNode.getMap().values()) {
            dfs(nextNode, result);
        }
    }

    private void print(Character c, int layer) {
        for (int i = 0; i < layer; ++i) System.out.print("  ");
        System.out.println(c);
    }

    private void debug(TrieNode currentNode, int layer) {
        for (Character c : currentNode.getMap().keySet()) {
            print(c, layer);
            debug(currentNode.getNext(c), layer + 1);
        }
    }

    public void debug() {
        debug(root, 0);
    }

    public TrieNode getRoot() {
        return root;
    }
}

StudentQuery.java:

package io.github.medioqrity;

import java.util.*;
import java.io.*;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/query")
public class StudentQuery extends HttpServlet {

    private static Trie nameTrie, phoneTrie, idTrie, qqTrie, mailTrie;
    private static List<Student> students;

    static {
        nameTrie = new Trie();
        phoneTrie = new Trie();
        idTrie = new Trie();
        qqTrie = new Trie();
        mailTrie = new Trie();

        students = new ArrayList<>();

        loadData();
    }

    public StudentQuery() {
        super();
    }

    private static void insert(Trie trie, String str, int index) {
        for (int i = 0; i < str.length(); ++i) {
            trie.insert(str, index, i);
        }
    }

    private static Student parse(String line) {
        String[] l = line.split("\\t");
        if (l.length != 5) return null;
        return new Student(l[0], l[1], l[2], l[3], l[4]);
    }

    private static void loadData() {
        String fileName = "out.txt";
        BufferedReader reader = null;

        int index = 0;

        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(fileName), "UTF-8"));
            String temp = null;
            while ((temp = reader.readLine()) != null) {
                Student student = parse(temp);
                if (student != null) {
                    students.add(student);
                    insert(nameTrie, student.getName(), index);
                    insert(phoneTrie, student.getPhone(), index);
                    insert(idTrie, student.getId(), index);
                    insert(qqTrie, student.getQq(), index);
                    insert(mailTrie, student.getMail(), index);
                    ++index;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void query(Trie trie, String name, Set<Integer> result) {
        trie.query(name, result);
    }

    public void doPost(HttpServletRequest req, HttpServletResponse res) 
    throws ServletException, IOException {
        res.setContentType("text/html; charset=UTF-8");
        req.setCharacterEncoding("UTF-8");
        res.setCharacterEncoding("UTF-8");

        String keyword = req.getParameter("keyword");

        Set<Integer> result = new HashSet<>();
        PrintWriter out = res.getWriter();

        out.println("<body>");
        
        query(nameTrie, keyword, result);
        query(phoneTrie, keyword, result);
        query(idTrie, keyword, result);
        query(qqTrie, keyword, result);
        query(mailTrie, keyword, result);

        for (int i : result) {
            out.println(students.get(i) + "<br>");
        }

        out.println("</body>");
    }
}

index.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset = "UTF-8">
</head>
<body>
    
    <form action = 'query' method = 'post'>
        Enter name or something else: <input type = 'text' name = 'keyword'><br>
        <input type = 'submit' value = 'Google 搜索'>
    </form>

</body>
</html>

最后加载到服务器上就能获得结果。截图就不放了,存在泄漏真实信息的风险(毕竟是以真实数据作为基础生成的学生数据)

第五次课

偷懒没带电脑,只能回来重新敲到电脑上了。

req.getContextPath()

对于http://localhost:8080/j2ee1/servlet/a?abc=1而言,req.getContextPath()的返回值,可能是/j2ee,也可能是空串,这取决于web应用被部署在服务器的哪个位置。

路径迷思

public class AServlet extends HttpServlet {
    public void service(req, res) throws {
        req.getRequestDispatcher("/servlet/b").forward(req, res);
    }
}

service()中,转发的servlet的路径只要像上面那样写就可以了。

但是在index.html中,应该采用document root:

<form action="/j2ee1/servlet/a" method="get></form>"

filter

一定会考doFilter(req, res, FilterChain chain),注意这里的FilterChain,一个过滤器链,就是一串过滤器。

可是过滤器到底是什么呢

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class MyFilter implements Filter {
  public void destroy() {
  
  }
  public void init(FilterConfig config) throws ServletException {
    this.config = config;
  }
  public void doFilter(req, res, chain) throws ServletException, IOException {
    chain.doFilter(req, res);
  }
}

说实话没太理解为什么一个过滤器里要放一个过滤器链。

Filter在请求到达后端之前被执行,因此又被称为interceptor。

暴力转码

整理笔记的时候才发现这老师讲课怎么这么跳

String wd = req.getParameter("wd");
wd = new String(wd.getBytes("ISO-8859-1"), "UTF-8");

另外一种就是req.setCharacterEncoding()

ServletConfig

<servlet>
<init-param>
<param-name>rate</param-name>
<param-value>7</param-value>
</init-param>
</servlet>

然后这些肯定是被封装在ServletConfig中,因为init(ServletConfig config),所以显然config肯定有config.getInitParameter("rate")

不过servlet本身也实现了getInitParameter,所以不用把config拉出来再获取初始化参数。

类似的,filter配置也是在web.xml中实现的:

FilterConfig

<filter-mapping>
  <filter-name>f1</filter-name>
  <servlet-name>s1</servlet-name> <!-- 指定过滤某个servlet -->
  <url-pattern>/*.jpg</url-pattern>
  <dipatcher>REQUEST</dispatcher> <!-- 只拦截浏览器的请求 -->
  <dipatcher>FORWARD</dispatcher> <!-- 和上面的REQUEST一起,会过滤使用REQUEST和FORWARD过来的请求 -->
</filter-mapping>

注意doFilter()里参数是ServletRequest, ServletResponse,而不是派生出来的HttpServletRequest之类的。所以读取之后要强制转换类型:

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

public class MyFilter implements Filter {
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
    HttpServletRequest hreq = (HttpServletRequest) req;
    HttpServletResponse hres = (HttpServletResponse) res;
    
    HttpSession ss = req.getSession();
    
    String userName = (String) ss.getAttribute("username");
    
    if (userName == null) {
      // guest
      req.getRequestDispatcher("login.html").forward(req, res);
      return;
    }
    chain.doFilter(req, res);
  }
}

第六次课

outline & assignment 1 review

outline

Listener

assignment 1 review

package io.github.medioqrity;

public class FindServlet {
    // absolute path vs relative path
    String wrongPath = "/WEB-INF/contact/a.txt"; // still absolute path
    String rightPath = context.getRealPath("/WEB-INF/contact/a.txt"); // transform to the real absolute path using relative path
    
    // file reading
    FileInputStream fis = new FileInputStream(rightPath):
    InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
    BufferedReader br = new BufferedReader(isr);

    String temp = null;
    while ((temp = br.readLine()) != null) {
        
    }
}

缓存文件到内存中

publc static InputStream readFile(String file) {
    File f = new File(fileName);
    FileInputStream fis = new FileInputStream(f);
    BufferedInputStream bis = BufferedInputStream(fis);

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    byte[] buffer = new byte[8 * 1024];
    int r = 0;
    while ((r = fis.read(buffer)) != -1) {
        baos.write(buffer, 0, r);
    }
    bis.close(); fis.close();
    buffer = baos.toByteArray();
    return new ByteArrayInputStream(buffer);
}

war文件

Web ARchive,网站归档文件。实际上就是一个压缩包,可以高效存储小文件,同时还可以检测是否有文件在传输过程中损坏。

使用getServletContext().getResourceAsStream("/WEB-INF/c.txt"),可以方便地读取资源。

ClassLoader也是很不错的。

分页

因为分页这件事情要跨越多次访问过程,所以应该使用Session来存储。

HttpSession session = request.getSession();
session.setAttribute("result", result);

Listener

Event Listener

Button b = new Button(); 
ActionEven
ActionListener

产生了一个事件之后会广播给所有听众,因为并不清楚哪个听众会处理哪个事件。

把生死看破,(咦)所以对象的创建和销毁都是重大事件

@WebListener
public class ApplicationInitializer implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
    }

    public void contextDestroyed(ServletContextEvent sce) {
        // something
    }
}

部署

<listener>
    <listener-class>ApplicationInitializer</listener-class>
</listener>

回去自己看其他listener。其实基本都不难。

JSP

java server page。用来解决类似于out.println("<td>" + data + "</td>")这种垃圾代码。

可以直接用%来分割java代码和html代码:

<h1><%= new java.util.Date() %></h1>

其实jsp等价于servlet。上述语句实际上被转换为:

out.write("<h1>");
out.println(new java.util.Date());

部署

<servlet>
    <servlet-name>a<servlet-name>
    <jsp-file>/WEB-INF/jsps/a.jsp</jsp-file>
</servlet>

jsp语法

<%@ page import="java.util.*", "java.io.*" 
    pageEncoding="utf-8"
    isErrorPage="true|false"
    errorPage="/error.jsp"
    session="true"
    %>

其中的isErrorPage表示当前页面是否是出错页面。

errorPage代表如果当前页面出错则应该访问的页面。

不必担心抛出异常。所有的语句都会被放在一个巨大的try catch块里。

剩下的自己学去。(jsp implicit object)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值