首页 > 技术文章 > day50_BOS项目_02

chenmingjun 2018-10-04 18:46 原文

  • day50_BOS项目_02今天内容安排:
    • 1、根据提供的pdm文件生成sql脚本文件
    • 2、持久层和表现层的设计(BaseDao、BaseAction)
    • 3、基于BaseDao和BaseAction实现用户登录功能
    • 4、jQuery EasyUI 消息提示控件
    • 5、jQuery EasyUI 菜单按钮 menubutton
    • 6、自定义struts2拦截器,实现用户未登录自动跳转到登录页面
    • 7、基于ajax实现修改密码功能

1、根据提供的pdm文件生成sql脚本

  • 根据提供的pdm文件生成sql脚本文件(即建表语句文件)
  • 我们通过Mysql命令行的方式,使用sql脚本文件生成对应的数据库表,操作如下图所示:
  • 生成的数据库表结构如下图所示:
  • 然后通过数据库中的表,使用MyEclipse的反向生成工具-->hibernate反转引擎引擎(MyEclipse自带的插件)来反转生成实体类和对应的映射文件
    参考链接:https://www.cnblogs.com/chenmingjun/p/9733326.html

2、持久层和表现层的设计(BaseDao、BaseAction)

2.1、持久层的设计(基于泛型+反射)

持久层的设计(基于泛型+反射)图解,如下图所示:


通用接口代码:
IBaseDao.java
package com.itheima.bos.dao.base;

import java.io.Serializable;
import java.util.List;

/*
 * 抽取持久层通用方法:增删改查+分页+...
 */

public interface IBaseDao<T{
    // 增删改查
    public void save(T entity);
    public void delete(T entity);
    public void update(T entity);
    // 因为id不管是int类型还是string类型,他们都实现了Serializable序列化接口,使用Serializable类型,或者更狠一点:就用Object类型
    public T findById(Serializable id);
    public List<T> findAll();

    // 分页

    // ...
}

通用接口实现代码:
BaseDaoImpl.java

package com.itheima.bos.dao.base.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Resource;

import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.itheima.bos.dao.base.IBaseDao;

/*
 * 持久层通用实现 
 */

public class BaseDaoImpl<Textends HibernateDaoSupport implements IBaseDao<T{

    // 声明实体类型
    private Class<T> entityClass; // Class也是一个class,Class代表的是类型

    // 在构造方法中动态获取操作要用的实体类型
    public BaseDaoImpl() // this是当前实例化对象,通过getClass(),得到当前类的类型,getGenericSuperclass() 获得带有泛型的父类,getSuperclass() 获得该类的父类
        // Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
        Type type = this.getClass().getGenericSuperclass();
        // ParameterizedType 参数化类型,即泛型
        ParameterizedType pt = (ParameterizedType)type;
        // getActualTypeArguments() 获取参数化类型的数组,泛型可能有多个
        Type[] actualTypeArguments = pt.getActualTypeArguments();
        entityClass = (Class<T>) actualTypeArguments[0];
    }

    // 使用注解方式进行依赖注入setter方法
    // @Autowired 注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。注意:@Autowired 默认按类型匹配(byType)进行注入的。
    // 如果需要按名称(byName)匹配的话,可以使用@Qualifier注解与@Autowired结合,请注意必须在xml配置中启动注解驱动。

    // @Resource 倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式进行注入的。
    // 该注解需导入import javax.annotation.Resource;
    // 此时Spring只会尝试将属性名与bean名称进行匹配,如果找到则注入依赖bean。
    // 由于public final void setSessionFactory(SessionFactory sessionFactory) {...}是final的,所以需要我们自定义方法,再去使用super去调用它。
    @Resource
    public void setMySessionFactory(SessionFactory sessionFactory) {
        super.setSessionFactory(sessionFactory);
    }

    public void save(T entity) {
        this.getHibernateTemplate().save(entity);
    }

    public void delete(T entity) {
        this.getHibernateTemplate().delete(entity);
    }

    public void update(T entity) {
        this.getHibernateTemplate().update(entity);
    }

    public T findById(Serializable id) {
        this.getHibernateTemplate().get(entityClass, id);
        return null;
    }

    public List<T> findAll() {
        String hql = "from " + entityClass.getSimpleName(); // 注意from后面有空格
        return this.getHibernateTemplate().find(hql);
    }

}

