初学三大框架,懵懵懂懂,似是而非,云里雾里的,趁着眼下思路还算清晰,赶紧做个笔记,以备不时之需。
三大框架,各有利弊,分开来用都是没的说,但是怎么能让它们整合起来发挥出各自的优势,却不是那么容易。
下面先列出我的项目目录图,以便于参照。
关于jar包,真的是让人又爱又恨的一个存在。版本之间不同的话,冲突还是很大的,尤其是大项目,依赖的jar包更是一个难题。
与之相比,Maven的存在在很大程度上解决了这个问题,仓库设置的恰当的话,更是一种享受。
为了更好的锻炼自己的能力,今天我还是按照原始的做法,从Dynamic Web Project开始吧。
一般来说,jar包都是项目经理或者团队之间预先准备好的,一方面可以解决版本问题,另一方面也能很好的管理团队的开发,维护。我这边东拼西凑的,也组织好了一份完整的jar包。有需要的话可以点击下面的链接下载(0积分,与君共勉)。
http://download.csdn.net/detail/marksinoberg/9782432
下载完之后,放到创建好的web项目的webContent文件夹下的WEB-INF的lib目录即可。
然后还可以add to build path.方面后续的操作。
我自己对于Struts2的理解,就是一个“前端(这是相对的概念,相对于整个项目而言)”的信使,管理着客户端发来的请求,然后把请求转发给不同的action来处理。
底层必然是使用FilterDispatcher了,这里就不过多讲述这方面的细节吧。
搭建环境也是非常清晰,大致分为以下两步。
刚才也说过了,请求是要经过特定的action来处理的,action相当于咱们的第一道“防线”。
下面在com.hailstudio.action包下创建一个UserAction,继承自ActionSupport类即可。(这样方便请求的处理操作)。
然后是在src目录下创建一个strtus.xml文件。注意好里面的schema约束和相对于action的设置,下面我给出我的案例。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="struts2" extends="struts-default" namespace="/">
<!-- 为了防止action被spring和strtus2加载两次,class的值写成spring的bean的id的值即可。 -->
<action name="userAction" class="userAction"></action>
</package>
</struts>
里面注释的部分,是因为SSH整合的缘故,我们如果在class里面写上UserAction的全路径就会导致项目加载两次,这样的结果不是我们想要的,于是交给Spring的容器来处理就好了。
具体的做法就是把class的值写成对应在spring中相对应的bean的id的值。
设置过滤器的原因是为了让struts这个信使接收到来自客户端的请求。所以是很有必要的,在web.xml文件中添加上下面的代码就可以了。
<!-- 配置struts2的过滤器 -->
<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>
早前不知道为什么要这么设置,犯了很多的错误。大家引以为戒,就按照上面的格式来写就行了。
这也算是一个固定的表达吧。
Hibernate作为持久化的一层,需要和底层数据打交道,所以咱们还是小心点好。不过搭建这个层面的环境还算是比较简单,按照路程一步步往下走吧。大致可以分为以下几个小步骤。
对应刚才的User系列,在com.hailstudio.entity包下面创建一个User.java的类即可。
package com.hailstudio.entity;
public class User {
private Integer uid;
private String username;
private String address;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
这就没什么好说的了,Hibernate就是根据这个配置映射文件和底层数据库打交道的。其作用就是告诉Hibernate把我们的实体类怎么映射到数据库上。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.hailstudio.entity.User" table="t_user">
<id name="uid" column="uid">
<generator class="native"></generator>
</id>
<property name="username" column="username"></property>
<property name="address" column="address"></property>
</class>
</hibernate-mapping>
细节部分,还是参考官网文档比较好。
和strtus.xml文件一样,放到src的目录下即可。当然了,这个文件的文职我们可以任意指定,前提是spring来插手。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据源将在spring的配置中实现,这里可以注释掉 <property name="connection.username">root</property> <property name="connection.password">mysql</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql:///ssh?useUnicode=true&characterEncoding=UTF-8</property> -->
<property name="connection.dialect">org.hibernate.dialect.MySQLDialect</property>
<!-- 解决没有配置方言的问题或者配置文件无法工作: http://blog.csdn.net/daryl715/article/details/1507385 -->
<!-- <property name="current_session_context_class">thread</property> -->
<!-- 但是在整合HibernateTemplate的时候就会出现会话未激活现象,用下面的代替:http://blog.csdn.net/maoyuanming0806/article/details/61417995 -->
<property name="hibernate.current_session_context_class">org.springframework.orm.hibernate5.SpringSessionContext</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<property name="hbm2ddl.auto">update</property>
<!-- 引入映射文件 -->
<mapping resource="com/hailstudio/entity/user.hbm.xml" />
</session-factory>
</hibernate-configuration>
需要 注意的是切记不要忘记mapper文件的引入,不然核心配置文件还怎么工作?
针对MVC模型,Spring相当于一个中转站(Controller),接收来自Web层的Action请求,然后转交给持久层的Hibernate。
因此,不难看出,Spring在这里甚是重要。搭建起来思路也算是清晰,按照下面的步骤一点点来吧。
Spring的核心配置文件的命名其实没有什么约束,起什么名字都是无所谓的,因为后续还需要手动指定一下。官方建议是命名为applicationContext.xml,但是我这边起了个更简洁的名字spring.xml。
对于这个核心配置文件,约束schema至关重要,我这里的案例也不是很全,但是基本上还算够用。
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- dbcp数据连接池配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssh"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 让SessionFactory的创建也交给Spring来实现 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 把数据库的配置交给了spring的datasource了 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 指定Hibernate的核心配置文件, -->
<property name="configLocations" value="classpath:hibernate.cfg.xml"></property>
</bean>
<!-- 创建action的bean对象 -->
<bean id="userAction" class="com.hailstudio.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
<!-- 创建service相关 -->
<bean id="userService" class="com.hailstudio.service.UserService">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<!-- 创建dao相关 -->
<bean id="userDaoImpl" class="com.hailstudio.dao.UserDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<!-- 引入HibernateTemplate模板的支持 -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<!-- 通过构造函数或者setter注入的方式将sessionFactory传给模板对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 开启事务管理器========然后开启注解 -->
<!-- 单纯配置好上面,无法完成持久化操作。必须要开启事务,否则只能为“只读”模式 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<!-- 注入会话期工厂,可以点到源码中看到原因。sessionFactory中已经包含了DataSource -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
配置文件中很多东西没讲明白,后面会一一介绍每一个标签的作用的,这里就先这么写吧。
为了让项目跑的更舒快,最好是在服务器启动的时候就加载这个核心配置文件,初始化一切需要的设置。
底层来讲,服务器启动就加载需要监听器和指定配置文件的步骤。
如下:
在web.xml文件中加上下面的代码。
<!-- 配置监听器,让服务器一旦启动就加载所需的配置文件,这个标签需要配合context-param来使用 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
命名是固定的,按照上面的来写就行了。
监听器的存在是为了告诉tomcat再启动的时候触发加载的行为,但是如果不指定配置文件的位置,服务器不可能自己找到的。通过下面的代码,就可以解决这一个问题。
还是在web.xml文件中,加上下面的代码。
<!-- 给服务器指定要加载的配置文件的路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
classpath就是项目的类路径,如果spring.xml文件在具体的某一个包而不是在src路径下,我们只需要指定完整的路径即可。
从这里也可以看出,spring.xml文件的命名和放置的位置都是可以随意指定的了吧。
Spring作为连接Strtus和Hibernate的桥梁,就在于要分别和它们来整合。
在具体的整合之前,我们再来思考一下,为什么要这么做。
因为Spring的IoC容器,我们把对象的创建权交给了Spring,所以action对象就得在spring中设置了。
具体有下面两个小步骤。
action说到底,还是类,只要是类,spring就能很好的管理它。在spring.xml文件中写上如下代码。
<!-- 创建action的bean对象 -->
<bean id="userAction" class="com.hailstudio.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
需要注意的就是action的scope需要设置为prototype,也就是多实例的。这是因为action本身的特性决定的。一个action要能被多次实例化,这样才能更好的处理多个客户端的请求嘛。
优化处理就是减少不必要的对象的创建。具体来说也就是这个action bean对象只需要被spring处理就行了。所以这个class的值设置为这个类的完整类路径。而不需要再strtus的action标签中再次声明,对应刚才的strtus.xml的
<package name="struts2" extends="struts-default" namespace="/">
<!-- 为了防止action被spring和strtus2加载两次,class的值写成spring的bean的id的值即可。 -->
<action name="userAction" class="userAction"></action>
</package>
这里面的class指定为对应的action在spring.xml的bean的id值就可以了。底层spring对很好的帮助我们处理。
类比刚才的Strtus和Spring的整合,核心思想就是把创建对象的操作全部交给Spring来处理。那么Hibernate中关于数据源啊,SessionFactory啊这些,都配置在spring.xml文件中就行了。
<!-- dbcp数据连接池配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssh"></property>
<property name="username" value="root"></property>
<property name="password" value="mysql"></property>
</bean>
<!-- 让SessionFactory的创建也交给Spring来实现 -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<!-- 把数据库的配置交给了spring的datasource了 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 指定Hibernate的核心配置文件, -->
<property name="configLocations" value="classpath:hibernate.cfg.xml"></property>
</bean>
HibernateTemplate是一个微型的框架,类似于dbutils和jdbcTemplate或者我自己写过的DbHelper(https://github.com/guoruibiao/dbhelper)。
简化了底层操作,是spring封装了Hibernate的模块。
需要明确的是,在dao层中使用HibernateTemplate需要注入的支持,所以注入的具体实现还是要交给spring来处理。
按照处理客户端的具体的流程:action–service–daoimpl–hibernatetemplate。写出下面的注入配置就可以了。
<!-- 创建action的bean对象 -->
<bean id="userAction" class="com.hailstudio.action.UserAction" scope="prototype">
<property name="userService" ref="userService"></property>
</bean>
<!-- 创建service相关 -->
<bean id="userService" class="com.hailstudio.service.UserService">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
<!-- 创建dao相关 -->
<bean id="userDaoImpl" class="com.hailstudio.dao.UserDaoImpl">
<property name="hibernateTemplate" ref="hibernateTemplate"></property>
</bean>
<!-- 引入HibernateTemplate模板的支持 -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<!-- 通过构造函数或者setter注入的方式将sessionFactory传给模板对象 -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
我在这一点就忙活了好久,自认为代码这块没有问题了,但是一测试就会报错:没有active的session处理。查看了一些解决方案后原来是没有配置事务。在spring.xml使用下面的代码即可开启事务处理。
<!-- 开启事务管理器========然后开启注解 -->
<!-- 单纯配置好上面,无法完成持久化操作。必须要开启事务,否则只能为“只读”模式 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<!-- 注入会话期工厂,可以点到源码中看到原因。sessionFactory中已经包含了DataSource -->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
思路就是:配置事务管家,然后开启注解。开启注解是为了告诉框架在哪里开启事务。
通常来说会在service层中处理我们的业务逻辑,所以在service层开启事务是挺合适的啦。
如下:
package com.hailstudio.service;
import org.springframework.transaction.annotation.Transactional;
import com.hailstudio.dao.UserDao;
@Transactional
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void service(int flag) {
System.out.println("User service !!!");
userDao.add(flag);
}
}
在UserService类上面添加@Transactional注解即可。
至此,一个简答的SSH整合就算是结束了。下面简单的来演示一下效果。
最后来回顾一下本文的主要内容。也就是一点点的搭建了整个SSH框架整合的过程。
总的来说,思路虽然还算是清晰,但是步骤也着实多了些。耐心不足的话很容易放弃。
而且对于spring的bean,其实还可以仅仅通过注解的方式实现。在spring.xml加上
<context:component-scan
base-package="com.hail.action,com.hail.dao,com.hail.service,com.hail.model" />
就行了,然后配合@component(@Controller, @Service, @Repository)注解就可以完成在xml中起相同作用的配置。
烦烦扰扰的配置了这么多,项目本身也变得越来越大。真的是不容易啊,不过一旦搭建好了环境,剩下的就只剩写业务代码了。也就没什么难度了。
还是怀念我的PHP啊,完成相同的功能,却是那么的简单。