06Web开发设计模式—MVC&JavaBean
为了解决web资源开发技术:演变过程
//TAB 右移 ctrl+TAB 左移 ,在User.java中设一个校验数据的方法。
一、Servlet技术:在Servlet中拼接HTML/JS内容时十分不方便。
二、JSP:改变了Servlet在Java代码中拼写HTML代码的过程,改变了在HTML中拼写java代码;但是在HTML内容中嵌入大量java代码,仍然会导致java代码和HTML代码混杂在一起,不方便开发维护。
JSP和Servlet1、相同点:
JSP看作为一个特殊的Servlet,它只不过是对Servlet的扩展,只要JSP可以完成的工作,使用Servlet都可以完成,
例如生成动态页面。由于JSP页面最终被转化为Servlet来运行,因此处理请求实际上是编译后的Servlet。
2、不同点:
(1)、Servlet的实现方式是在JAVA中嵌入HTML代码,编写和修改HTML非常不方便,所以它适合做流程控制、业务处理;
而JSP的实现方式是在HTML中嵌入JAVA代码,比较适合页面的显示。
例如:在Struts框架中,Servlet位于MVC设计模式的控制层,而JSP位于视图层。
(2)、Servlet没有内置对象,而JSP中的内置对象都是必须通过HttpServletRequest对象、HttpServletResponse
对象以及HttpServlet对象得到。
三、JSP+JavaBean:模式一,利用JavaBean将大量代码提取走,JSP负责接受请求、调用程序和展示页面,JavaBean负责封装数据、处理数据。
过程: 浏览器来访问的时候,请求交给JSP来处理,JSP把其中用户相关的数据封装到JavaBean中,数据封装后,JavaBea提供处理数据的方法,处理好后返回给JSP,JSP拿到处理好的数据展示给用户。
四、Servlet+JSP+JavaBean:模式二,Servlet负责接受请求、控制程序流转,JavaBean负责封装数据、处理数据,JSP负责展示页面;
在这种开发模式下,各个组建都只做自己擅长的事情,从而使程序具有更好的结构性,从而方便开发和维护。 MVC设计模式
过程:浏览器来访问的时候,请求交给Servlet来处理,Servlet把其中用户相关的数据封装到JavaBean中,数据封装后,JavaBean提供处理数据的方法,处理好后将数据返回给Servlet;Servlet拿着处理好的数据,通过请求转发的方式,在request域中存好处理的结果,请求转发给JSP;JSP从request域中拿出数据,作为展示页面给浏览器。
任何软件都可以认为有以下三种模式组成。
控制器(Control)用来控制程序的流转,界面(View)用来和用户进行交互,模型(Model)用来封装数据和处理业务逻辑的部分。
一个设计良好的软件,应该将这三个部分尽量的独立开来,互不影响,从而使软件更具有模块化的特点。符合这种思想的软件都称为符合MVC设计模式的软件。
1、控制器C:逻辑处理、控制实体数据在视图上展示、调用模型处理业务请求。
当 Web 用户单击 Web 页面中的提交按钮来发送 HTML 表单时,控制器接收请求并调用相应的模型组件去处理请求,然后调用相应的视图来显示模型返回的数据。
2、视图V:数据的展示。
视图是用户看到并与之交互的界面。视图向用户显示相关的数据,并能接收用户的输入数据,但是它并不进行任何实际的业务处理。视图可以向模型查询业务状态,但不能改变模型。视图还能接受模型发出的数据更新事件,从而对用户界面进行同步更新。
3、模型M:应用对象。
模型是应用程序的主体部分。 模型代表了业务数据和业务逻辑; 当数据发生改变时,它要负责通知视图部分;一个模型能为多个视图提供数据。由于同一个模型可以被多个视图重用,所以提高了应用的可重用性。
五、JavaWeb的经典三层架构
将模式二中的JavaBean的功能拆分,使JavaBean只负责自己最擅长的工作——封装数据,处理业务逻辑交给service处理数据,访问交给dao,这样以来,每个模块都只做自己擅长的事情,方便程序开发维护。
一、为什么:要分层
使软件具有结构性,便于开发、维护和管理。
将不同功能模块独立,在需要替换某一模块时不需要改动其他模块,方便代码的复用、替换
二、层与层耦合的概念,利用工厂类解耦
在分层结构中,我们希望将各个功能
约束在各自的模块(层)当中的,而当属于某一层的对象、方法“入侵”到了其他层,如将web层的ServletContext对象传入service层,
或service层调用XMLDao独有的方法,就会导致层与层之间的关系过于“紧密”,当需要修改某一层时不可避免的要修改其他关联的层,
这和我们软件分层最初的设想-----层与层分离,一个层尽量不依赖其他层存在,当修改一层时无需修改另一层的设想是违背的。
这种“入侵”造成的“紧密”关系就早做层与层之间发生的“耦合”,而去掉这种耦合性的过程就叫做层与层之间“解耦”
利用工厂类可以实现解耦的功能
三、如何判断一项功能到底属于哪一层
某一项功能属于哪一层,往往是不能明确确定出来的,这时可以参考如下标准进行判断:
此项功能在业务逻辑上更贴近与哪一层,放在哪一层更能较少耦合
此项功能是否必须使用某一层特有的对象
如果放在哪一层都可以,那么放在哪一层更方便技术上的实现,及方便代码的编写和维护
四、异常的处理
如果一个异常抛给上一层会增加程序的耦合性,请当场解决:如将xml解析错误抛给service层,那么当换成mysqldao时,
还需要修改service去掉xml解析异常的处理
如果上一层明确需要此异常进行代码的流转,请抛出:如当查找一个用户信息而用户找不到时,可以抛出一个用户找不到异常,明确要求上一层处理
如果这一层和上一层都能解决尽量在这一层解决掉
如果这一层不能解决,而上一层能解决抛给上一层
如果所有层都不能解决,则应抛出给虚拟机使线程停止,但是如果直接抛出这个异常,则还需要调用者一级一级继续往上抛出最后才能抛给虚拟机,
所以还不如在出现异常的位置直接trycatch住后转换为RuntimeException抛出。:如读取配置文件出错,任何层都不能解决,转为RuntimeException抛出,停止线程。
1、MVC的特点:
(1)、多个视图可以对应一个模型。减少代码的复制及代码的维护量,一旦模型发生改变,也易于维护。
(2)、模型返回的数据与显示逻辑分离。模型数据可以应用任何的显示技术,例如,使用JSP页面、Velocity模板或者直接产生Excel文档等。
(3)、应用被分为三层,降低了各层之间的耦合,提供了应用的可扩展性。
(4)、控制层的概念也很有效,由于它把不同的模型和不同的视图组合在一起,完成不同的请求。因此,控制层可以说是包含了用户请求权限的概念。
(5)、MVC更适合软件工程化管理的精神。不同的层各司其职,每一层的组件具有相同的特征,有利于通过工程化和工具化产生管理程序代码。
遵循MVC模型的JavaWeb的运行流程:
六、JavaBean
(一)、JavaBean简介
JavaBean是可复用的组件,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于JavaBean是被容器(例如Tomcat)所创建的,因此JavaBean应具有一个无参的构造器,另外,通常JavaBean还要实现Serializable接口用于实现Bean的持久性。JavaBean实际上相当于微软COM模型中的本地进程内COM组件,是不能被跨进程访问的。
1、JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
(1)、这个Java类必须具有一个无参的构造函数
(2)、属性必须私有化。
(3)、私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
2、虽然Sun公司在定义JavaBean规范时,允许Java开发人员把JavaBean设计得可以像Swing组件一样功能强大,但在实际的J2EE开发中,通常只使用到以上JavaBean最基本的特性。
3、JavaBean在J2EE开发中,通常用于封装数据,对于遵循以上写法的JavaBean组件,其它程序可以通过反射技术实例化JavaBean对象,并且通过反射那些遵守命名规范的方法,从而获知JavaBean的属性,进而调用其属性保存数据。
(二)、JavaBean的属性
1、JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。
2、属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。
3、属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。
4、一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性。
import java.io.Serializable;
public class Person implements Serializable{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
(三)、在JSP中使用JavaBean
1、JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为:
(1)、<jsp:useBean>标签:用于在JSP页面中查找或实例化一个JavaBean组件。
(2)、<jsp:setProperty>标签:用于在JSP页面中设置一个JavaBean组件的属性。
(3)、<jsp:getProperty>标签:用于在JSP页面中获取一个JavaBean组件的属性。
2、<jsp:useBean>标签
(1)、 用于在指定的域范围内查找指定名称的JavaBean对象:
1)、如果存在,则直接返回该JavaBean对象的引用。
2)、如果不存在,则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。
(2)、常用语法:
<jsp:useBean id="beanName" class="package.class"
scope="page|request|session|application"/>
1)、id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。
2)、class属性用于指定JavaBean的完整类名(即必须带有包名)。
3)、scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。
(3)、执行原理:
(4)、带标签体的<jsp:useBean>标签
1)、语法:
<jsp:useBean ...>
Body
</jsp:useBean>
2)、功能:Body部分的内容只在<jsp:useBean>标签创建JavaBean的实例对象时才执行。
3、<jsp:setProperty>标签
(1)、 用于设置和访问JavaBean对象的属性:
(2)、常用语法:
<jsp:setProperty name="beanName"
{
property="propertyName" value="{string | <%= expression %>}" |
property="propertyName" [ param="parameterName" ] |
property= "*"
}/>
1)、name属性用于指定JavaBean对象的名称。
2)、property属性用于指定JavaBean实例对象的属性名。
3)、value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。
4)、 param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。
4、<jsp:getProperty>标签
(1)、 用于读取JavaBean对象的属性,也就是调用JavaBean对象的getter方法,然后将读取的属性值转换成字符串后插入进输出的响应正文中。
(2)、常用语法:
<jsp:getProperty name="beanInstanceName" property="PropertyName" />
1)、name属性用于指定JavaBean实例对象的名称,其值应与<jsp:useBean>标签的id属性值相同。
2)、property属性用于指定JavaBean实例对象的属性名。
(3)、如果一个JavaBean实例对象的某个属性的值为null,那么,使用<jsp:getProperty>标签输出该属性的结果将是一个内容为“null”的字符串。
七、JSP开发模式
1、SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式。
2、JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用程序,这种模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据。
3、Servlet+JSP+JavaBean(MVC)模式适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。 Servlet+JSP、JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。
八、案例:
(一)、使用模式一编写计算器
(二)、使用模式二完成用户注册和登陆:
1、Servlet+jsp+JavaBean+dom4j(XPATH)
2、JavaEE的经典三层结构
3、各种功能包
com.lmd.web servlet
com.lmd.service 业务逻辑层
com.lmd.dao
com.lmd.domain JavaBean
com.lmd.util 工具类
com.lmd.test 测试类
com.lmd.exception 异常类
com.lmd.factory() 利用工厂类实现接口
4、导入第三方包:*junit dom4j *JSTL(已内置) beanutils
5、Debug调试模式
6、配置文件:users.xml(模拟数据库) config.properties(程序的主配置文件)
分析从前往后,开发从后往前
步骤一:导入第三方包到WebRoot--》WEB-INF-》lib下:dom4j-1.6.1.jar和commons-beanutils-1.8.3.jar及其依赖包commons-logging-1.1.1.jar。
步骤二:在src下新建一个users.xml(模拟数据库)文件和config.properties(工厂类时使用)文件:
步骤三:分析需要开发的内容如上图,接下来开发 分析从前往后,开发从后往前
1、在com.lmd.domain包下开发JavaBean—User.java:
记住:Dao中不要混有任何业务逻辑层,只是操作数据库内容。
2、在com.lmd.dao包下开发XmlUserDao.java:
若把Dom4j解析工过程放在此处,每次访问都要解析XML,耗时、浪费时间,并且每个方法中都要解析XML,所以提取出来。因此在中建一个专门解析XML的XmlDaoUtils.java文件,其中:私有构造器的存在可以让某些类不能被实例化和子类化,这些类通常是一些工具类;静态代码块仅在类加载的时候执行一次。
将共用的功能的放到工具类XmlDaoUtils.java文件中。
3、 在com.lmd.test包下开发一个测试类XmlUserTest.java文件:
junit测试包版本不对,会出错。
4、在com.lmd.service包下开发UserService.java: (业务逻辑层)
5、在com.lmd.service包下开发index.jsp等: (web层:servlet和jsp)
按照调用顺序开发
(1)、开发regist.jsp注册界面,里面有验证码;开发验证码ValiImg.java文件,(并在web.xml中配置,Myeclipse不需要)
开发注册Servlet:RegistServlet.java,其中遇到问题:乱码问题已解决;
验证码出错后回显注册页面(密码除外),之前填写的注册信息消失,要求回显解决:保存请求参数(param)。
封装数据的时候,使用user.setUsername(request.getParameter("username"));若多个属性太复杂,可以使用beanutils包下的 BeanUtils.populate(user, request.getParameterMap());
注册成功后,回到注册用户的主页。
(2)、开发注销LogoutServlet.java
(3)、开发登录界面login.jsp和LoginServlet.java,并开发记住用户名(存在Cookie中)功能。
记住用户名:要求再次回到登录页面,记住了用户名(为了不嵌套java代码,使用自定义标签解码用户名的编码,显示出来)
步骤一:导入第三方包到WebRoot--》WEB-INF-》lib下:dom4j-1.6.1.jar和commons-beanutils-1.8.3.jar及其依赖包commons-logging-1.1.1.jar。
步骤二:在src下新建一个users.xml(模拟数据库)文件和config.properties(工厂类时使用)文件:
<?xml version="1.0" encoding="UTF-8"?>
<users>
<user username="admin" password="admin" nickname="admin" email="admin8qq.com"/>
</users>
步骤三:分析需要开发的内容如上图,接下来开发 分析从前往后,开发从后往前
1、在com.lmd.domain包下开发JavaBean—User.java:
记住:Dao中不要混有任何业务逻辑层,只是操作数据库内容。
package com.lmd.domain;
import com.lmd.exception.MsgException;
public class User {
private String username;
private String password;
//第二个密码用于验证两个密码是否相等
private String password2;
private String nickname;
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword2() {
return password2;
}
public void setPassword2(String password2) {
this.password2 = password2;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return username + ":" + password;
}
public void checkValue() throws MsgException{
if (username==null || "".equals(username)) {
throw new MsgException("用户名不能为空!");
}
if (password==null || "".equals(password)) {
throw new MsgException("密码不能为空!");
}
if (password2==null || "".equals(password2)) {
throw new MsgException("确认密码不能为空!");
}
if (!password.equals(password2)) {
throw new MsgException("两次密码不一致!");
}
if (nickname==null || "".equals(nickname)) {
throw new MsgException("昵称不能为空!");
}
if (email==null || "".equals(email)) {
throw new MsgException("邮箱不能为空!");
}
//xxxxxx@xxxx.xx
if (!email.matches("^\\w+@\\w+(\\.\\w+)$")) {
throw new MsgException("邮箱格式不正确!");
}
}
}
package com.lmd.dao;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import com.lmd.domain.User;
import com.lmd.util.XmlDaoUtils;
public class XmlUserDao {
/**
* 根据用户名查找用户
* @param username 用户名
* @return 根据用户名找到的用户信息bean,若没找到返回null
*/
public User findUserByUserName(String username){
Document dom = XmlDaoUtils.getDom();
Element root = dom.getRootElement();
//在XML中查找具有username属性值等于传入的用户名的元素
List<Element> list = root.selectNodes("//user[@username='"+username+"']");
if (list.size() > 0) {//大于0,找到这个用户
Element userEle = list.get(0);
//将找到的用户信息封装到bean后返回
User user = new User();
user.setUsername(userEle.attributeValue("username"));
user.setPassword(userEle.attributeValue("password"));
user.setNickname(userEle.attributeValue("nickname"));
user.setEmail(userEle.attributeValue("email"));
return user;
}else {//否者找不到
return null;
}
}
/**
* 添加用户
* @param user 要添加的用户信息bean
*/
public void addUser(User user){
Document dom = XmlDaoUtils.getDom();
Element root = dom.getRootElement();
//1、凭空创建一个<user>元素,根据传入的user信息,设置此元素的属性
Element userEle = DocumentHelper.createElement("user");
userEle.setAttributeValue("username", user.getUsername());
userEle.setAttributeValue("password", user.getPassword());
userEle.setAttributeValue("nickname", user.getNickname());
userEle.setAttributeValue("email", user.getEmail());
//2、挂载到<user>元素上
root.add(userEle);
//3、回写到XML文件中--共有,提到工具类里
XmlDaoUtils.refXML();
}
/**
* 根据用户名和密码查找对应的用户
* @param username 用户名
* @param password 密码
* @return 找到的用户,若没找到返回null
*/
public User findUserByUNandPSW(String username, String password){
Document dom = XmlDaoUtils.getDom();
Element root = dom.getRootElement();
//在XML中查找具有username属性值等于传入的用户名并等于传入密码的元素
List<Element> list = root.selectNodes("//user[@username='"+username+"' and @password='"+password+"']");
if (list.size() > 0) {//大于0,找到这个用户
Element userEle = list.get(0);
//将找到的用户信息封装到bean后返回
User user = new User();
user.setUsername(userEle.attributeValue("username"));
user.setPassword(userEle.attributeValue("password"));
user.setNickname(userEle.attributeValue("nickname"));
user.setEmail(userEle.attributeValue("email"));
return user;
}else {//否者找不到
return null;
}
}
}
2、在com.lmd.dao包下开发XmlUserDao.java:
若把Dom4j解析工过程放在此处,每次访问都要解析XML,耗时、浪费时间,并且每个方法中都要解析XML,所以提取出来。因此在中建一个专门解析XML的XmlDaoUtils.java文件,其中:私有构造器的存在可以让某些类不能被实例化和子类化,这些类通常是一些工具类;静态代码块仅在类加载的时候执行一次。
将共用的功能的放到工具类XmlDaoUtils.java文件中。
package com.lmd.util;
import java.io.FileOutputStream;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* 私有构造器的存在可以让某些类不能被实例化和子类化,
* 这些类通常是一些工具类
* @author angel11288
*
*/
public class XmlDaoUtils {
private static Document dom = null;
private static String path = XmlDaoUtils.class.getClassLoader()
- .getResource("users.xml").getPath();
//私有构造函数的目的:无法被类以外的函数使用,只能通过调用来实现。
private XmlDaoUtils(){ }
//使XML仅解析一次,要放在静态代码块里
static{
try {
SAXReader reader = new SAXReader();
//类加载器,真实路径要分析,要用相对路径
dom = reader.read(path);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
public static Document getDom(){
return dom;
}
public static void refXML() {
try {
XMLWriter writer = new XMLWriter(new FileOutputStream(path)
, OutputFormat.createPrettyPrint());
writer.write(dom);
writer.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
3、 在com.lmd.test包下开发一个测试类XmlUserTest.java文件:
junit测试包版本不对,会出错。
package com.lmd.test;
import org.junit.Test;
import com.lmd.dao.XmlUserDao;
import com.lmd.domain.User;
public class XmlUserTest {
@Test
public void testFindUserByUserName() {
XmlUserDao dao = new XmlUserDao();
User user = dao.findUserByUserName("admin");
System.out.println(user);
//admin:admin
}
@Test
public void testFindUserByUNandPSW() {
XmlUserDao dao = new XmlUserDao();
User user = dao.findUserByUNandPSW("admin", "adminXX");
System.out.println(user);
//admin:admin //null
}
@Test
public void testAddUser() {
XmlUserDao dao = new XmlUserDao();
User user = new User();
user.setUsername("张甜");
user.setPassword("666666");
user.setNickname("小甜甜");
user.setEmail("zhangtian8@163.com");
dao.addUser(user);
//未发布,查看F:\webexample\JAVAWEB\User\WebRoot\WEB-INF\classes下users.xml
}
}
4、在com.lmd.service包下开发UserService.java: (业务逻辑层)
package com.lmd.service;
import com.lmd.dao.XmlUserDao;
import com.lmd.domain.User;
import com.lmd.exception.MsgException;
/**
* 业务逻辑层
* @author angel11288
*/
public class UserService {
private XmlUserDao dao = new XmlUserDao();
/**
* 添加用户
* @param user
* @throws MsgException
*/
public void registUser(User user) throws MsgException {
//1、检查用户名是否已经存在,若存在,则提示
if (dao.findUserByUserName(user.getUsername()) != null) {
//此处提示,返回值不太好,返回值被占用;可以使用异常机制提示
throw new MsgException("用户名已经存在");
}
//2、若不存在。则调用dao中的方法添加用户
dao.addUser(user);
}
/**检查用户名和密码是否正确
*
* @param username
* @param password
*/
public User isUser(String username, String password) {
return dao.findUserByUNandPSW(username, password);
}
}
5、在com.lmd.service包下开发index.jsp等: (web层:servlet和jsp)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<h1>我的网站</h1> <hr>
<c:if test="${sessionScope.user == null }">
欢迎光临!游客!
<a href="${pageContext.request.contextPath }/regist.jsp">注册</a>
<a href="${pageContext.request.contextPath }/login.jsp">登录</a>
</c:if>
<c:if test="${sessionScope.user != null }">
欢迎回来!${sessionScope.user.username }!
<a href="${pageContext.request.contextPath }/LogoutServlet">注销</a>
</c:if>
</body>
</html>
按照调用顺序开发
(1)、开发regist.jsp注册界面,里面有验证码;开发验证码ValiImg.java文件,(并在web.xml中配置,Myeclipse不需要)
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script type="text/javascript">
function changeImg(img){
//添加后面的时间后,每次点击后,会改变验证码;
//地址不变,加一个时间参数,会刷新
img.src = "/User/ValiImg?time="+new Date().getTime();
}
</script>
</head>
<body style="text-align:center;">
<h1>我的网站_注册</h1> <hr>
<font color="red">${msg}</font>
<form action="${pageContext.request.contextPath }/RegistServlet" method="post">
<table border="1" align="center">
<tr>
<td>用户名</td>
<td><input type="text" name="username" value="${ param.username}" /></td>
</tr>
<!-- requestScope是所有域属性组成的map;而param是请求参数组成的map -->
<tr>
<td>密码</td>
<td><input type="password" name="password" /></td>
</tr>
<tr>
<td>确认密码</td>
<td><input type="password" name="password2" /></td>
</tr>
<tr>
<td>昵称</td>
<td><input type="text" name="nickname" value="${param.nickname }"/></td>
</tr>
<tr>
<td>邮箱</td>
<td><input type="text" name="email" value="${param.email }"/></td>
</tr>
<tr>
<td>验证码</td>
<td><input type="text" name="valistr" /></td>
</tr>
<tr>
<td><input type="submit" value="注册" /></td>
<td><img src="/User/ValiImg" style="cursor:pointer"
onclick="changeImg(this)"/></td>
</tr>
</table>
</form>
</body>
</html>
开发注册Servlet:RegistServlet.java,其中遇到问题:乱码问题已解决;
验证码出错后回显注册页面(密码除外),之前填写的注册信息消失,要求回显解决:保存请求参数(param)。
封装数据的时候,使用user.setUsername(request.getParameter("username"));若多个属性太复杂,可以使用beanutils包下的BeanUtils.populate(user, request.getParameterMap());
//TAB 右移 ctrl+TAB 左移 ,在User.java中设一个校验数据的方法。
注册成功后,回到注册用户的主页。
package com.lmd.web;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.persistence.metamodel.SetAttribute;
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 org.apache.commons.beanutils.BeanUtils;
import com.lmd.domain.User;
import com.lmd.exception.MsgException;
import com.lmd.service.UserService;
/**
* Servlet implementation class RegistServlet
*/
@WebServlet("/RegistServlet")
public class RegistServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//TAB 右移 ctrl+TAB 左移
try {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
UserService service = new UserService();
//1、检验验证码
String valistr = request.getParameter("valistr");
String valistr2 = (String) request.getSession().getAttribute("valistr");
if (valistr == null || valistr2 == null || !valistr.equals(valistr2)) {
request.setAttribute("msg", "验证码不正确");
request.getRequestDispatcher("/regist.jsp").forward(request, response);;
return;
}
//2、封装数据,校验数据
User user = new User();
BeanUtils.populate(user, request.getParameterMap());
user.checkValue(); //throw new MsgException
//3、调用service中的方法添加用户
service.registUser(user); //throw new MsgException
//4、登录用户
request.getSession().setAttribute("user", user);
//5、提示注册成功回到主页
response.getWriter().write("恭喜您注册成功!3秒后回到主页...");
response.setHeader("Refresh","3;url="+request.getContextPath()+"/index.jsp");
} catch (MsgException me) {
request.setAttribute("msg", me.getMessage());
request.getRequestDispatcher("/regist.jsp").forward(request, response);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
(2)、开发注销LogoutServlet.java
package com.lmd.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class LogoutServlet
*/
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (request.getSession(false)!=null && request.getSession()
.getAttribute("user")!=null) {
request.getSession().invalidate();
}
response.sendRedirect(request.getContextPath() + "/index.jsp");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
(3)、开发登录界面login.jsp和LoginServlet.java,并开发记住用户名(存在Cookie中)功能。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.lmd.com/UserTag" prefix="UserTag" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
</head>
<body>
<div style="text-align: center;">
<h1>我的网站_登录</h1> <hr>
<font color="red">${msg }</font>
<form action="${pageContext.request.contextPath}/LoginServlet" method="post">
<table border="1" align="center">
<tr>
<td>用户名</td>
<td><input type="text" name="username" value="<UserTag:UserTag
content="${cookie.remname.value }" encode="UTF-8"/>"/></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td><input type="submit" value="登录"/></td>
<!-- 回显回来,是被勾中的状态 -->
<td><input type="checkbox" value="ok" name="remname"
<c:if test="${cookie.remname != null}">
checked="checked"
</c:if>
/>记住用户名</td>
</tr>
</table>
</form>
</div>
</body>
</html>
package com.lmd.web;
import java.io.IOException;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lmd.domain.User;
import com.lmd.service.UserService;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//以后使用过滤器解全栈乱码
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
UserService service = new UserService();
//1、获取客户端提交的用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//2、调用service中的方法检查用户名和密码
User user = service.isUser(username, password);
if (user == null) {
//3、若不正确,则提示
request.setAttribute("msg", "用户名或密码不正确!");
request.getRequestDispatcher("/login.jsp").forward(request, response);
return;
}else {
//4、正确回到登录主页
request.getSession().setAttribute("user", user);
//实现记住用户名:
if ("ok".equals(request.getParameter("remname"))) {
//若勾住,记住,则发送cookie令浏览器保存用户名
//中文有问题,要编码
Cookie remNameC = new Cookie("remname",
URLEncoder.encode(user.getUsername()));
remNameC.setPath(request.getContextPath());
remNameC.setMaxAge(3600*30*24);
response.addCookie(remNameC);
}else {
//若不勾住,不记住,则删除记住用户名的cookie
Cookie remNameC = new Cookie("remname", "");
remNameC.setPath(request.getContextPath());
remNameC.setMaxAge(0);
response.addCookie(remNameC);
}
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
记住用户名:要求再次回到登录页面,记住了用户名(为了不嵌套java代码,使用自定义标签解码用户名的编码,显示出来)
package com.lm.tag;
import java.io.IOException;
import java.net.URLDecoder;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class URLEncoderTag extends SimpleTagSupport{
private String content;
private String encode;
public void setContent(String content) {
this.content = content;
}
public void setEncode(String encode) {
this.encode = encode;
}
@Override
public void doTag() throws JspException, IOException {
//encode设置<required>false</required>,非必须
String s = URLDecoder.decode(content, encode == null ? "UTF-8" : encode);
getJspContext().getOut().write(s);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>UserTag</short-name>
<uri>http://www.lmd.com/UserTag</uri>
<tag>
<name>UserTag</name>
<tag-class>com.lm.tag.URLEncoderTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>content</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>encode</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>