我们再补上IUserDao和UserDaoImpl的示例代码:
IUserDao.java

package com.itheima.bos.dao;

import com.itheima.bos.dao.base.IBaseDao;
import com.itheima.bos.domain.User;

public interface IUserDao extends IBaseDao<User> {

}

UserDaoImpl.java

package com.itheima.bos.dao.impl;

// import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;

import com.itheima.bos.dao.IUserDao;
import com.itheima.bos.dao.base.impl.BaseDaoImpl;
import com.itheima.bos.domain.User;

@Repository // IUserDaoImpl对象默认是单例的,单例对象创建的时机:在applicationContext.xml配置文件加载,执行组件扫描,创建工厂的时候,单例对象被创建了
// @Scope("prototype") 多例,多例对象创建的时机:在getBean() 真正从工厂获取的对象时候才创建
public class UserDaoImpl extends BaseDaoImpl<Userimplements IUserDao {

}

2.2、表现层的设计

表现层的设计图解,如下图所示:


表现层通用代码:
BaseAction.java
package com.itheima.bos.web.action.base;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

public class BaseAction<Textends ActionSupport implements ModelDriven<T{

    // 声明模型对象
    private T model;

    public T getModel() {
        return model;
    }

    // 在构造方法中动态获取操作要用的实体类型,然后通过反射创建模型对象
    public BaseAction() {
        // Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
        Type type = this.getClass().getGenericSuperclass();
        // ParameterizedType 参数化类型,即泛型
        ParameterizedType pt = (ParameterizedType)type;
        // getActualTypeArguments() 获取参数化类型的数组,泛型可能有多个
        Type[] actualTypeArguments = pt.getActualTypeArguments();
        // 获取操作要用的实体类型
        Class<T> entityClass = (Class<T>) actualTypeArguments[0];

        try {
            // 通过反射创建模型对象
            model = entityClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

我们再补UserAction的示例代码:
UserAction.java

package com.itheima.bos.web.action;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.User;
import com.itheima.bos.web.action.base.BaseAction;

@Controller
@Scope("prototype"// 配置对象多例
public class UserAction extends BaseAction<User{

    // 简单测试struts是否配置成功,测试完删掉
    public String test() {
        return "test";
    }
}

相应的struts.xml文件需要对userAction进行配置:
struts.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
    "http://struts.apache.org/dtds/struts-2.3.dtd">

<struts>
    <!-- 设置为开发者模式 -->
    <constant name="struts.devMode" value="true"/>
    <!-- 使Spring对象工厂成为自动默认值,struts2与spring整合,该句不是必须的,在整合jar中已经配置过了 ,这里只是为了强调-->
    <constant name="struts.objectFactory" value="spring"/>

    <package name="basicstruts2" extends="struts-default">
        <!-- 需要进行权限控制的页面访问,使用默认的类和默认的方法,默认的类和默认的方法可以不用写,这里写出来为了强调 -->
        <action name="page_*_*" class="com.opensymphony.xwork2.ActionSupport" method="execute">
            <result name="success" type="dispatcher">/WEB-INF/pages/{1}/{2}.jsp</result>
        </action>
        <!-- 配置userAction-->
        <action name="userAction_*" class="userAction" method="{1}">
            <result>xxx</result>
        </action>
    </package>
</struts>

3、基于BaseDao和BaseAction实现用户登录功能

第一步:修改login.jsp页面,点击登录按钮,提交表单
login.jsp

......
    <form id="loginform" name="loginform" method="post" class="niceform"
        action="${pageContext.request.contextPath}/userAction_login.action">
......
    <%-- <a href="${pageContext.request.contextPath}/page_common_index.action" id="loginform:j_id19" name="loginform:j_id19"> --%>
    <a onclick="document.forms[0].submit()" id="loginform:j_id19" name="loginform:j_id19">
    <span id="loginform:loginBtn" class="btn btn-login" style="margin-top:-36px;">登录</span>
    </a>
......

浏览器页面如下图所示:


第二步:在UserAction中提供login()方法
UserAction.java
package com.itheima.bos.web.action;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.ServletActionContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import com.itheima.bos.domain.User;
import com.itheima.bos.service.IUserService;
import com.itheima.bos.web.action.base.BaseAction;

@Controller
@Scope("prototype") // 配置对象多例
public class UserAction extends BaseAction<User{

    // 注入service
    @Resource
    private IUserService userServie;

    // 通过属性驱动接收传递过来的验证码
    private String checkcode;
    public void setCheckcode(String checkcode) {
        this.checkcode = checkcode;
    }

    public String login() {
        // 判断用户输入的验证码是否正确
        // 先获取我们自己生成的验证码
        String key = (String) ServletActionContext.getRequest().getSession().getAttribute("key");
        // 判断用户是否有输入验证码和输入的验证码是否和我生成的验证码是否相等
        if (StringUtils.isNotBlank(checkcode) && checkcode.equals(key)) {
            // 说明验证码存在且正确
            User user = userServie.login(model);
            if (user != null) {
                // 说明登录成功,将User放入session域中,并跳转到系统首页
                ServletActionContext.getRequest().getSession().setAttribute("loginUser", user);
                return "home";
            } else {
                // 说明登录失败,设置错误提示信息,并跳转至登录页面
                // this.addActionError("用户名或者密码错误"); // 在Struts2中,所有的消息提示都是基于国际化的。
                this.addActionError(this.getText("loginError"));
                return "login";
            }
        } else {
            // 说明验证码错误,设置错误提示信息,并跳转至登录页面
            // this.addActionError("验证码错误"); // 在Struts2中,所有的消息提示都是基于国际化的。
            this.addActionError(this.getText("validatecodeError"));
            return "login";
        }
    }

}

第三步:提供IUserService接口和UserServiceImpl类
IUserService.java

package com.itheima.bos.service;

import com.itheima.bos.domain.User;

public interface IUserService {

    public User login(User model);
}

UserServiceImpl.java

package com.itheima.bos.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.itheima.bos.dao.IUserDao;
import com.itheima.bos.domain.User;
import com.itheima.bos.service.IUserService;
import com.itheima.bos.utils.MD5Utils;

@Service
@Transactional
public class UserServiceImpl implements IUserService {

    // 注入dao
    @Autowired
    private IUserDao userDao;

    public User login(User model) {
        String username = model.getUsername();
        String password = model.getPassword(); // 明文密码
        password = MD5Utils.md5(password); // MD5加密后的密码
        User user = userDao.findByUserNameAndPassWord(username, password);
        return user;
    }

}

第四步:在IUserDao接口中扩展方法和UserDaoImpl实现类,根据用户名和密码查询用户
IUserDao.java

package com.itheima.bos.dao;

import com.itheima.bos.dao.base.IBaseDao;
import com.itheima.bos.domain.User;

public interface IUserDao extends IBaseDao<User{

    public User findByUserNameAndPassword(String username, String password);
}

UserDaoImpl.java

package com.itheima.bos.dao.impl;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.itheima.bos.dao.IUserDao;
import com.itheima.bos.dao.base.impl.BaseDaoImpl;
import com.itheima.bos.domain.User;

@Repository // IUserDaoImpl对象默认是单例的,单例对象创建的时机:在applicationContext.xml配置文件加载,执行组件扫描,创建工厂的时候,单例对象被创建了
// @Scope("prototype") 多例,多例对象创建的时机:在getBean() 真正从工厂获取的对象时候才创建
public class UserDaoImpl extends BaseDaoImpl<Userimplements IUserDao {

    /*
     * 根据用户名和密码查询用户
     */

    public User findByUserNameAndPassword(String username, String password) {
        String hql = "from User u where u.username=? and u.password=?";
        List<User> list = this.getHibernateTemplate().find(hql, username, password);
        if (list != null && list.size() > 0 ) {
            return list.get(0);
        }
        return null;
    }

}

第五步:编写国际化配置文件,在struts.xml中注册(配置)国际化文件
/bos19/config/message.properties

loginError=\u7528\u6237\u540D\u6216\u8005\u5BC6\u7801\u9519\u8BEF
validatecodeError=\u9A8C\u8BC1\u7801\u9519\u8BEF

struts.xml

......
    <!-- 在struts中注册(配置)国际化文件 -->
    <constant name="struts.custom.i18n.resources" value="message"></constant>
......

第六步:在login.jsp页面中使用struts2提供的标签展示错误提示信息
login.jsp

......
<%@ taglib uri="/struts-tags" prefix="s"%>
......
    <font color="red">
        &emsp;&emsp;&emsp;&emsp;<s:actionerror/>
    </font>

4、jQuery EasyUI 消息框控件 messager

示例代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>messager---消息框控件</title>
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/icon.css">
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/jquery.easyui.min.js"></script>
    <!-- 引入中文语言包 -->
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/locale/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript">
        $(function() {
            // alert();
            // window.alert();
            // 消息提示框
            $.messager.alert("标题""内容信息""question");
            window.setTimeout(function() {
                // 消息提示框(在屏幕的右下角显示一个消息窗口)
                $.messager.show({
                    title:'欢迎信息',
                    msg:'欢迎张三登录系统',
                    timeout:3000,
                    showType:'slide'
                }); 
            }, 3000);

            // 消息确认框
            $.messager.confirm("标题""你确定删除当前数据吗?"function(r{
                alert(r);
            });

            // 带有输入功能的消息确认框
            $.messager.prompt("标题""你确定删除当前数据吗?"function(r{
                alert(r);
            });

            // 显示进度提示框
            $.messager.progress();
            // 延时3秒后关闭进度提示框
            window.setTimeout(function() {
                $.messager.progress('close');
            }, 3000);
        });
    
</script>
</head>
<body>
</body>
</html>

5、jQuery EasyUI 菜单按钮 menubutton

示例代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>menubutton---菜单按钮</title>
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/js/easyui/themes/icon.css">
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/jquery.easyui.min.js"></script>
    <!-- 引入中文语言包 -->
    <script type="text/javascript" src="${pageContext.request.contextPath}/js/easyui/locale/easyui-lang-zh_CN.js"></script>
</head>
<body>
    <a data-options="iconCls:'icon-help',menu:'#mm'" class="easyui-menubutton">控制面板</a>
    <!-- 使用div制作下拉菜单选项 -->
    <div id="mm">
        <!-- 使用子div制作具体的一个选项 -->
        <div onclick="alert(111)" data-options="iconCls:'icon-edit'">修改密码</div>
        <div>联系管理员</div>
        <div class="menu-sep"></div>
        <div>退出系统</div>
    </div>
</body>
</html>

效果如下图所示:

6、自定义struts2拦截器,实现用户未登录时自动跳转到登录页面

第一步:自定义一个拦截器类
BOSLoginInterceptor.java

package com.itheima.bos.web.intereptor;

import org.apache.struts2.ServletActionContext;

import com.itheima.bos.domain.User;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

/*
 * 自定义一个struts2拦截器,实现用户未登录时,自动跳转到登录页面
 */

public class BOSLoginInterceptor extends MethodFilterInterceptor{

    // 拦截方法
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        // 测试自定义拦截器是否工作
        // ActionProxy proxy = invocation.getProxy();
        // String namespace = proxy.getNamespace();
        // String actionName = proxy.getActionName();
        // String url = namespace + actionName;
        // System.out.println("自定义的struts拦截器执行了" + url);

        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("loginUser");
        if (user == null) {
            // 说明用户未登录,跳转到登录页面
            return "login";
        }
        // 否则说明用户登录,就放行
        return invocation.invoke();
    }
}

第二步:在struts.xml中注册(配置/声明)自定义的拦截器

......
        <!-- 注册(配置/声明)拦截器  -->
        <interceptors>
            <!-- 注册(配置/声明)自定义的拦截器 -->
            <interceptor name="BOSLoginInterceptor" class="com.itheima.bos.web.intereptor.BOSLoginInterceptor"></interceptor>
            <!-- 我的全局拦截器栈中包括: -->
            <interceptor-stack name="myStack">
                <!-- 1、默认的拦截器栈 -->
                <interceptor-ref name="defaultStack"></interceptor-ref>
                <!-- 2、我自定义的拦截器 -->
                <interceptor-ref name="BOSLoginInterceptor">
                    <!-- 自定义的拦截器要配置:需要拦截哪些方法,和需要放过哪些方法 -->
                    <param name="excludeMethods">login</param>
                </interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <!-- 重新定义默认的拦截器栈 -->
        <default-interceptor-ref name="myStack"></default-interceptor-ref>
        <!-- 配置/声明全局逻辑结果视图 -->
        <global-results>
            <result name="login">/login.jsp</result>
        </global-results>
......

7、基于ajax实现修改密码功能

/WEB-INF/pages/common/index.jsp


第一步:为密码输入框制定输入校验规则,使用easyUI提供的form表单控件中的easyui-validatebox
......
    <!-- 注意:该表单不是用来提交用的,而是用来制定输入校验规则用的 -->
    <form id="editPasswordForm">
        <table cellpadding=3>
            <tr>
                <td>新密码:</td>
                <td><input id="txtNewPass" type="Password" class="txt01 easyui-validatebox"
                    data-options="required:true, validType:'length[4,8]'"
                />
</td>
            </tr>
            <tr>
                <td>确认密码:</td>
                <td><input id="txtRePass" type="Password" class="txt01 easyui-validatebox" 
                    data-options="required:true, validType:'length[4,8]'"
                />
</td>
            </tr>
        </table>
    </form>
......

第二步:为修改密码的“确定”按钮绑定事件

    // 为修改密码的“确定”按钮绑定事件
    $("#btnEp").click(function() {
        // 进行表单验证
        // 基于jQuery的表单验证插件Validation Engine
        var v = $("#editPasswordForm").form("validate"); // 先对该表单中的所有输入框进行校验(前提:你先要有校验规则)
        // alert(v); // true 或 false
        if (v) { 
            // 说明表单校验通过
            // 再判断两次密码输入是否一致
            var v1 = $("#txtNewPass").val();
            var v2 = $("#txtRePass").val();
            if (v1 == v2) {
                // 说明两次输入一致,发送ajax请求,修改当前用户的密码
                var url = "${pageContext.request.contextPath}/userAction_editPassword.action";
                $.post(url, {"password":v1}, function(data{
                    // alert(data);
                    if (data == '1') {
                        // 密码修改成功
                         $.messager.alert("提示信息""密码修改成功!""info");
                    } else {
                        // 密码修改失败
                        $.messager.alert("提示信息""密码修改失败!""warning");
                    } 
                    // 关闭修改密码的窗口
                    $('#editPwdWindow').window('close');
                });
            } else {
                // 说明两次密码输入不一致,提示用户:两次输入密码不一致
                $.messager.alert("提示信息""两次输入密码不一致""warning");
            }
        }
    });

第三步:在UserAction中提供editPassword方法,用于修改当前用户的密码

    /*
     * 修改当前用户的密码
     */

    public String editPassword() throws IOException {
        User user = (User) ServletActionContext.getRequest().getSession().getAttribute("loginUser");
        // 获取新密码
        String password = model.getPassword();
        // MD5加密
        password = MD5Utils.md5(password);
        user.setPassword(password);

        // 此法不好,User的所有字段都要更新,更新的数据太多了
        // userServie.update(user); // update t_user set name=?,password=?,... 

        String flag = "1";
        try {
            // 使用通用的更新方法
            userServie.editPassword(password, user.getId());
        } catch (Exception e) {
            // 修改密码失败
            flag = "0";
        }
        // 响应给浏览器一个状态码,这种手法常用
        ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
        ServletActionContext.getResponse().getWriter().print(flag);

        return "none";
    }

第四步:在IBaseDao中扩展一个通用的更新方法
IBaseDao.java

    // 提供通用修改方法,可变参数
    public void executeUpdate(String queryName, Object ...objects);

BaseDaoImpl.java

    /*
     * 通用更新方法
     */

    public void executeUpdate(String queryName, Object... objects) {
        // 从本地线程中获得session对象
        Session session = this.getSession();
        // 使用命名查询语句获得一个查询对象
        Query query = session.getNamedQuery(queryName);
        // 循环为HQL语句中的?设置值(赋值)
        int i = 0;
        for (Object arg : objects) {
            query.setParameter(i++, arg);
        }
        // 执行更新
        query.executeUpdate();
    }

第五步:在User.hbm.xml中定义一个HQL语句,用于修改密码
注意:<query>标签与<class>标签同级

    <!-- 命名查询语句 -->
    <query name="editPassword">
        update User set password=? where id=?
    </query>

推荐阅读