通过新闻发布系统学习页面静态化
在一个网站运行的期间,要实现高效快速的访问一个页面,首先需要解决的就是网站的加载问题,这里的加载是网页上需要动态加载的一些东西,诸如图片,视频,文字,当我们访问这些网站的时候,网站后台的服务器负责加载这些资源,在我们平时写小项目的时候,可能都不需要考虑这个,因为,每次都是你一个用户在来回的发布,访问项目,一个页面加载的资源可能很快就完事了。MVC设计模式提倡把一个项目的不同功能分开,不同的层写不同的代码,显示给用户的只有V,也就是view视图层,往往项目中的视图层都是jsp编写的(可能还有php,asp),这里说一下平时的jsp页面是怎么执行,怎么就能在页面上显示呢?用户的一个请求过来之后,服务器根据用户的请求加载jsp页面,而这个jsp页面会被服务器这个web容器(jsp引擎)转换成Servlet,显然Servlet是纯java代码写得,接下来就是java编译成class文件,执行,最后显示在页面上。例如在Tomcat中的根目录下的work文件夹中可以看到jsp转换后的servlet代码。当我们访问一个jsp页面的时候要经历这么多步骤,一个人访问这个页面可以,但是当很多的人同时访问某个jsp页面的时候,例如某某电商网站促销期间访问量都是上亿级别的,如果用户访问一个页面的时候,每个图片都是一个一个去加载,然后返回给用户,这个量就很大了,我想无论哪个服务器都会受不了的。现在有一种方式可以减少这种资源加载带来的影响,页面静态化(当然一个大型的网站后台不可能只利用静态化就能搞定一切,还涉及到负载均衡,分布式存储等等技术)。把一切的页面都转换成html静态页面,静态页面和动态页面的区别就是没有进行数据的交互,数据和内容都是写死的,这样在访问一个页面的时候就是一个写好的html页面,想想一个html页面我们通过浏览器直接就可以打开,而jsp页面呢?是需要服务器转换编译的才可以在浏览器上看到。在平时逛电商网站的时候,可能就会注意到我们能看到的所有页面都是后缀名为.html的页面(可以看看),这样的每个页面都不是一个个网页设计人员每个图片贴上去的,而是后台一个动态页面先加载资源文件后生成静态页面,这样我们在访问的时候都是这个静态页面了。
说了这么多页面静态化的好处及特点,接下来就是这个新闻发布系统了,我们在电脑上浏览新闻网站的时候,可能也会注意到,他们都是静态页面,对于新闻这种发布之后几乎就不需要更改的页面静态化是很基本的操作了。
本新闻发布系统利用了MVC设计模式,用jsp/servlet技术开发,利用了文本编辑器来生成新闻的段落标签(很好用),在后台直接就可以获取,详见:我的另一篇文章,详细介绍了怎么集成文本编辑器。
用到了一个实体类News,用来存储新闻的一些基本信息。
package com_domain; /** * 新闻实体类 * Created with IDEA * author:DuzhenTong * Date:2017/11/24 * Time:15:41 */ public class News { private int id; //作者 private String author; //类型 private String type; //标题 private String title; //模板一路径 private String path1; //模板二路径 private String path2; public News() { } public News(int id, String author, String type, String title, String path1, String path2) { this.id = id; this.author = author; this.type = type; this.title = title; this.path1 = path1; this.path2 = path2; } @Override public String toString() { return "News{" + "id=" + id + ", author='" + author + '\'' + ", type='" + type + '\'' + ", title='" + title + '\'' + ", path1='" + path1 + '\'' + ", path2='" + path2 + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getPath1() { return path1; } public void setPath1(String path1) { this.path1 = path1; } public String getPath2() { return path2; } public void setPath2(String path2) { this.path2 = path2; } }数据库操作类Dao:主要用来存储新闻基本信息,分页查询
package com_dao; import com_domain.News; import com_util.JdbcUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; /** * 数据库操作类 * Created with IDEA * author:DuzhenTong * Date:2017/11/23 * Time:9:01 */ public class Dao { /** * 插入新闻的基本信息 * @param news 新闻对象 */ public static void insert(News news) { Connection connection = JdbcUtil.getInstance().getConnection(); PreparedStatement preparedStatement = null; String sql = "insert into newslink values(default,?,?,?,?,?)"; try { preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, news.getAuthor()); preparedStatement.setString(2, news.getType()); preparedStatement.setString(3, news.getTitle()); preparedStatement.setString(4, news.getPath1()); preparedStatement.setString(5, news.getPath2()); preparedStatement.execute(); } catch (SQLException e) { e.printStackTrace(); } finally { JdbcUtil.releaseResources(connection, preparedStatement, null); } } /** * 获取新闻总数,分页时使用 * @return */ public static int count() { Connection connection = JdbcUtil.getInstance().getConnection(); String sql = "select count(*) from newslink"; PreparedStatement preparedStatement = null; ResultSet resultSet = null; int count = 0; try { preparedStatement = connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { count = resultSet.getInt(1); } } catch (SQLException e) { e.printStackTrace(); }finally { JdbcUtil.releaseResources(connection, preparedStatement, resultSet); } return count; } /** * 分页查询 * @param currentPage 当前页 * @param limit 每页显示条数 * @return */ public static List listPage(int currentPage,int limit) { List list = new ArrayList(); Connection connection = JdbcUtil.getInstance().getConnection(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; String sql = "select * from newslink limit ?,? "; try { preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1, currentPage); preparedStatement.setInt(2, limit); resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { News news = new News(); news.setAuthor(resultSet.getString(2)); news.setType(resultSet.getString(3)); news.setTitle(resultSet.getString(4)); news.setPath1(resultSet.getString(5)); news.setPath2(resultSet.getString(6)); list.add(news); } } catch (SQLException e) { e.printStackTrace(); }finally { JdbcUtil.releaseResources(connection, preparedStatement, resultSet); } return list; } }
先看添加新闻的页面:
点击提交模板后表单跳转到WriteServlet中进行处理:包括获取页面中的基本信息,新闻内容,对于同一篇新闻提供了两种模板阅读,Html01和Html02是分别生成两个模板的静态化处理的工具类。这里的网页路径为什么会写成localhost:63342,因为这个项目把生成的静态页面通过绝对路径放在了项目的文件夹下,在访问这些静态页面的时候,浏览器对于绝对路径是无法访问的(处于安全策略,火狐及谷歌都试了不可以),通过测试在一个html页面中加入一个超链接,点击之后访问的主机和端口号是localhost:63342,绝对路径访问的问题搞了三天……
访问链接实例图:
package com_servlet; import com_domain.News; import com_io.HtmlIO1; import com_io.HtmlIO2; import java.io.IOException; /** * Created with IDEA * author:DuzhenTong * Date:2017/11/21 * Time:9:28 */ public class WriteServlet extends javax.servlet.http.HttpServlet { @Override protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { request.setCharacterEncoding("UTF-8"); String news = request.getParameter("wysiwyg"); String type = request.getParameter("type"); String title = request.getParameter("title"); String author = request.getParameter("author"); News news1 = new News(); news1.setAuthor(author); news1.setType(type); news1.setTitle(title); news1.setPath1("http://localhost:63342/news/web/html/" + title + "1.html"); news1.setPath2("http://localhost:63342/news/web/html/" + title + "2.html"); HtmlIO1.write(news1, news); HtmlIO2.write(news1, news); System.out.println(title); response.sendRedirect("/ListPageServlet"); } @Override protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doPost(request, response); } }生成静态页面用到了最简单的拼字符串,拼成一个html页面的代码,io写入,粘入部分代码,大部分代码都是html代码所以不贴了,代码放在了github可以下载看
Dao.insert(news1);//插入数据库 String path = news1.getPath1(); File file = new File("E:\\IntelliJ IDEA\\news\\web\\html\\" + news1.getTitle() + "1.html"); if (!file.exists()) { //文件不存在则创建文件,先创建目录 File dir = new File(file.getParent()); dir.mkdirs(); file.createNewFile(); System.out.println("写入!!!"); } Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")); writer.write(body.toString()); writer.flush(); writer.close();在生成静态页面后跳转到新闻列表页面,ListPageServlet用于分页
package com_servlet; import com_dao.Dao; import com_domain.News; import com_util.Page; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; /** * 生成新闻列表,分页 * Created with IDEA * author:DuzhenTong * Date:2017/11/25 * Time:10:27 */ @WebServlet(name = "ListPageServlet") public class ListPageServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Page page = new Page(); String pages = request.getParameter("pages"); int limit = page.getLimit(); List list = null; int totalpage = Dao.count() % limit == 0 ? Dao.count() / limit : Dao.count() / limit + 1; if (pages == null) { page.setCurrentPage(1); list = Dao.listPage((page.getCurrentPage() - 1) * limit, limit); request.setAttribute("p", 1); }else{ int p = Integer.parseInt(pages); if (p <= 0) { p = 1; } if (p >= totalpage) { p = totalpage; } page.setCurrentPage(p); list = Dao.listPage((p - 1) * limit, limit); request.setAttribute("p", p); } page.setTotalPages(totalpage); request.setAttribute("list", list); request.setAttribute("pagecount", page.getTotalPages()); request.getRequestDispatcher("/ToHtmlServlet").forward(request, response); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }上面的ListPageServlet跳转到了一个ToHtmlServlet中,这个Servlet用于把分页显示的jsp页面转换成html页面(不是通过拼字符串的),这样分页后的新闻列表也是一个静态页面了,虽然每次都要从新生成。
package com_servlet; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; 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; import javax.servlet.http.HttpServletResponseWrapper; import java.io.*; /** * Created with IDEA * author:DuzhenTong * Date:2017/11/27 * Time:18:09 */ @WebServlet(name = "ToHtmlServlet") public class ToHtmlServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); ServletContext sc = getServletContext();//为你的应用的上下文路径。 // 则你访问这个servlet时加参数.如http://localhost/test/toHtml?fileName=index String url = "/newsList.jsp";// 你要生成的页面的文件名。扩展名为jsp String name = sc.getRealPath("/newsList.html");// 这是生成的html文件名 RequestDispatcher rd = sc.getRequestDispatcher(url); final ByteArrayOutputStream os = new ByteArrayOutputStream(); final ServletOutputStream stream = new ServletOutputStream() { @Override public void write(byte[] data, int offset, int length) { os.write(data, offset, length); } @Override public void write(int b) throws IOException { os.write(b); } }; final PrintWriter pw = new PrintWriter(new OutputStreamWriter(os)); HttpServletResponse rep = new HttpServletResponseWrapper(response) { @Override public ServletOutputStream getOutputStream() { return stream; } @Override public PrintWriter getWriter() { return pw; } }; rd.include(request, rep); pw.flush(); FileOutputStream fos = new FileOutputStream(name); // 把jsp 输出的内容写到xxx.html os.writeTo(fos); fos.close(); PrintWriter out = response.getWriter(); response.sendRedirect("/newsList.html"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
新闻列表图:提供两种模板阅读
项目源代码放到了github上:https://github.com/Ai-yoo/news