前面的一系列文章中,我们总结了三大框架:Struts2,Hibernate,Spring 的基本知识。本篇就姑且尝试着使用 Maven 这个项目构建工具来将这三个框架整合一起。说到这里,如果有对 Maven 还不熟悉的同学,此处推荐下面两个链接快速了解下,记得回来!
Maven 是什么? 如何给小白说明 Maven 是什么?
我对 Maven 的理解就是,它是一个工具能提供两大主要功能,其一是依赖管理,其二是项目构建。
所谓的依赖管理就是指,我们对于框架中的 jar 包从此不需要手动的添加到项目中来,而是使用 Maven 的语法进行引用,当然在打包发布的时候,这些包还是会被加入进来,但是在我们的源代码中就不存在任何的 jar 包,整个项目轻量可移植性强。
而所谓的项目构建指的是,对项目进行编译,测试,打包,发布等。原先我们是通过 IDE 来完成这样的工作,但是不同的 IDE 有不同的使用操作,但是如果你是 Maven 项目的话,无论你在哪种 IDE 中编码,都可以使用相同的 Maven 命令进行上述的几种操作,这其实在一定程度上减少了我们对 IDE 的依赖。
上述我们简单介绍了 Maven 这个项目管理工具,具体深入的学习不是本篇的重点,本篇着重完成对 SSH 框架整合的一个操作,主要涉及以下内容:
- 分别配置各个框架的运行环境
- 了解整个 Web 项目配置文件的加载顺序
- Spring 整合 Struts2
- Spring 整合 Hibernate
- 创建Action,Service,Dao 模拟登录过程
- 项目的模块分离与再聚合
一、分别配置各个框架的运行环境
首先,我们先将三个框架各自的运行环境都配置到我们的 web 项目中。
创建一个 Maven 的 web 工程,基本的目录结构大致是这样的:
然后我们先来配置 struts2 的运行环境。 第一步,pom.xml 中依赖 struts2 的核心包:
代码语言:javascript复制<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.3.32</version>
</dependency>
由于 Maven 会自动引入该核心包所依赖的其他 jar 包,所以我们无需在手动依赖其他任何 jar 包。
第二步,web.xml 中配置核心过滤器:
代码语言:javascript复制<!--配置Struts的核心过滤器-->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步,创建 struts.xml 用于配置控制器 Action。
至此,struts2 的运行环境已经配置完成,下面我们看 Hibernate 运行环境的配置。 第一步,依赖相关的 jar 包,我们看看 Hibernate 需要依赖哪些 jar 包?
代码语言:javascript复制//mysql 驱动的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
//Hibernate 核心包依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.11.Final</version>
</dependency>
同样,hibernate 核心包需要依赖的其他 jar 包,Maven 会为我们自动查询并引入。
第二步,创建 Hibernate 核心配置文件 hibernate.cfg.xml 并完成基本信息的配置。
需要说明一点的是,为了简单起见,这里我们并没有使用数据源进行数据库的连接,等到与 Spring 整合的时候会使用数据源配置数据库连接。
至此,Hibernate 的基本环境也已经配置完成,下面我们看 Spring 的环境配置。
第一步,引入 Spring 相关依赖:
代码语言:javascript复制<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
第二步,创建 Spring 核心配置文件 applicationContext.xml 。
第三步,web.xml 中创建 Spring 的配置文件加载 Listener,它的具体作用我们下一小节进行介绍。
代码语言:javascript复制<!--配置Spring监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListene</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
这样,三个框架各自的运行环境就已经创建完成了,但是显然它们现在是不能整合共同工作的。整个项目的目录结构如下:
二、了解整个 Web 项目配置文件的加载顺序
如果上述操作无误的话,现在我们的 web.xml 配置文件中起码应该是这样的:
当整个 web 应用启动时,首先会去加载 web.xml ,其中会启动 struts 的核心过滤器,然后我们的 Spring 监听器监听到 web 容器启动后,会根据 ServletContext 中保存的我们的配置参数信息去加载 Spring 的配置文件。而一般来说,Spring 整合 Hibernate 之后,Spring 的配置文件中会指明去加载 Hibernate 的配置文件,更具体的我们下文会详细介绍,此处只需要了解 Spring 配置文件中会去加载 Hibernate 的核心配置文件。
那么,这样的话,我们三个框架的核心配置文件已经能够按顺序加载了。下面我们两两整合框架,先进行 Spring 与 Struts2 的整合。
三、Spring 整合 Struts2
Spring 与 Struts2 整合的核心点在于,将 Struts2 的 Action 交由 Spring 的 IOC 容器来创建。我们先创建一个 Action,
代码语言:javascript复制//index.jsp
<body>
<form action="login" method="post">
姓名:<input type="text" name="name" /><br/>
<input type="submit" value="提交" />
</form>
</body>
代码语言:javascript复制public class LoginAction extends ActionSupport {
private String name;
//省略getter,setter方法
public String execute(){
if("single".equals(this.name)){
return SUCCESS;
}else{
return ERROR;
}
}
}
Spring 中配置该 Action 的实例信息:
代码语言:javascript复制<bean id="login_action" class="Actions.LoginAction" scope="prototype">
</bean>
接下来在 struts.xml 中引用该 bean 即可。
代码语言:javascript复制<package name="myPackage" extends="struts-default">
<action name="login" class="login_action">
<result name="success">/welcom.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
注意我们这里 action 标签中的 class 属性已经不再指向具体的 Action 类了,它指向的是 Spring 中的实例 bean。
至此,对于 Struts2 和 Spring 的整合大致上结束了,为什么说大致呢?当你启动 web 容器运行上述程序时,会报错:
代码语言:javascript复制Unable to load configuration. - action .....
告诉你无法加载 struts.xml 配置文件,其实就是 action 的 class 属性对应的类找不到的意思。解决办法是,添加一个 Spring 插件依赖,该插件会让容器在找不到对应的实体类的时候,去 Spring IOC 容器中找。具体依赖是:
代码语言:javascript复制<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.3.15.1</version>
</dependency>
然后我们启动 web 容器测试我们的整合效果。
整合成功! Struts2 与 Spring 的整合还算简单,Spring 整合 Hibernate 则相对复杂一些。
四、Spring 整合 Hibernate
整合 Hibernate 的第一步依然是依赖相关 jar 包。
代码语言:javascript复制//MySQL 驱动包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
//c3p0
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5</version>
</dependency>
第二步是配置数据源和 session 工厂。
代码语言:javascript复制<!--读取属性配置文件内容-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置c3p0数据源-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${diverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
创建一个属性文件保存 jdbc 连接的基本信息,连接驱动,连接 url,用户名,密码等。然后我们在 Spring 配置文件中配置了一个单例的数据源,它负责创建于数据库的连接。
接着配置一个 SessionFactory 实例:
代码语言:javascript复制<!--配置session工厂-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocations" value="classpath:hibernate.cfg.xml"/>
</bean>
创建 session 工厂的时候会去加载 Hibernate 的核心配置文件,这里我们通过注入 configLocations 属性的值告诉 Spring 该文件的位置。这里还需要引入一个在 Spring 配置文件中支持 Hibernate 相关操作的一个插件:
代码语言:javascript复制<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
Spring 对 Hibernate 的整合也基本结束,下面我们通过模拟一个登陆操作来初步体验下整合后框架的整体运行过程。
五、模拟登录过程
为了项目之间各个模块的耦合性低,我们通常会选择将整个项目分分层,Action 控制器拦截请求,Service 处理业务,Dao 完成对数据的存取。
MyDao 中封装着我们对于数据库的所有操作,MyService 中封装着我们所有的业务逻辑。我们先在 Dao 层中添加一个方法用于根据用户名返回给用于所有信息,也就是返回该用户名所匹配的那条记录。
代码语言:javascript复制public class MyDao {
private SessionFactory sessionFactory;
//省略getter,setter方法
public UserInfo getByName(String name){
Session session = this.sessionFactory.openSession();
List list = session.createSQLQuery("SELECT * FROM logins WHERE NAME ='" name "'")
.addEntity(UserInfo.class)
.list();
session.close();
if(list != null && list.size() != 0){
return (UserInfo) list.get(0);
}else {
return null;
}
}
}
代码语言:javascript复制//Spring IOC 容器中配置一个 MyDao 实例
<bean id="myDao" class="Dao.MyDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
接着我们写 Service 层:
代码语言:javascript复制public class MyService {
private MyDao dao;
//省略getter,setter方法
public boolean sure(String name,String pwd){
UserInfo userInfo = this.dao.getByName(name);
if(userInfo == null){
return false;
}else {
if(userInfo.getPwd().equals(pwd)) return true;
else return false;
}
}
}
代码语言:javascript复制//Spring IOC 容器中配置一个 Service 实例
<bean id="myService" class="Service.MyService">
<property name="dao" ref="myDao"/>
</bean>
创建 Service 实例的同时并将 MyDao 实例注入到其中,逻辑上实现了 Service 调 Dao,组件调用。下面写控制器:
代码语言:javascript复制public class LoginAction extends ActionSupport {
private String name;
private String pwd;
private MyService service;
//省略getter,setter方法
public void setName(String name) {
this.name = name;
}
public String execute(){
boolean flag = this.service.sure(name,pwd);
if(flag) return SUCCESS;
else return ERROR;
}
}
代码语言:javascript复制//Spring IOC 容器中配置 Action 实例
<bean id="login_Action" class="Actions.LoginAction" scope="prototype">
<property name="service" ref="myService"/>
</bean>
代码语言:javascript复制//struts.xml 中使 Action 生效
<package name="myPackage" extends="struts-default">
<action name="login" class="login_Action">
<result name="success">/welcom.jsp</result>
<result name="error">/error.jsp</result>
</action>
</package>
好了,我们来测试一下!
上述我们使用整合后的框架做了一个小案例,由于代码量较小还不能体现框架分层处理的优势,但是我们还要对其进行更深层次的解耦。
六、项目的模块分离与再聚合
上述的项目中,我们的 Dao,Service,Action 等层次的代码都处于同一个项目中,项目的模块化就是将这些层分离出去,然后再以热插拔的形式聚合回原项目,这样的话,项目中的各个组件都是一个一个的子模块,那么任意一个模块出现问题后,可以立即用一个新模块接上。这种模块化的思想已经越来越成为一种主流的思想。
下面我们来介绍下,如何拆分出这些模块。
首先我们要新建一个工程作为父工程,这些 Dao,Service,Action 都是它下面的子模块。打个比方来说,父工程就相当于电脑主板,Dao,Service,Action 都是热插拔在上面的内存条、硬盘等部件,主板上的资源在各个部件上都是可见的。所以我们的父工程只需要做一件事,依赖子模块所需要的所有的 jar 包。
这个父工程什么都不要有,只需要有一个 pom.xml 文件用于依赖所有需要的 jar 包即可。然后我们将上一个项目中所有需要依赖的 jar 包都添加到父工程的 pom.xml 中进行引用。
然后我们选中项目创建第一个子模块 Dao,
将属于 Dao 的所有相关文件全部移植到 Dao 模块中来。
除了这些,还有一些配置在 Spring 中的代码,我们也要拷贝过来。
拷贝原先的 Spring 的配置文件并删除和我们 Dao 层无关的所有配置代码。这就完成了对 Dao 模块的分离,下面我们分离 Service 模块。
第一步是一样的,先创建一个子模块然后移植过来原先项目中和 Service 相关的所有代码文件。
相关的文件拷贝后的 Service 目录结构如上图所示,但是整个 Service 模块是报错的,原因很简单,Service 现在是独立于 Dao 的,但是它又是依赖 Dao 的,所以我们只需要将 Dao 模块打包发布到本地仓库,然后 Service 通过 Maven 依赖即可。
代码语言:javascript复制<dependency>
<groupId>top.walkeryam</groupId>
<artifactId>Dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
依赖 Dao 包之后就会发现项目不再报错了,最后我们分离 Action。
第一步依然是创建子模块并移植过来相关的所有代码,
这里的报错和上述的原因是一样的,Action 要依赖 Service ,所以我们只要为其添加 Service 的引用即可。
代码语言:javascript复制<dependency>
<groupId>top.walkeryam</groupId>
<artifactId>Service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
但是还没完,这么多 Spring 配置文件,web 容器怎么知道加载哪个? 修改 web.xml 中有关 Spring 配置文件位置的代码:
使用通配符告诉容器加载所有类路径下符合指定规则的配置文件,这样我们各个模块中的 Spring 的配置都可以被加载了。
下面我们将 Action 打成 war 包,并存放到 web 容器的项目目录下,单独运行 Action,由于 Dao 和 Service 都已经以热插拔的形式接入到 Action 中了,所以直接运行 Action 是没有问题的。
启动本地 Tomcat ,浏览器中进行测试。
测试成功!那至此我们也完成了 Maven 项目的模块化拆分与聚合的操作,项目之间的各个组件耦合度降低,每个组件又都是可热插拔的,一旦哪天项目中的某个组件崩溃导致整个项目挂了,可随机替换出错的组件。
模块化的思想已经越来越成熟了,不知道本篇上述的所有内容阐述的是否到位,但依然建议你深入理解整个解耦模块化的思想。总结不到之处,望指出!