本项目已经完结,项目日期3月10日至3月17日,历时8天,在三实验楼207机房和作者寝室完成该项目。您可以访问以下链接体验该项目,发现有任何bug欢迎向我反馈。
一、项目概述
本项目为一个简易的新闻门户网站,该门户网站需要实现新闻网站的基础功能。前端页面使用的是现成的,极其丑陋,还望见谅。
前台页面需要为读者展示新闻分类、展示新闻标题,其中新闻的排序要按照发布时间最新的新闻呈现在前。点击相应的分类可以显示该分类下的新闻,点击新闻标题可以进入该条新闻的阅读页面,在阅读页面中可以发表对该条新闻的评论。同时前台页面还要提供管理员登录入口。
后台页面为新闻管理员操作页面,需要管理员登录后才能进入,具有添加新闻、编辑新闻、添加新闻分类、编辑新新闻分类分、管理新闻评论这5个功能。
本项目要对以上的功能进行实现,并确保后台页面在管理员未登录的情况下不能进入。
二、技术实现
1.前端页面:使用JSP页面作为视图供用户访问,并结合HTML、CSS、JavaScript前端三大件展示出完整的用户页面。对于需要与后台进行交互的请求,使用form表单或超链接来实现跳转到后台的Servlet中,对于动态参数则使用EL表达式结合JSTL标签来进行获取。Servlet完成相关请求后将处理结果返回给前端JSP页面,前端页面通过前端三大件结合EL表达式将相关信息展示给用户。
2.后台:后台开发使用JavaEE三层架构,即web层、services层和dao层。
web层使用servlet技术,通过servlet接收用户从前端页面提交的信息,分析用户正在执行的操作,将这些信息封装为Java对象或者相关的参数,传递给services层对应的方法进行业务处理。
services层实现业务逻辑,即对新闻门户的各个功能模块需要完成的操作通过Java代码做具体的实现,如果某个功能需要进入数据库完成操作,则交给dao层进行处理。
dao层是对数据进行增删改查,这里要用到JDBC来实现对数据库的操作,然后将增删改查的结果返回给services层。
3.JDBC:JDBC是通过Java对MySQL数据库的连接,用于对数据库实现增删改查。这里我使用了阿里巴巴的Druid连接池和Spring框架下的JdbcTemplate技术,大幅减少了代码量,因为我们的目的仅仅是为了执行SQL语句,而不是使用大量的语句浪费在对数据库进行连接和资源释放。
4.domain层:对用户、新闻、评论、分类分别创建JavaBean,并结合BeanUtils工具类,在需要封装对象时实现快速封装,从而免去了手动调用setter方法,节省了代码量。
5.Junit单元测试:当我们实现一个功能却不知道我们写的代码正确与否,但是当时的代码并不足以让我们去实际环境进行测试时,可以使用Junit单元测试,检测我们代码的正确性。
6.项目构建:项目构建使用Maven,包括Tomcat运行环境同样从Maven插件中获取,最新版本为Tomcat7。所有依赖jar包也从Maven中央仓库获取,省去了自己导入jar包的过程,当项目转移到其他电脑时也可以快速构建项目。由于Maven中央仓库服务器在国外,为了加快下载速度,配置镜像服务器为阿里云服务器,这样可以提升依赖下载速度。
综上所述,本项目一共用到的技术有:
Maven、JavaSE基础、JSP、EL表达式、JSTL标签库、Servlet、JavaBean、BeanUtils快速封装JavaBean对象、JDBC、Druid连接池、JdbcTemplate。
所需要的全部依赖JAR包如下,通过配置Maven的pom.xml配置文件,这些依赖会自动进行下载。
三、项目结构
1.用户页面和管理员页面
包含了前端三大件(HTML、CSS、JavaScript),但这三大件我们几乎不对其进行修改,完成用户请求提交的页面均是JSP页面。
用户页面:主页、新闻阅读页面。
后台管理页面:后台管理主页、显示新闻列表、新闻添加、新闻编辑、分类添加、分类修改功能。
2.数据库结构
数据库使用MySQL数据库,共4张表:评论表、新闻表、分类表、用户表,各个表所具有的字段如图所示。
评论表(comments):保存每一条新闻评论的详细信息,其中cnid属性作为外键约束关联news表的主键nid,一条新闻可以有多条评论。
新闻表(news):保存每一条新闻的详细信息,其中ntid属性作为外键约束关联topic表的主键tid,一个分类可以有多条新闻。
分类表(topic):保存每一个新闻分类,例如:国内新闻、国际新闻、娱乐新闻等。
用户表(news_users):保存用户名和密码。
3.Java代码结构
domain层:存放各个实体类的JavaBean。
util层:存放JDBC工具类,大大减少执行SQL语句的代码量。
servlet层:接收用户在前端JSP页面发出的请求,并将参数传递给services层执行相应方法。
services层:完成新闻门户各个模块的业务逻辑具体实现。
dao层:对数据进行增删改查,执行SQL语句。
filter层:拦截用户请求,判断请求是否非法,此处用于检测用户是否登录和敏感词汇过滤。
test层:需要进行单元测试的功能在该层进行测试。
四、功能实现
项目源代码已经发布在gitee,可以直接clone到本地,使用IDEA运行代码。以下文章不会大段大段的上传每一个Java文件的源代码,需要看源码请自行在仓库中浏览或者下载。
1.项目构建
配置pom.xml文件,添加tomcat7运行环境和本项目相关依赖。代码较长,请访问源码链接查看。
2.新闻门户首页的实现
首页效果如图所示,主要难点在于
- 用户访问网站后如何直接访问显示主页的servlet
- 中间部分能够显示所有的新闻分类,并且能够通过点击分类,切换不同类型的新闻。
- 在不点击具体分类的情况下,按照时间顺序展示所有新闻。点击了分类后,按照时间顺序展示该分类下的新闻。
- 新闻列表可以分页展示,每页20条。仅当分类下存在新闻时才显示页码和换页按钮。
- 左侧分别显示指定分类的新闻。
具体实现代码:
1.用户访问后直接进入主页
写一个IndexServlet类,该类的功能就是实现自动跳转到newsServlet获取全部新闻。
package servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 实现首页跳转 */ public class IndexServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("newsServlet?method=getIndexByTopic&pageNo=1&size=20&tid=0").forward(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } }
然后在WEB-INF/web.xml文件中配置当用户直接访问该项目时,进入IndexServlet,在indexServlet中又一次进行跳转,这样就实现了当用户访问后自动进入newsServlet。
<welcome-file-list> <welcome-file>indexServlet</welcome-file> </welcome-file-list> <servlet> <servlet-name>IndexServlet</servlet-name> <servlet-class>servlet.IndexServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>IndexServlet</servlet-name> <url-pattern>/indexServlet</url-pattern> </servlet-mapping>
2.实现分类分页显示新闻
后端servlet代码如下,位于NewsServlet中。在用户访问首页后,通过IndexServlet,直接跳转到了newsServlet?method=getIndexByTopic&pageNo=1&size=20&tid=0
,这样就进入了以下方法。
if (method.equals("getIndexByTopic")) {//通过新闻分类显示新闻列表 //获取所有topic List<Topic> topicList = topicServices.getAllTopic(); session.setAttribute("allTopic", topicList); //获取页码、每页条数、获取具体新闻类型 String pageNo = request.getParameter("pageNo"); String size = request.getParameter("size"); String tid = request.getParameter("tid"); //获取该类型的新闻有多少条,计算需要多少页来展示 int countNews = newsServices.countNews(Integer.parseInt(tid)); int totalPages = (int) Math.ceil(countNews / Double.parseDouble(size)); //将获取到的页码、每页条数、获取具体新闻类型存入request域 request.setAttribute("pageNo", Integer.parseInt(pageNo)); request.setAttribute("totalPages", totalPages); request.setAttribute("tid", Integer.parseInt(tid)); //获取具体类型的新闻对象并存入request域 List<News> newsList = newsServices.getNewsByNumber(Integer.parseInt(tid), Integer.parseInt(pageNo), Integer.parseInt(size)); request.setAttribute("newsList", newsList); request.getRequestDispatcher("/index.jsp").forward(request, response); }
前端代码如下,位于index.jsp中
<div class="content"> <ul class="class_date"> <!-- 新闻主题链接区域 --> <%--使用jstl处理集合数据--%> <c:forEach items="${allTopic}" var="topic" varStatus="i"> <a href="newsServlet?method=getIndexByTopic&pageNo=1&size=20&tid=${topic.tid}" class="topic"><b>${topic.tname}</b></a> <c:if test="${i.count % 9 == 0}"> <li id="class_month"></li> </c:if> </c:forEach> </ul> <ul class="classlist"> <!-- 分页显示新闻区域 --> <%--判断是否有新闻数据--%> <c:choose> <c:when test="${empty newsList}"> <li>暂无新闻数据</li> </c:when> <c:otherwise> <c:forEach items="${newsList}" var="news"> <li> <a href="newsServlet?method=readNews&nid=${news.nid}">${news.ntitle}</a> <span>${news.nmodifyDate}</span> </li> </c:forEach> </c:otherwise> </c:choose> <c:if test="${not empty newsList}"> <p align="center" style="font-size: 14px;">第${pageNo}页 共${totalPages}页</p> </c:if> <a href="newsServlet?method=getIndexByTopic&pageNo=1&size=20&tid=0">全部</a> <c:if test="${pageNo != 1}"> <a href="newsServlet?method=getIndexByTopic&pageNo=1&size=20&tid=${tid}">首页</a> </c:if> <c:if test="${pageNo != 1}"> <a href="newsServlet?method=getIndexByTopic&pageNo=${pageNo-1}&size=20&tid=${tid}">上一页</a> </c:if> <c:set var="totalPagesValue" value="${totalPages}"/> <c:if test="${pageNo != totalPagesValue && totalPagesValue != 0}"> <a href="newsServlet?method=getIndexByTopic&pageNo=${pageNo+1}&size=20&tid=${tid}">下一页</a> </c:if> <c:if test="${pageNo != totalPagesValue && totalPagesValue != 0}"> <a href="newsServlet?method=getIndexByTopic&pageNo=${totalPages}&size=20&tid=${tid}">末页</a> </c:if> </ul> </div>
(1)显示所有分类:可以看到第3行调用了TopicServices层的getAllTopic方法获取所有的新闻分类并将每个分类封装成了Topic对象,存储在了一个List集合当中。然后我们将这个List集合存入了session中,并命名键名为allTopic,这样前端页面就能通过EL表达式获取到这个List集合中的所有Topic对象,再通过Topic对象的getter方法可以获取到分类名称,将所有的分类名称显示在页面上。
(2)新闻分页显示:跳转时指定了参数pageNo=1&size=20&tid=0
,意思是当前页码第1页,每页显示20条,分类id为0。这三个参数可以被newsServlet接收,通过NewsServices层的countNews方法计算出总页数,然后将页码、总页数、新闻类型存入request域,这样主页就能通过EL表达式获取到当前页码和总页数并显示在页面上,新闻类型用于保存当前处于哪个分类,翻页时也只需要对页码进行加减,并通过EL表达式获取到分类名称,对newsServlet再次发送getIndexByTopic
请求,获取下一页的新闻。
(3)分类显示新闻:由于每一个分类的Topic对象均已经存入session,这样每个分类都能通过EL表达式获取到自己的tid值,这样我们只需要将相应的tid参数修改为具体分类的tid值,传递到newsServlet层,就可以实现分类显示新闻。如果该分类下没有新闻,则显示"暂时没有新闻",并且不显示页码和翻页按钮,此需求通过EL表达式的判断是否为空来决定是否显示。
(4)按时间顺序显示新闻:此需求需要在对数据库查询时实现,通过SQL语句的sort by限制条件,可以指定排列方式。具体在NewsDao中的实现如下
public List<News> getNewsByNumber(int tid, int no, int size) { String sql = "SELECT * FROM news where ntid = ? ORDER BY nmodifyDate DESC LIMIT ?,?"; //tid为0时代表查询所有新闻 if (0 == tid) { sql = "SELECT * FROM news ORDER BY nmodifyDate DESC LIMIT ?,?"; List<News> newsList = template.query(sql, new BeanPropertyRowMapper<News>(News.class), (no - 1) * size, size); return newsList; } List<News> newsList = template.query(sql, new BeanPropertyRowMapper<News>(News.class), tid, (no - 1) * size, size); return newsList; }
3.新闻阅读页面的实现
后端servlet代码如下,位于NewsServlet中。当用户点击新闻标题时,链接地址是newsServlet?method=readNews&nid=${news.nid}
,其中这样就能进入NewsServlet中的readNews方法,指定新闻nid的值在数据库中查询新闻的详细信息,将查询结果封装为News对象返回。
同时,新闻页面还会显示评论,由于comments表的cnid关联了news表的nid,我们同时可以调用CommentsServices层的getCommentsByCnid方法获取这一条新闻的评论,并将这些评论封装为Comments对象,存储在List集合中。
最后将News对象和Comments对象存储到request域中,这样在前端页面,就能通过EL表达式获取到对象中的属性,并展示在页面上。
else if (method.equals("readNews")) {//新闻阅读页面 //获取新闻的nid,通过nid查询新闻的全部信息,封装News对象 String nid = request.getParameter("nid"); News newsByNid = newsServices.getNewsDetailsByNid(Integer.parseInt(nid)); //将查询到的新闻存入session request.setAttribute("news", newsByNid); //获取评论 //1.进入服务层获取评论对象 List<Comments> commentsByCnid = commentsServices.getCommentsByCnid(Integer.parseInt(nid)); //2.将评论对象存入request域 request.setAttribute("comments", commentsByCnid); //跳转到新闻阅读页面 request.getRequestDispatcher("/newspages/news_read.jsp").forward(request, response); }
前端代码为news_read.jsp,代码较长,此处不放代码,源码链接如下
4.添加评论的实现
添加评论的功能在新闻阅读页面就要实现,采用表单提交的方式,跳转到CommentsServlet中完成处理,前端代码就在上面的news_read.jsp源码中,后端的Servlet代码如下。servlet接收评论的各项参数,通过BeanUtils封装Comments对象,传递给CommentsServices层进行处理。
if (method.equals("addComment")) {//添加评论 Map parameterMap = request.getParameterMap(); //封装comment对象 Comments comment = new Comments(); BeanUtils.populate(comment, parameterMap); //添加时间 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = sdf.format(new Date()); comment.setCdate(date); //进入服务层添加评论到数据库 int count = commentsServices.addComments(comment); System.out.println("有" + count + "条评论被添加:" + comment); response.sendRedirect(request.getContextPath() + "/newsServlet?method=readNews&nid=" + comment.getCnid()); }
5.登录到后台管理页面和退出登录的实现
此功能同样采用表单提交的方式,在主页和新闻阅读页面设置登录入口,以表单形式提交用户名和密码,将用户名和密码封装为User对象,交由UserServlet进行处理,在UserServices层中判断用户名和密码是否正确,如果正确返回该用户的User对象,并将这个对象保存到Session中,为后续做用户是否登录的判断做铺垫。登陆失败则将错误信息存储到request域,在前端使用EL表达式展示出错误信息。
退出登录,则直接在session中移除已经登录的User对象即可,为什么这么做在后面会写。
if (method.equals("login")) {//用户登录 //获取用户发送的数据 Map<String, String[]> parameterMap = request.getParameterMap(); //使用BeanUtils封装正在登录的用户 User loginUser = new User(); BeanUtils.populate(loginUser, parameterMap); //进入服务层判断用户密码是否正确 User user = userServices.login(loginUser); if (user == null) { //登录失败,写入错误信息,跳转回首页 request.setAttribute("loginError_msg", "很高兴,你的用户名或密码错误"); request.getRequestDispatcher("/indexServlet").forward(request, response); } else { //登录成功 //获取所有topic List<Topic> allTopic = topicServices.getAllTopic(); //将User对象和topic列表分别存入session和request域 session.setAttribute("user", user); request.setAttribute("topicList", allTopic); request.getRequestDispatcher("/newspages/admin.jsp").forward(request, response); } } else if (method.equals("loginOut")){//退出登录 //在session中移除user对象 session.removeAttribute("user"); //存储提示信息 request.setAttribute("loginError_msg", "您已退出登录"); //跳转到主页 request.getRequestDispatcher("/indexServlet").forward(request, response); }
前端页面代码:
<form action="userServlet/news" method="post" onsubmit="return check()"> <input type="hidden" name="method" value="login"/> <label> 登录名 </label> <input type="text" name="uname" value="" id="uname" class="login_input"/> <label> 密码 </label> <input type="password" name="upwd" value="" id="upwd" class="login_input"/> <input type="submit" class="login_sub" value="登录"/> <label id="error" style="color: red">${loginError_msg}</label> <a href="indexServlet" class="login_link">返回首页</a> <img src="images/friend_logo.gif" alt="Google" id="friend_logo"/> </form>
6.后台管理页面:管理新闻分类的实现
先说一下其中一个功能:显示所有分类页面的展示,首先要显示出所有分类,这个通过servlet即可实现,在servlet中调用TopicServices层的方法,代码如下
else if (method.equals("refreshTopic")) {//刷新分类页面 //获取所有topic List<Topic> topicList = topicServices.getAllTopic(); //将topic列表存入request域 request.setAttribute("topicList", topicList); request.getRequestDispatcher("/newspages/admin.jsp").forward(request, response); }
前端页面则通过EL表达式结合JSTL的forEach循环,将所有的分类展示在页面上
<c:forEach items="${topicList}" var="topic"> <li>      ${topic.tname}      <a href='../newspages/topic_modify.jsp?method=modifyTopic&tid=${topic.tid}&tname=${topic.tname}' class="tpsMdfLink" id='${topic.tid}:${topic.tname}'>修改</a> <a href='../adminServlet/news?method=deleteTopic&tid=${topic.tid}&tname=${topic.tname}' class="tpsDelLink" id='${topic.tid}'>删除</a> </li> </c:forEach>
删除分类、修改分类、添加分类这三个功能,实现原理是一样的,均是通过特定的servlet链接或者表单,先进入Servlet获取相关参数,然后去Services层调用相关的方法,再去Dao层对数据库进行操作,操作完成后逐层返回,根据返回值结果判断是否操作成功,将操作成功的消息存入request域,在前端页面使用EL表达式展示消息。这里就不做演示了。
7.后台管理页面:管理新闻的实现
修改新闻,我们要先获取到这一条新闻的News对象,获取到News对象后,将News对象存入request域,然后在新闻编辑页面通过EL表达式获取News对象的各个属性,就能显示出新闻的各项信息和具体内容显示在JSP页面了。
else if (method.equals("showNewsDetailsByNid")) {//获取新闻的详细信息展示在编辑新闻页面上 //获取需要修改的新闻nid String nid = request.getParameter("nid"); //获取所有topic,用于所有显示新闻类型 List<Topic> allTopic = topicServices.getAllTopic(); //topic列表存入request域 request.setAttribute("topicList", allTopic); //根据nid进入服务层获取News对象 News news = newsServices.getNewsDetailsByNid(Integer.parseInt(nid)); //将News对象存入request域 request.setAttribute("news", news); //获取评论 //1.进入服务层获取评论对象 List<Comments> commentsByCnid = commentsServices.getCommentsByCnid(Integer.parseInt(nid)); //2.将评论对象存入request域 request.setAttribute("comments", commentsByCnid); //跳转到新闻编辑页面 request.getRequestDispatcher("/newspages/news_modify.jsp").forward(request, response); }
前端页面则是点击需要修改的新闻后,访问../adminServlet/news?method=showNewsDetailsByNid&nid=${news.nid}
,进入新闻编辑页面。
分页显示所有新闻的功能,与前面前端用户页面实现分页显示的实现原理完全相同。
添加新闻,通过填写表单-提交到Servlet-Servlet封装新闻对象-Services层处理-Dao层添加新的新闻到数据库的方式。
删除新闻则更简单,只需要获取到新闻的nid值,通过SQL语句删除即可。
8.当用户直接通过链接访问后台时,自动检查用户是否已经登录,如果未登录则跳转回首页
此功能通过Filter过滤器实现,拦截的链接有以下链接(在web.xml文件填写),因为这些链接是涉及到后台页面的。
<filter> <filter-name>LoginFilter</filter-name> <filter-class>filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/adminServlet/news</url-pattern> <url-pattern>/newspages/admin.jsp</url-pattern> <url-pattern>/newspages/news_add.jsp</url-pattern> <url-pattern>/newspages/news_modify.jsp</url-pattern> <url-pattern>/newspages/topic_modify.jsp</url-pattern> <url-pattern>/newspages/topic_add.jsp</url-pattern> <url-pattern>/newspages/showNews.jsp</url-pattern> <url-pattern>/newspages/showTopics.jsp</url-pattern> </filter-mapping>
实现思路为,当用户登录成功后,会将登录的User对象存入Session,在过滤器中判断Session中是否存在User对象即可,如果存在则放行,不存在则跳转到首页,并显示“请先登录”。用户退出登录后,会清除Session中的User对象。
代码实现如下:
public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //强制转换为HttpServletRequest HttpServletRequest request = (HttpServletRequest) req; //1.获取资源的请求路径(谁请求这个资源) String uri = request.getRequestURI(); //2.判断是否包含与登录相关的资源路径,并排除css/jd/图片/验证码等资源 if(uri.contains("userServlet")){ //如果包含,说明用户就是想去登录,直接放行 chain.doFilter(req, resp); }else{ //如果不包含,说明用户并不是在执行登录操作,需要判断用户是否已经登录 //3.从Session中获取User对象 Object user = request.getSession().getAttribute("user"); if (user != null){ //用户已登录,放行 chain.doFilter(req,resp); }else { //用户未登录,跳转回主页 request.setAttribute("loginError_msg","请先登录"); request.getRequestDispatcher("/indexServlet").forward(req,resp); } } } }
9.过滤评论中的敏感词汇
使用一个文件来保存敏感词汇,读取这个文件,当用户发表的评论出现了文件中的敏感词汇时,自动将敏感词汇替换为"***"显示在页面上。
实现思路为使用Filter拦截访问CommentsServlet的请求,这些请求都是发表评论的请求。拦截这些请求后,做动态代理,在动态代理中对getParameter
和getParameterMap
方法进行增强。如果获取到的参数值有敏感词汇,则将敏感词汇替换为"***"后,再将参数值返回,这样存入数据库的评论中敏感词汇也会被***代替。
此处有一个坑,对getParameterMap
方法增强时,如果出现敏感词汇,是不能直接通过Map集合的put方法直接试图覆盖原来的参数的值的,具体原因看以下文章。
动态代理踩坑:java.lang.IllegalStateException: No modifications are allowed to a locked ParameterMap
public class SensitiveWordsFilter implements Filter { private List<String> wordsList = new ArrayList<String>();//敏感词汇列表 public void init(FilterConfig config) throws ServletException { try { //加载敏感词汇 //1.获取文件路径 URL resource = SensitiveWordsFilter.class.getResource("words.txt"); String path = resource.getPath(); //2.读取文件 InputStreamReader isr = new InputStreamReader(new FileInputStream(path), "utf-8"); BufferedReader br = new BufferedReader(isr); //3.将文件的每一行数据添加到list中 String line = null; while (null != (line = br.readLine())) { wordsList.add(line); } br.close(); System.out.println(wordsList); } catch (IOException e) { e.printStackTrace(); } } public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { //1.创建代理对象,增强获取参数值的方法(该方法用于获取表单填写的信息) /** * 动态代理增强Request对象 * 三个固定参数: * 1.类加载器:真实对象.getClass().getClassLoader() * 2.接口数组:真实对象.getClass().getInterfaces() * 3.处理器:new InvocationHandler() */ ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() { /** * 代理逻辑编写的方法:代理对象调用的所有方法都会触发该方法执行 * @param proxy 代理对象 * @param method 代理对象调用的方法,被封装为一个对象 * @param args 代理对象调用方法时,传递的所有实参,被封装为一个数组 * @return * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断是否是getParameter方法 if (method.getName().equals("getParameter")) { /* * String getParameter(String name) */ String value = (String) method.invoke(req, args);//返回从表单获取到的属性值 //增强返回值 if (null != value) { //有返回值,遍历敏感字符 for (String word : wordsList) { //返回值中包含敏感字符,替换为*** if (value.contains(word)) { value = value.replaceAll(word, "***"); } } } return value;//返回被修改后的返回值 //判断是否是getParameter方法 } else if (method.getName().equals("getParameterMap")) { /* * Map<String,String[]> getParameterMap() */ Map<String,String[]> map = new HashMap<String,String[]>((Map) method.invoke(req, args));//返回从表单获取到的由属性名和属性值组成的map if (!map.isEmpty()) { //遍历map Set<String> keySet = map.keySet(); for (String key : keySet) { //获取每一个属性名对应的属性值 String[] valueArray = map.get(key); //遍历敏感词汇 for (String word : wordsList) { String value = String.valueOf(valueArray[0]); if (value.contains(word)){ //如果属性值包含敏感词,将敏感词替换为"***" valueArray[0] = value.replaceAll(word, "***"); map.put(key,valueArray); } } } } return map; } else { Object obj = method.invoke(req, args); return obj; } } }); //放行时要放行代理对象 chain.doFilter(proxy_req, resp); } }
五、总结
本项目使用了Maven、JavaSE基础、JSP、EL表达式、JSTL标签库、Servlet、JavaBean、BeanUtils快速封装JavaBean对象、JDBC、Druid连接池、JdbcTemplate技术,是一个综合性项目,对于我来说是一个非常好的练手项目,巩固了JavaWeb的学习,加深了对Servlet的理解,初步实践了JavaEE三层架构的思想,并学会了使用Maven构建一个项目,这些对我将来的Java框架学习之路打下了一个基础。
本项目已经上传到服务器,可以直接访问,欢迎体验:AnonyEast's 新闻门户 http://news.anonyeast.top
4 Comments
棒~
@可可爱爱没脑袋的小仙女 嘻嘻
杨东,做的很好,进行努力。。。达内李胤东
值得学习 继续努力???