今日开始进行OA项目了,OA是一个大型的办公自动化管理系统。汤老师使用6天的时间带领我们做这个项目,显然是不可能全部完成的,我们要做其中重点的几个模块。这个项目将对我们之前学习的struts1、hibernate3、jbpm3、jstl1.1、junit4进行综合性的系统练习。
在跟着老师学习新技术时,课堂上我们能很好的理解各知识点。但放到一起,在实际项目中应用时还时感觉有点陌生。不过还好,以前的工作经验和每天整理学习日志让这个项目的开端并未对我构成多大挑战。但这对一个新人来说,是需要反复揣摩的,也正是这种实践性的练习才让我们掌握和混用理论性的知识。
做为一个应用级别的开发人员不仅要熟练掌握编程技术,而且也要对各业务流程有一定的了解。这在日后的开发工作中将起到重要的作用。说的直白些,就是熟练、稳健的使用编程技术将业务流程搭建起来。所以在这种实践性项目中,我不仅要摸清各项技术的应用,也要熟悉业务流程,更要学好项目的架构。但是这个项目多少有些让我失望,因为它的重点是让同学们将之前学习的知识做个综合练习,所以没有从详细的需求分析、编程文档...一一到来。
早上汤兄只是简单的详解了OA各大模块,然后选出了其中的几个小模块做练习。下面我来总结一下。
一、OA办公自动化系统
利用网络通讯基础及先进的网络应用平台,建设一个安全、可靠、开放、高效的信息网络和办公自动化、信息管理电子化系统,为管理部门提供现代化的日常办公条件及丰富的综合信息服务,实现档案管理自动化和办公事务处理自动化,以提高办公效率和管理水平,实现企业各部门日常业务工作的规范化、电子化、标准化,增强档案部门文书档案、人事档案、科技档案、 财务档案等档案的可管理性,实现信息的在线查询、借阅,最终实现"无纸"办公。
其详细资料可以查询互联网...。
二、环境搭建
1.开发环境MyEclipse
2.创建一个WEB工程
3.在工程下添加资源目录:
process:jbpm使用的目录
config:hibernate、jbpm等工程使用的配置文件
test:单元测试类存放的目录
4.在WebRoot目录下添加:
script:脚本文件存放目录
style:CSS样式文件存放目录
5.在WEB-INF目录下添加:
pages:页面存放目录
pages/department:与部门相关的页面
pages/employee:与职员相关的页面
pages/role:与角色相关的页面,角色是一种权限分配的方法。经理、人事、秘书...这就是角色,角色具有相应的权限组。比如经理具有审核员工、要员调动等权限。
6.向工程中导入jar文件:
struts1、hibernate、mysql、jbpm、junit4相应该的jar文件。
可以在工程上右键-->"MyEclipse"-->"...",添加相应的框架jar文件。
7.向工程中添加配置文件:
Struts1和hibernater的配置文件。
三、基础功能
1.DAO:
将WEB应用分三层架构,这样使得程序更加易于编写与维护。三层架构并不总适合于各WEB应用,在某些WEB应用中业务逻辑简单,这样使用三层就没必要了。所以将service与dao层合并到一起。汤兄说先搞成三层的,然后我们再看有没有必要这样搞。然后再搞成二层的。
我们的层与层之间使用接口连接,这样是为了可以方便的更换其中某一层的实现。比如数据访问层,我们将数据库更换为XML文件。但不需要修改service的代码。这个早就说过了,只不过今日再次提出让大家体会更深刻些。
汤老师使用staruml工具画图,接口与类的关系。我也用这个工具画一下,感觉老好了:
按照上边的关系,编写接口与实现类,这个非常简单。我们使用hibernate访问数据库,所以在DaoBaseImpl类的方法中使用hibernate的Session进行数据库访问。
2.事务管理:
在一个请求中,我们如何保证这个请求的所有操作使用的是一个Session?比如,一个请求同时调用了save和delete操作。我们需要增强代码的重用,如何使得这两次调用只打开和关闭一次Session?
我们有什么好方法?编写一个过滤器,在调用doFilter之前打开一个Session,在调用doFilter返回后关闭这个Session。我们这个过滤器过滤一“*.do”的请求。但这样会导致,不需要Session操作数据库的请求也会创建和关闭一个Session,这是完全浪费资源的。但我们有一好的解决办法,看下面的实现。
我需要需要编写一个HibernateSessionUtils类,用于管理hibernate的Session:
package cn.itcast.oa.utils; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateSessionUtils { // Session工厂 private static SessionFactory sessionFactory = null; static { sessionFactory = new Configuration().configure().buildSessionFactory(); } // 一个请求对应一个线程,为了使一个请求统一使用一个session。 // 所以在这里的使用ThreadLocal,ThreadLocal<Session>是Map<Thread, Session>的简化形式。 private static ThreadLocal<Session> sessionMap = new ThreadLocal<Session>(); /** * 取当前线程的session * 即使使用了过滤器,但没有使用Session的请求是不会调用getCurrentSession(true)的。 * @param creat * true且当session为null时自动创建session,否则返回null。 * @return */ public static Session getCurrentSession(boolean creat) { Session session = sessionMap.get(); if (creat && session == null) { session = sessionFactory.openSession(); // 任何地方当初次使用Session时便开户事务 session.beginTransaction(); sessionMap.set(session); } return session; } /** * 关闭和删除session */ public static void closeAndRemoveSession() { Session session = sessionMap.get(); if (session != null) { session.close(); sessionMap.remove(); } } } |
在一个请求中,我们如何保证Session事务的正确处理?比如,经典的银行转账示例。Ok,看了上边的代码,我们在初次使用Session时便开户事务,那何时提交或回滚事务呢?我们在过滤器中实现:
package cn.itcast.oa.web.filter; import java.io.IOException; import javax.servlet.*; import org.hibernate.Session; import cn.itcast.oa.utils.HibernateSessionUtils; /** * 过滤*.do的请求 * @author Administrator * */ public class HibernateSessionFilter implements Filter { public void destroy() { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { // 继续调用Action chain.doFilter(request, response); // 获取当前请求的Session,返回null表示当前请求没有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果当前请求使用了seesion,则提交事务。 session.getTransaction().commit(); } catch (Exception e) { // 获取当前请求的Session,返回null表示当前请求没有使用session Session session = HibernateSessionUtils.getCurrentSession(false); if(session != null) // 如果当前请求使用了seesion,则回滚事务。 session.getTransaction().rollback(); throw new ServletException(e); } finally { // 关闭和删除session HibernateSessionUtils.closeAndRemoveSession(); } } public void init(FilterConfig arg0) throws ServletException { } } |
使用过滤器进行session管理还不算是一个好的方法。因为我们还没有实现service层,所以在这里我们使用了Filter。管理session更好一些的方式是在service层中。因为service层中的一个方法处理一个业务逻辑请求。
四、BEAN对象管理
我们每编写一个实现类的功能时,需要进行单元测试在这里就不多说了。
我们在单元测试或service层中需要创建Dao的接口对象,来完成对数据的访问。如果我们直接new DaoImpl,这样并未实现层与层之间的分离。此时经典的工厂模式派上了用场,这个模式从JAVA基础增强、JAVAWEB到现在都有应用过。
我们通过工厂获取需要的接口实现类对象,但如果整个系统中共需要100个或者更多的不同接口实现类,难道我们需要在工厂类中添加相应数量的getxxx方法?汤老师经验丰富且十分有才,他使用了一个方法就可以解决获取任意接口实现类的对象:
package cn.itcast.oa.utils; import java.io.*; import java.util.Properties; public class BeanFactory { /** * 获取指定简单接口名对应的实现类 * @param <T> * @param clazz * @return */ public static <T> T getBean(Class<T> clazz){ InputStream ins = null; try { // 加载BeanFactory.properties属性文件 Properties pros = new Properties(); ins = BeanFactory.class.getResourceAsStream("/BeanFactory.propreties"); pros.load(ins); // 读取实现类名 String className = pros.getProperty(clazz.getSimpleName()); // 返回类实例 return (T) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } finally{ if(ins != null) try { ins.close(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } } } } |
这个BeanFactory非常经典!
汤兄授课是对某一应用的实现由粗糙到细致、由狂乱到优雅,这一教学方式使得大家懂得最终的实现方式是如此优雅。
汤老师有些内项,但他十分愿意与学生们分享学习心得,并且给学生们很大的鼓励。老张、老方、老佟、老徐、汤兄都鼓励学生们努力学习...非常好!