本文用S2SH实现用户登录。
框架图
Modle层就是对应的数据库表的实体类(如User类)。
Dao层,一般可以再分为***Dao接口和***DaoImpl实现类,如userDao接口和userDaoImpl实现类,接口负责定义数据库curd的操作方法,实现类负责具体的实现,即实现Dao接口定义的方法。
一个DAO单独对1个表进行CURD/排序/分页等操作。
Service层,引用对应的Dao层数据库操作,在这里可以编写自己需要的代码(比如简单的判断),也可以再细分为Service接口和ServiceImpl实现类。
一个Service可以操作几个DAO。
Action层:引用对应的Service层实现业务逻辑,在这里结合Struts的配置文件,跳转到指定的页面,当然也能接受页面传递的请求数据,也可以做些计算处理、前端输入合法性检验(前端可修改网页绕过前端合法性检验,需在后台加一层)。
Model层开发
model层开发主要包括创建实体类及映射文件。
实体类创建
UseInfo:
package com.digital.entity;
import java.util.Date;
public class UserInfo {
private int id;
private String userName;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 UserInfo() {
}
// 有参构造
public UserInfo(String userName, String password, Date regDate) {
this.userName = userName;
this.password = password;
}
}
创建映射文件
UserInfo.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.digital.entity">
<class name="UserInfo" table="user_info" catalog="digital">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native"></generator>
</id>
<property name="userName" type="java.lang.String">
<column name="userName" length="16" not-null="true" />
</property>
<property name="password" type="java.lang.String">
<column name="password" length="16" not-null="true" />
</property>
</class>
</hibernate-mapping>
Spring与Hibernate的整合
先回顾一下Hibernate的运行过程:
- 通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
- 由hibernate.cfg.xml中的
<mapping resource="xxx.hbm.xml"/>
读取并解析映射信息 - 通过config.buildSessionFactory();//创建SessionFactory
- sessionFactory.openSession();//打开Sesssion
- session.beginTransaction();//创建事务Transation
- persistent operate持久化操作 //一般指Save这个方法
- session.getTransaction().commit();//提交事务
- 关闭Session
很关键的就是sessionFacctory了,Spring整合Hibernate的目的就在于用Spring的IoC容器来管理Hibrenate的SessionFactory(把它当成了一个Bean),同时让Hibernate使用Spring的声明式事务,而这些都是在spring配置文件中完成的。
applicationContext中整合Hibernate的部分:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 配置数据源 -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///digital" />
<property name="user" value="root" />
<property name="password" value="admin" />
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="10" />
</bean>
<!-- 配置Hibernate的sessionFactory实例 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 配置数据源属性 -->
<property name="dataSource">
<ref bean="dataSource" />
</property>
<!-- 配置 Hibernate的基本属性-->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
</props>
</property>
<!-- 配置 Hibernate映射文件的位置及名称-->
<property name="mappingResources">
<list>
<value>com/digital/entity/UserInfo.hbm.xml</value>
</list>
</property>
</bean>
<!-- 声明Hibernate事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 定义事务通知 ,需要事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 指定事务传播规则 -->
<tx:attributes>
<!-- 对所有方法应用REQUIRED事务规则 -->
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!--定义切面,并将事务通知和切面组合(定义哪些方法应用事务规则) -->
<aop:config>
<!-- 对com.digital.service包下的所有类的所有方法都应用事务规则 -->
<aop:pointcut id="serviceMethods" expression="execution(* com.digital.service.*.*(..))" />
<!-- 将事务通知和切面组合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
DAO层开发
DAO层主要实现与数据库交互的代码:
UserInfoDAO接口
package com.digital.dao;
import java.util.List;
import com.digital.entity.*;
public interface UserInfoDAO {
public List<UserInfo> search(UserInfo cond);
}
UserInfoDAOImpl
package com.digital.dao.impl;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Example;
import com.digital.dao.UserInfoDAO;
import com.digital.entity.UserInfo;
public class UserInfoDAOImpl implements UserInfoDAO {
SessionFactory sessionFactory;
//通过注入方式获得实例
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public List<UserInfo> search(UserInfo cond) {
List<UserInfo> uiList = null;
// 获得session
Session session = sessionFactory.getCurrentSession();
// 创建Criteria对象
Criteria c = session.createCriteria(UserInfo.class);
// 使用Example工具类创建示例对象
Example example = Example.create(cond);
// 为Criteria对象指定示例对象example作为查询条件
c.add(example);
// 执行查询
uiList = c.list();
// 返回结果
return uiList;
}
}
添加DAO的bean到applicationContext
<!-- 定义com.digital.dao.impl.UserInfoDAOImpl类-->
<bean id="userInfoDAO" class="com.digital.dao.impl.UserInfoDAOImpl">
<property name="sessionFactory" ref="sessionFactory" /></bean>
Service层开发
UserInfoService接口
package com.digital.service;
import java.util.List;
import com.digital.entity.*;
public interface UserInfoService {
public List<UserInfo> login(UserInfo cond);
}
UserInfoServiceImpl
package com.digital.service.impl;
import java.util.List;
import com.digital.dao.UserInfoDAO;
import com.digital.entity.UserInfo;
import com.digital.service.UserInfoService;
public class UserInfoServiceImpl implements UserInfoService {
UserInfoDAO userInfoDAO;
//注入获得实例
public void setUserInfoDAO(UserInfoDAO userInfoDAO) {
this.userInfoDAO = userInfoDAO;
}
@Override
public List<UserInfo> login(UserInfo cond) {
return userInfoDAO.search(cond);
}
}
添加Service的bean到applicationContext
<!-- 定义UserInfoServiceImpl类,并为其userInfoDAO属性注入值-->
<bean id="userInfoService" class="com.digital.service.impl.UserInfoServiceImpl">
<property name="userInfoDAO" ref="userInfoDAO" />
</bean>
Action开发
package com.digital.action;
import java.util.List;
import com.digital.entity.*;
import com.digital.service.UserInfoService;
import com.opensymphony.xwork2.ActionSupport;
public class UserInfoAction extends ActionSupport {
UserInfo ui;
public UserInfo getUi() {
return ui;
}
public void setUi(UserInfo ui) {
this.ui = ui;
}
UserInfoService userInfoService;
public void setUserInfoService(UserInfoService userInfoService) {
this.userInfoService = userInfoService;
}
public String doLogin() throws Exception {
List<UserInfo> uiList = userInfoService.login(ui);
if (uiList.size() > 0) {
// 登录成功,转发到到index.jsp
return "index";
} else {
// 登录失败,重定向到login.jsp
return "login";
}
}
}
Spring整合Struts2
先来回顾一下struts2执行原理:
此外,struts2还有个很重要的valuestack来保存action等东西:
创建action对象的方法有很多,一种是利用struts2本身的反射机制,通过ObjectFactory来创建action对象。第二个就是把创建对象委托给其他容器,例如spring。
Spring整合Struts2的目的在于使用Spring IOC容器来管理Struts2的Action。
web.xml配置
ssh框架中,我们通过配置就可以实现spring容器随着Tomcat/Jboss等的启动而自动启动,而无需我们手动开启。这是通过监听器来实现的,当spring容器启动后,自动读取并加载相应的配置文件。
<!-- 指定以Listerner方式启动Spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
添加UserInfoAction的bean到applicationContext
用prototype,每个用户都会创建一个新的Bean实例。
<!-- 定义UserInfoAction类 ,并为其中属性userInfoService注入值 -->
<bean name="uiAction" class="com.digital.action.UserInfoAction" scope="prototype">
<property name="userInfoService" ref="userInfoService" />
</bean>
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.i18n.encoding" value="utf-8"></constant>
<!-- 定义一个名称为digital的包,继承struts 2的默认包,指定命名空间为"/" -->
<package name="digital" namespace="/" extends="struts-default">
<!-- 为类中的方法配置映射 -->
<action name="doLogin" class="uiAction" method="doLogin"><!--class引用bean实例-->
<result name="index" type="dispatcher">index.jsp</result>
<result name="login" type="redirect">login.jsp</result>
</action>
</package>
</struts>
关于dispatcher和redirect的区别
dispatcher用于两个组件协同工作的时候,而redirect用于一个工作完成之后开始下一个工作,重定向之后当前页面的所有数据信息在下一个页面将全部失效,也就是说,使用redirect跳转页面的时候不能把当前页面的参数传递到下一个页面。
比如:登录成功之后应该用redirect重定向不能用dispatcher,因为登录成功表示一个工作已经完成,如果用重定向的话,由于地址还是原来的地址,用户刷新页面之后又会要求重新登录,不符合常理。
创建交互页面
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>登录页</title>
</head>
<body>
<s:form action="doLogin">
<table>
<tr>
<s:textfield name="ui.userName" label="用户名" />
</tr>
<tr>
<s:textfield name="ui.password" label="密码" />
</tr>
<tr>
<s:submit value="登录" />
</tr>
</table>
</s:form>
</body>
</html>
登录成功页面
index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>系统首页面</title>
</head>
<body>
欢迎您,登录成功!
</body>
</html>
Server MyEclipse Tomcat v8.5 was unable to start within 45 seconds. If the server requires more time…
解决办法是在Workpaces->.metadata->.plugins->org.eclipse.wst.server.core->servers.xml修改timeout大一点就行了,项目一大,所需的时间也就会越长。
执行流程
从页面开始,提交表单或者点击链接会触发一个action(doLogin)
action交给struts2处理,读取src目录struts.xml文件,在配置中找到对应的action
根据class=”XXXAction”交给Spring(为什么struts的action会交给spring处理呢?
原因是:Struts2提供一个jar包:struts2-spring-plugin-2.1.2.jar,有个struts.properties文件配置了这样一句话:struts.objectFactory.spring.autoWire = name。
也就是有了上面那个jar包之后,根据name自动提交给Spring处理,读取Spring容器的配置文件/WebRoot/WEB-INF/applicationContext.xml文件。 根据applicationContext.xml配置文件找到xxx.xxx.action.xxxAction类,
其中有一个属性xxxService,并提供了setxxxService()方法,由applicationContext.xml文件得知该xxxService对象由Spring容器依赖注入,set注入。取得xxxService对象(接口对接口实现类的一个引用)后,调用它的方法。后面的就是访问DAO了,一长串省略号代替
执行的结果会在action的对应的方法中以字符串的形式给出。然后根据配置文件中的result.找到下一步要执行的动作,是跳转到页面显示还是继续交给其他的action处理。