Spring + SpringMVC + Druid + JPA(Hibernate impl) 给你一个稳妥的后端解决方案

532676955 7年前
   <p>最近手头的工作不太繁重,自己试着倒腾了一套用开源框架组建的 JavaWeb 后端解决方案。</p>    <p>感觉还不错的样子,但实践和项目实战还是有很大的落差,这里只做抛砖引玉之用。</p>    <p>大体采用的开源项目有:Spring + SpringMVC + Druid + JPA(Hibernate Impl)。</p>    <h3><strong>1. 采用到的开源项目漫谈</strong></h3>    <p>Spring 迷人的依赖注入特性, 使其已经稳稳的占据在 JavaEE 项目引用开源项目列表中的上层位置。</p>    <p>秉承低耦合高内聚的遵旨, Spring 提倡的对象工厂解耦类关系的思想已深入到每个攻城狮的心中。</p>    <p>SpringMVC 做为 Spring 的干儿子,最让我沉醉的是她强大的扩展能力,深邃的像大海一样。</p>    <p>前端无论是 freemarker/velocity/jsp...,后端 DAO 层无论是传统的 ORM 还是新近上位的领域模型。</p>    <p>她的态度始终如一,给你360度最贴心的呵护,有一人对你如此,此生足矣。</p>    <p>项目中关于 SpringMVC + Spring 的依赖:</p>    <pre>  <code class="language-java">       <!--spring mvc-->          <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-webmvc</artifactId>              <version>4.3.3.RELEASE</version>          </dependency>            <!-- Spring-orm -->          <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-orm</artifactId>              <version>4.3.3.RELEASE</version>          </dependency>            <!-- Spring AOP 动态代理 -->          <dependency>              <groupId>org.aspectj</groupId>              <artifactId>aspectjweaver</artifactId>              <version>1.8.9</version>          </dependency>  </code></pre>    <p>这上面我想说的是 AspectJ 这个东东, AspectJ 是最早、功能比较强大的 AOP 实现之一。</p>    <p>在 Java 领域,AspectJ 中的很多语法结构基本上已成为 AOP 领域的标准。</p>    <p>Spring 也有自己的 Spring-AOP,Spring-AOP 采用运行时生成代理类,底层可以选用 JDK 或者 CGLIB 动态代理。</p>    <p>通俗点,AspectJ 在编译时增强要切入的类,而 Spring-AOP 是在运行时通过代理类增强切入的类,效率和性能可想而知。</p>    <p>所以 Spring 在 2.0 的时候就已经开始支持 AspectJ ,现在到 4.X 的时代已经很完美的和 AspectJ  结合到一起。</p>    <p>Druid 出自阿里巴巴技术团队之手,个人认为是比较好的数据库连接池之一,尤其是监控部分是我的最爱。</p>    <p>项目中的 web.xml 配置监控配置和监控界面:</p>    <pre>  <code class="language-java">   <!--Druid 数据库连接池监控-->      <servlet>          <servlet-name>DruidStatView</servlet-name>          <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>      </servlet>      <servlet-mapping>          <servlet-name>DruidStatView</servlet-name>          <url-pattern>/druid/*</url-pattern>      </servlet-mapping>  </code></pre>    <p><img src="https://simg.open-open.com/show/5cce73681d410b4ae482ffe602e0c117.png"></p>    <p>JPA 作为 Sun 公司引入的 ORM 规范,就像是 JDBC 之于各种数据库驱动 Jar,</p>    <p>不要去在意使用了什么样的数据库,用 JDBC 提供的规范方法去撸代码即可。</p>    <p>JPA 制定持久层规范,相同与抽象接口,有 ORM 框架撸具体的实现层。</p>    <p>Sun 想实现 ORM 技术统一,可能不远的将来,你不用在纠结选择什么样子的 ORM 框架。</p>    <p>而现有热门的 ORM 框架会渐渐失去光泽,这毕竟是个漫长的过程,让我们拭目以待。</p>    <h3><strong>2. 方案整体一览</strong></h3>    <p>方案中所有的类都位于 SpringContext 中,由 Spring 统一进行管理。</p>    <p>让 Spring 统一管理的前提是你要告诉有这样一个类需要它管理,目前我接触到的告诉途径有两种。</p>    <p>传统的 xml 配置和注解方式,xml 配置和注解方式各有优劣,比如 xml 配置的优点:</p>    <p>a. 如果你公司项目在引用另外一个公司的 jar,这时候,唯一可行方式为 xml 配置。</p>    <p>b. 如果类之间的依赖关系变动频繁,xml 配置是比较优秀的,改动代码和改动配置文件,无论是技术上还是风险上,xml 都稳赢注解。</p>    <p>注解声明的方式优点:代码和声明在一起,开发的时候不用切来切去,比 xml 配置声明要简单明了的多。</p>    <p>现在很多主流的框架都引入了注解,但也无法摈弃 xml 配置声明的方式。</p>    <p> </p>    <p><img src="https://simg.open-open.com/show/f886ec6a44a8954ccb08cf1e3ae1a019.png"></p>    <p>在这个方案中我使用干净简单注解的方式,controller 包下使用注解@controller,dao-impl 包下使用@Repository,service 包下使用@service。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9cb31b39a6aa32a36985a718ff88be27.png"></p>    <p>控制层注入服务实例,服务层注入数据访问层对象,持久层对象由 JAP 进行注解,页面通过控制层来传输和获取数据。</p>    <p>web.xml:</p>    <pre>  <code class="language-java">  <?xml version="1.0" encoding="UTF-8"?>  <web-app xmlns="http://java.sun.com/xml/ns/javaee"           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee            http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"           version="3.0">      <!--Spring mvc -->      <servlet>          <servlet-name>springMVC</servlet-name>          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>          <init-param>              <param-name>contextConfigLocation</param-name>              <param-value>                  classpath:spring-mvc-base.xml                  classpath:spring-mvc-beans.xml                  classpath:spring-hibernate-beans.xml              </param-value>          </init-param>          <load-on-startup>1</load-on-startup>          <async-supported>true</async-supported>      </servlet>        <servlet-mapping>          <servlet-name>springMVC</servlet-name>          <url-pattern>/</url-pattern>      </servlet-mapping>        <!--Druid 数据库连接池监控-->      <servlet>          <servlet-name>DruidStatView</servlet-name>          <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>      </servlet>      <servlet-mapping>          <servlet-name>DruidStatView</servlet-name>          <url-pattern>/druid/*</url-pattern>      </servlet-mapping>  </web-app>    View Code</code></pre>    <p>maven pom.xml:</p>    <pre>  <code class="language-java">  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">      <modelVersion>4.0.0</modelVersion>      <groupId>com.rambo</groupId>      <artifactId>sdh</artifactId>      <packaging>war</packaging>      <version>1.0-SNAPSHOT</version>      <name>sdh</name>      <description>spring mvc + Druid + hibernate</description>        <dependencies>          <!--spring mvc-->          <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-webmvc</artifactId>              <version>4.3.3.RELEASE</version>          </dependency>            <!-- Spring-orm -->          <dependency>              <groupId>org.springframework</groupId>              <artifactId>spring-orm</artifactId>              <version>4.3.3.RELEASE</version>          </dependency>            <!-- Spring AOP 动态代理 -->          <dependency>              <groupId>org.aspectj</groupId>              <artifactId>aspectjweaver</artifactId>              <version>1.8.9</version>          </dependency>            <!-- Hibernate-core -->          <dependency>              <groupId>org.hibernate</groupId>              <artifactId>hibernate-core</artifactId>              <version>4.3.1.Final</version>          </dependency>            <!-- Druid -->          <dependency>              <groupId>com.alibaba</groupId>              <artifactId>druid</artifactId>              <version>1.0.26</version>          </dependency>            <!--mysql 数据驱动-->          <dependency>              <groupId>mysql</groupId>              <artifactId>mysql-connector-java</artifactId>              <version>5.1.38</version>          </dependency>            <!--日志-->          <dependency>              <groupId>log4j</groupId>              <artifactId>log4j</artifactId>              <version>1.2.17</version><!--2012.05 最后版本-->          </dependency>      </dependencies>      <build>          <finalName>sdh</finalName>          <plugins>              <plugin>                  <groupId>org.eclipse.jetty</groupId>                  <artifactId>jetty-maven-plugin</artifactId>                  <version>9.2.11.v20150529</version>                  <configuration>                      <jvmArgs>-XX:PermSize=256M -XX:MaxPermSize=1024M</jvmArgs>                      <webApp>                          <contextPath>/${project.name}</contextPath>                      </webApp>                      <httpConnector>                          <port>4040</port>                      </httpConnector>                      <stopKey>foo</stopKey>                      <stopPort>9998</stopPort>                  </configuration>              </plugin>          </plugins>      </build>  </project>    View Code</code></pre>    <h3><strong>3. DAO 层的种种设计思路</strong></h3>    <p>controller 和 service 层非常容易理解,就是不赘述了。</p>    <p>DAO 层中 BasePo 和 是希望将一些共有的属性抽象在父类当中(属性由具体项目需求决定)。</p>    <pre>  <code class="language-java">  @MappedSuperclass  public class BasePO implements Serializable {      @Id      @Column(length = 32, nullable = true)      @GeneratedValue(generator = "uuid")      @GenericGenerator(name = "uuid", strategy = "uuid")      private String uuid;        @Column(updatable = false)      private Date createDate;        private Date modifyDate;        ....getter/setter  }    BasePO</code></pre>    <p>BaseDaoImpl 希望将一些公共的数据访问方法实现在父类当中(我这里的方法可能有点少,可以由具体项目增加)。</p>    <pre>  <code class="language-java">  @Repository  public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {        private Class<T> entityClass;      @Resource      protected SessionFactory sessionFactory;        public BaseDaoImpl() {          this.entityClass = null;          Class<?> c = getClass();          Type type = c.getGenericSuperclass(); //反射获取超类          if (type instanceof ParameterizedType) {              Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();              this.entityClass = (Class<T>) parameterizedType[0];          }      }        protected Session getSession() {          return sessionFactory.getCurrentSession();      }        public T getByKey(PK id) {          Assert.notNull(id, "id is required");          return (T) getSession().get(entityClass, id);      }        public T add(T entity) {          Assert.notNull(entity, "entity is required");          getSession().save(entity);          return entity;      }        public T edit(T entity) {          Assert.notNull(entity, "entity is required");          getSession().update(entity);          return entity;      }        public T deleteByKey(PK id) {          Assert.notNull(id, "id is required");          T t = (T) getSession().load(entityClass, id);          getSession().delete(t);          return t;      }  }    BaseDaoImpl</code></pre>    <p>使用 JAP 注解编写业务使用到的持久层对象。</p>    <pre>  <code class="language-java">@Entity  @Table(name = "t_user")  public class User extends BasePO {      @Column(nullable = false)      String name;      @Column(nullable = false)      String pwd;        ....getter/setter  }  </code></pre>    <p>配置启动时扫描 POJO 的动作,至于是新建还是更新都有配置选项,可以自己查阅相关文档。</p>    <pre>  <code class="language-java">    <!-- 配置hibernate session工厂,需添加 spring-orm -->      <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">          <property name="dataSource" ref="dataSource"/>          <property name="hibernateProperties">              <props>                  <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>                  <prop key="hibernate.dialect">${hibernate.dialect}</prop>                  <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>                  <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>              </props>          </property>            <!-- 自动扫描注解方式配置的hibernate类文件 -->          <property name="packagesToScan">              <list>                  <value>com.rambo.sdh.pojo</value>              </list>          </property>      </bean>  </code></pre>    <p>操纵数据库最主要的事务管理,采用 AOP 声明方式,在执行含有数据变动的方法前后进行拦截。</p>    <p>采用 AOP 声明方式进行拦截的好处,不用去关注数据库事务的开启和关闭,将重心放到业务逻辑上面。</p>    <pre>  <code class="language-java"><!-- 配置事务管理器 -->      <bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">          <property name="sessionFactory" ref="sessionFactory"/>      </bean>        <!-- 声明式容器事务管理 ,transaction-manager指定事务管理器为transactionManager- -->      <tx:advice id="transactionAdvice" transaction-manager="transactionManager">          <tx:attributes>              <tx:method name="add*"/>              <tx:method name="save*"/>              <tx:method name="update*"/>              <tx:method name="modify*"/>              <tx:method name="edit*"/>              <tx:method name="delete*"/>              <tx:method name="remove*"/>              <tx:method name="repair"/>              <tx:method name="deleteAndRepair"/>                <tx:method name="get*" propagation="SUPPORTS"/>              <tx:method name="find*" propagation="SUPPORTS"/>              <tx:method name="load*" propagation="SUPPORTS"/>              <tx:method name="search*" propagation="SUPPORTS"/>              <tx:method name="datagrid*" propagation="SUPPORTS"/>              <tx:method name="*" propagation="SUPPORTS"/>          </tx:attributes>      </tx:advice>      <aop:config>          <aop:pointcut id="transactionPointcut" expression="execution(* com.rambo.sdh.service..*Impl.*(..))"/>          <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/>      </aop:config>  </code></pre>    <p>上面配置文件的大体意思是说,在包 com.rambo.sdh.service..*Impl.* 下所执行的已 add/save/update.....开头的方法。</p>    <p>方法在执行前后都会被 HibernateTransactionManager 拦截住,进行事务的开启和关闭。</p>    <p>当然还有一些其他的事情,有兴趣可以 debug 源码去一探究竟。</p>    <p>貌似说的也差不多了,该方案为 javaweb 后端解决方案,前端用你想用的渲染技术即可。</p>    <p> </p>    <p> </p>    <p>来自:http://www.cnblogs.com/java-class/p/5951738.html</p>    <p> </p>