zoukankan      html  css  js  c++  java
  • 学习SSH全注解实例

    在JavaEE 企业级开发中,以SSH2 框 架为核心的应用非常广,大象根据项目实践经验,通过一个实例,详细的为大家讲解如何实现全注解式的开发。 
     
        开发环境 
        JDK 1.6.0 _18 
        Eclipse 3.2.1
        MyEclipse 5.1.0 
        Tomcat 6.0.10 
        MySQL 5.0.27 
        Navicat Lite for MySQL 8.1.20 
     
        每个人的开发环境可能会有差异,但有一点我需要说明的是,JDK 的版本不得低于1.5 ,因为用到了很多1.5 版 才支持的新特性。Tomcat 和MySQL 请不要低于我所用的版本,因为我没在其它的版本上进行测试。
     
     
    Navicat 则是MySQL 数据库的图形化操作工具。我 在这里假定各位目前已经设置好了开发环境,下面就开始详细的说明。
        由于要阐述的内容比较多,大象决定将它们划分成个几章节来讲,这一章就主要来说说jar 包 的选择。
     
        第一部分 : 选择必须的 jar 包 
     
        新建一个web 项目,然后将必要的jar 包COPY 到lib 里 面。根据本文实例demo ,大象给出下图中的最少jar 包配置。 
             
        我对这些jar 包 进行一下说明,方便大家理解。
        
        解压Struts 2.1.6 的lib 文件夹,从中选出上面7 个jar 包 添加到我们的工程库中。commons-logging 、freemarker、ognl 、struts2-core 、xwork 这5 个 还是struts2 的 核心包。但在Struts2.1.6 这个版本中,还需要加上commons-fileupload 包。如果没 有,则启动就会报错,不过不需要像网上传言的那样还得加上commons-io 的jar 包,这些大象都亲自做过测试。在 本实例中,我将对struts2 也采取注解的方式,所以用到了struts2-convention-plugin-2.1.6.jar这 个插件。因为要与spring 整合,所以struts2-spring-plugin-2.1.6.jar 也必不可 少。
        
     
        大象在这里偷个懒,直接将spring 的完整jar 包 加了进来,如果各位想精简类库的话,就选取它的分类jar 包吧。比如本例使用struts2 作为MVC 框 架,所以spring 的webmvc 就不可能用到了。有想改的朋友请自己动手改下。另外有点我想说 下,如果采取完整spring 的jar 包,还需要Spring 2.5.6 \lib\ concurrent 文件夹中的backport-util-concurrent.jar , 如果不加这个,spring 会报错。但是采取spring 分类jar 包 的形式,这个可以不用加,至于具体使用什么需要依赖这个包,大象还没去测试过,这个有待验证。还有lib\ slf4j 下的日志包,目前很多都开始采用基于 slf4j 接口的日志器,它的好处就是日志器是根据 slf4j 的接口来进行实现,可以在不改变代码的情况下更换日志器。最后 Spring 的源代码中使用的是 commons-logging 记 录日志,因此这个包不能少,不过因为struts2 也用到了,所以这里就省了。
     
        
     
        Hibernate 从3.3 版开始,对jar 包 结构做了一次大的调整,我们只需要加入lib\required 文件夹下面的6 个jar 包。 请注意这6个jar 包都是使用Hibernate所 必须的。另外再加上hibernate 核 心包。这里我将slf4j-api- 1.5.2 .jar 换成了 1.5.0 , 这是因为slf4j 是 一个通用日志 接口,不提供任何实现,我在demo 里面使用的是log4j ,而hibernate 包 里面没有log4j 的slf4j 实现。而且如果版本不一致,会有异常,因此我就采用 Spring2.5.6\lib\slf4j 里面提供的配套版本。另外我将 commons-collections-3.1.jar换成了 Struts2.1.6 里面的 3.2 版。
     
        
     
        例子中使用Hibernate JPA 来完成实体对象映射,所以上面这些包都必不可少。使用注解的方式,可以不用写繁琐的配置文件,降低了出错机率。而且现在很多人都喜欢 这种方式。大家可以去sourceforge 下载。 
     
        下载地址: http://sourceforge.net/projects/hibernate/files/ 
        
        本例使用DBCP 连接池来管理数据源。
        
        MySQL 数据库的连接驱动。
        
     
        这个包的作用是创建动态代理对象。比如在使用AOP 方式管理spring 事务时,如果我们的目标对象没有实现 接口,而又要使用AOP 来处理事务,这时就需要用到这个jar 包。可以在Spring 2.5.6 \lib\cglib 里面找到。
        
     
        JSTL 标签库,很经典的东东,如果需要可以将它们加入lib 中。
     
     
    ========================================
    在上一章中详细分析了JAR 包的选择,那么这次我将对例子中 的一些必须 的配置文件进行下说明。虽然这些配置在网上也很容易找到,但是很多都没有讲个因为所以出来,这样根本就得不到提高。在此,大象为各位详细分析一下这些内 容。
     
        实例中涉及的配置文件有这么几个
         applicationContext.xml
         jdbc.properties
         log4j.properties
         struts.xml
        web.xml  
     
        我准备在本章中只讲applicationContext.xml 、 jdbc.properties 和 web.xml 。log4j 的 配置大同小异而且也不在本文范围。至于struts.xml 我 准备留到后面与Action 代码一起来讲,因为用的是struts2-convention-plugin 插 件来实现struts2 的注解,所以这两个结合起来讲要好一些。
     
     
    第二部分:分析配置文件 
     
        1 、 jdbc.properties 
        本例采用MySQL 数据库,所以我设置了一个属性文件,用 来存放一些连接信息和Hibernate 相关的设置。
     
     
        因为我们使用的是Hibernate 来 与数据库进行交互,把这些东西写在单独的文件里,是方便修改,如果你想换成SQL Server 或是Oracle ,只需要更改driver 、url 以及 dialect ,而且还可以自由控制 sql 语句的显示的开关,非常方便。至于写在这里怎么用呢?请接着看下 面的 applicationContext.xml 说明。 
        2 、applicationContext.xml 
        这个文件就是spring 的主配置文件了,当然,本 例也只有这么一个spring 的配置文件,内容不多,但做的工作还是很多的,下面我给大家详细分析一下。
     
     
     
        我把这两部分放在一起是因为这两者是相互联系的,而且也比较好说明。可以这样来理解,PropertyPlaceholderConfigurer 这 个类就是读取jdbc. properties 文件,并将它们设置到这个类的属性中。然后再将下面数 据源配置中定义的这些$ {jdbc.driver} 、$ {jdbc.url} 字符串换成属性文件中相 同名称的值。$ {} 这种写法,是类里面方法解析用的,网上都说这是叫占位符,我看了源代码的,其实是把它们当成字符串截取 前后的特殊字符,再根据里面定义的名称找属性文件中对应的值。所以这个类只能读取properties 格式的文件,你如果还有其它 需要加入的属性文件,可以在list 之间加入,写在value 标签里面。      
     
        根据base-package 指定的路径,扫描其下所有包含注解的Bean , 并自动注入。比如@Repository ,@Service 这些都是注解,前者表示持久层, 后者表示业务层。这可是非常非常好的一个功能,是从Spring2.5 开始加入的一个非常棒的特性。有了它,我们将不用再去写那 繁琐的<bean id="" class="" /> 。本文的主旨就是全注解,就是为了告诉大家不用写配置文件(当然不是绝对不写)来怎样进行开发工作。关于这部 分的具体情况,在后面代码章节中会详细讲解。
     
     
     
        这就是在Spring 中定义Hibernate 相 关的配置,Spring 已经集成了这部分功能。通过class 里面定义的类名称我们很容易就能理解, 这是使用注解的方式映射实体以及创建Hiberante SessionFactory 。$ {hibernate.dialect}、$ {hibernate.show_sql} 和 上面的数据源配置获取方式一样,当applicationContext.xml 定义好之后,就不用再对它进行修改,而是将修改对 象变成了jdbc.properties 文件。
        另外在Spring 2.5.6 版中,加入了一个很有用的小功能,就是packagesToScan 属性,它是根据value 中 定义的路径来扫描其下所有的注解实体类。大象对这个路径做了多种测试,另外又看了源代码,发现它只能匹配某一类型的路径, 而不是所有路径。比如上面的 value 值表示,扫描entity 包下面的所有包中的注解类,如果你将 类直接放在entity 包下,那么服务器启动和程序运行时都不会报错,但是当你的代码需要用到这个类的时候,就会出现异 常,提示你找不到实体。
         
     
     
        这是事务定义,而且是使用注解方式定义事务 ( @Transactional ), proxy-target-class = "true" 表示采用动态代理类来管理事务,如果是false 表示采用接口代理来管理事务(默认值为false )。 什么意思呢?就是说对于需要加入事务处理的类,如果是实现接口,那么将采用Spring 的默认事务管理(Spring 默 认方式为接口),如果不采用接口,而直接使用类,那么就需要cglib 类库的支持,它通过动态的创建目标类(就是你需要加入事务的类)的 子类,然后对这子类中的方法(当然是从目标类中继承来的)进行事务管理。这其实就是AOP 切面,而且从中可以看出来,需要加入事务的 方法不能为private 、static 、final 的方法。这样说也不是很严格,说它不 能加入事务,是说它不能主动的启动一个事务,如果某个private 方法是被某个public 方法调用的,而public 方 法是可以被动态代理加入事务的,所以这个private 方法也一样被加入了事务,只是它处在public 方 法的事务之中。但是static和final 这两类方法因为不能被子类覆盖,所以无法加入事务。如果这两类型的 方法不被其它的事务方法所调用,那么它们就会以无事务的方式运行,因此很容易造成隐患,这一点请大家特别注意。
     
     
     
        上 面这个就是使用配置式来定义事务,两种方式的区别主要是,注解式只用写那么一句话,然后在业务类或方法中加入 @Transactional 这个注解标记,就完成事务声明,不过对于 每个业务类都需要在类或方法中加入这些标记。而配置式声明,就是不用加这些标记,只要你的方法名称命名比较统一,就可以像上面这样定义事务规范,然后在aop 标 签中定义切入点与执行通知就行了。我感觉如果业务逻辑不是太复杂的情况,配置式会比较简单,而且修改起来也方便,这两种方 式我都写出来了,至于用哪一种,由你们自己决定。 
        3 、web.xml 
        现在使用的Servlet 容器还是2.4 版, 因此web.xml 里 面还是需要写配置文件的,到了3.0 版就可以采取注解的方式来实现了。
     
        Spring ApplicationContext 配置文件的路径, 可使用通配符, applicationContext*.xml 表示所有以applicationContext 开头的 xml 文件。 多个路径用, 号 分隔。比如可以这样写:
     
        不过推荐采用通配符的写法,能够简单点,为什么还要弄那么复杂呢?
        context-param 是在容器启动后最先被执行的,并且被放入到容器上下文中。在这里引入 spring 的配置文件,是供 Spring 的 ContextLoaderListener 监听器使用。而这个监听器中会有一个 ContextLoade 类用来获取这个配置文件中的信息。从而进行 Spring 容器的初始化工作。 因为是采用注解的方式来进行开发,所以 spring 的配置文件其实只有一个,上面那个星号可以去掉。
     
        这个监听器就是为了读取Spring 的 配置文件,这在上面已经讲到了。
     
     
     
        这是Spring 提供的一个用来防止内存泄漏的监听 器。在我们使用struts2 框架,或其它的某些类库时,因为它们自身的设计,会用到Introspector (内省)机制来获取 Bean 对象的信息。但不幸的 是,这些框架或类库在 分析完一个类之后却没有将它从内存中清除掉,内存中还保留有大量的静态资源,而这些东西又无法进行 垃圾回收,因此产生了很严重的内存泄漏问题。直接表现为服务器的内存使用会随着时间而不断上升,最后的结果当然就是服务器当掉。所以在这里加入此监听器, 能够帮助我们更好的处理内存资源回收的问题。
     
     
        这是Spring 的编码过滤器,我们可以直接拿来用,相信这段配置应该很好理 解,不过请大家注意forceEncoding这个参数,把它设置为true 表示不管请求中的编码是什么格式,都将强 制采用encoding 中设置的编码方式。另外对于响应也将按照encoding 指定的编码进行设置。 另外不建议将编码设置成gb2312 或是gbk 格式,请采用基于Unicode的UTF-8 编码。
     
     
     
        这个过滤器是个好东西,有了它,我们在使用Hibernate 延 迟加载的时候,就不会再为因Session 被关闭,导致延迟加载数据异常而头痛了。网上有很多人说这个不好,其实在使用中,效果还是不错 的。
     
     
        首先我要说这个过滤器的名字很雷,不知道写这类的家伙是不是个变态,或 者喜欢恶搞。主要原因就是,这个过滤器的功能是推迟清理值栈中的值,以便在web 层中进行访问,另外就是为了配合SiteMesh 装 饰器进行工作(官方中的说明)。如果不加这个,那么Struts2 的默认过滤器就会清空值栈中的值,这样就会导致异常。所以说这 类的名字和功能完全不搭边,很容易让人产生误解。
     
     
     
        在 2.1.6 版本里面,已经用这个过滤器取代了以前的FilterDispatcher ,而且在api 文 档中已经标注为 @deprecated(过时) ,并说明是从Struts 2.1.3 版开始就弃用这个过滤器了,改用StrutsPrepareAndExecuteFilter ,除此之外,还 可以选择StrutsPrepareFilter 和StrutsExecuteFilter 。不过大 象建议大家还是选择StrutsPrepareAndExecuteFilter吧,这也是官方推荐的。至于这个过滤器的真正用 途,我想在后面具体讲代码的时候再来详细分析它。
        web.xml 里面的几个重要的配置就这些,不过不要忘了,给这些filter 加 上filter-mapping 映 射。还有一点,请注意这些过滤器的顺序,这个顺序是很重要的,程序运行时,是根据这些filter-mapping 的排列顺序依次执行 过滤操作的。如果不想出现莫名其妙的错误,请控制好这些过滤器映射的顺序。
     
     
    ========================================
    在前两章我为大家详细分析了 JAR 包的选择和必须的配置文件,那么这一章,我就对例子的层次结构进行说明,并实现除 WEB 层的功能代码。
         第三部分:建立框架代码
         工程结构
     
        
    大家可以看到,本例一共分为: dao 、 entity 、 service 、 web 四层。另外在这些层次下,还以业务功能再进行分包,这样做是为了方便在以后的功能扩展中,能更好的管理和维护代码。如果将所有类都直接集中在这 4 个包下面,随着类的增加,会越来越难以维护,而且查找起来也很费劲。
         HibernateDao
        在本例中,我是通过继承 Spring 提供的 HibernateDaoSupport 来实现持久层的基类。同时引入泛型参数,封装了一些基本操作方法。
     
       
           这是 HibernateDao 的部分代码,引入的这个泛型参数,其实就是实体类( User 、 Role )。通过传递这个实体类,在构造方法中利用反射特性将它从 JVM 中取出来。这里的 getClass() 方法是获得继承 HibernateDao 的类( UserDao 、 RoleDao ) getGenericSuperclass() 方法就是通过这些继承了 HibernateDao 的类,来得到父类(父类就是 HibernateDao )的泛型。注意这个方法的返回值为 Type ,这是一个类型接口。请参考 API 。因为在继承 HibernateDao 的时候,会给它加一个泛型参数。比如, User 、 Role 实体类。因此超类是参数化类型,所以返回的  Type 对象包含所使用的实际类型参数。这里返回的 Type 对象是 ParameterizedType 接口的实现类ParameterizedTypeImpl ,所以要将返回类型转型为 ParameterizedType 。 getActualTypeArguments() 方法是ParameterizedType 接口中的,它的作用就获得实际类型参数 Type 对象的数组,因为我们这里只定义了一个泛型参数,数组里面也只一个值,所以需要在数组下标处填 0 。然后最后一步转型千万别忘记了,因为这个方法返回的可是一个 Type 数组喔。如果对于这部分的说明还有点不理解的话,请到时候有了代码,设个断点跟踪一下,就会全部清楚了。关于 java 反射,它已经超出本文的范围。大象只对本例中用到的部分进行讲解。使用这种写法,是方便我们进行类型转换与类型检查。另外还可以简化某些方法的写法。比如:createCriteria(Criterion... criterions) 这个方法。参数是 Criterion 类型的可变参数,它是用来设置查询条件。如果要进行对象化查询,那么最简单的写法就可以直接写成 createCriteria() 。另外还有重载的方法,可以根据传入 class 类型来创建自定义查询。
         dao
        持久层的 Dao 类是根据实体类定义,一般是一个实体类就会有一个对应的 Dao 类。当然这要跟业务需求来设计,不是绝对的。另外你也可以为了简便而去掉 dao 层,将持久化操作与业务逻辑全部写在 service 层。
     
        
          这些定义的方法是供 service 层调用,在业务层,将不会看到一行与持久层有关的代码,降低藕合性是这样做的目的。
    @Repository
    注解的作用就是标注这个 UserDao 是一个持久层组件。还记得前一章讲到的扫描器吗? component-scan  它就是用来将标有 @Repository , @Service 这样的注解类扫描到 Spring 的容器里,并且同时对标有 @Autowired 注解的Bean 启用了自动注入功能。这就是 Spring 从 2.5 开始提供的新特性。我们使用注解的方法,就可以告别那繁琐的配置文件定义。
         entity
    关于实体的定义就是使用 JPA 注解,关于这部分,我以前写过一篇文章专门讲这个,如果有不清楚的朋友可以先参考一下。 学习 JPA—— Hibernate Annotation 使用实例
        本例中,我有两点要讲下。
    第一、管理主键的表 generator_table 去掉原来单独定义的那个 ID 主键,把 g_key 设为主键,整个表将只有两个字段, g_key 和 g_value 。
         第二、在 User 实体中,我将角色 ID ( role_id )与角色实体( Role )做了一个多对一关联。这一点是原来文章中没有讲过的。
     
        
          请一定注意 role_id 是 user 表的字段。我在本例中设定的是一个角色可以对应多个人员,所以这个 role_id存的就是 role 表 id 的值。 fetch = FetchType. LAZY 指定采用延迟检索,如果当你取得了 User 对象,但又不想取 Role 中的信息,这时, User 对象中的 role 属性是代理状态。 Role 对象中的值都是空的。只有当你使用role.id 或 role.name 进行取值的时候, hibernate 才会去数据库中查找对应的记录,因此在一定程度上降低了资源消耗。不过这里有点要注意,采用延迟检索的时候,需要加上前一篇讲到的 OpenSessionInViewFilter 过滤器。否则会遇到 session 关闭的异常。
         service
       
     
     
     
     
         @Service 表示这是业务层组件。在业务层需要对 UserDao 加上 @Autowired 注解,我在这里将业务层的方法名与持久层的方法名定义为一样的,是我的一种习惯,大家可以按自己的想法来做。
         测试
        既然有了这么多代码,那就来测试一下吧,看看有没有问题。
     
     
     
    为了照顾那些坚定的 JUnit 拥护者,再写一个 JUnit 测试。本例使用 junit-4.4.jar
       
     
     
     
     
         @BeforeClass 注解的方法表示在类实例化之前,也就是在类的构造方法执行之前就会执行。而且使用这个注解的方法必须是 static void
         @Test 标明这是测试方法,方法名不用像以前那样必须按照规则进行命名,可以自由定义。 上图显示大象使用JUnit 方式测试也通过了(如果不会通过我写它干嘛?嘿嘿)。    假如我将张三改成张四,再来看看测试结果。
     
     
     
     
         这个截图可以很明显的说明所有东西。这一篇是给大家讲怎么用代码来实现除 web 层之外的全注解步骤。当然,我主要是讲思路,其实思路比代码重要得多。这一个系列的最后,我会放上所有源码供大家下载。现在这样慢慢分析,是想给大家讲道理。我们应该努力提升自己的境界与层次,而不要只把眼光放在代码上面。下一章将会着重介绍 web 层,以及 struts2 的注解插件 struts2-convention 。  
  • 相关阅读:
    常见的线性结构
    Lambda表达式学习笔记
    Spring Security 入门 (二)
    Spring Security 入门(一)
    Eclipse 创建 Maven 项目
    初学 Spring MVC(基于 Spring in Action)
    蓝桥杯之入学考试
    Java 学习总结
    二叉搜索树和红黑树
    Detours 劫持
  • 原文地址:https://www.cnblogs.com/huapox/p/3251559.html
Copyright © 2011-2022 走看看