最近手頭的工作不太繁重,自己試著倒騰了一套用開源框架組建的 JavaWeb 後端解決方案。 感覺還不錯的樣子,但實踐和項目實戰還是有很大的落差,這裡只做拋磚引玉之用。 項目 git 地址:https://git.oschina.net/LanboEx/sdh.git 大體採用的開源項目有:Sprin ...
最近手頭的工作不太繁重,自己試著倒騰了一套用開源框架組建的 JavaWeb 後端解決方案。
感覺還不錯的樣子,但實踐和項目實戰還是有很大的落差,這裡只做拋磚引玉之用。
項目 git 地址:https://git.oschina.net/LanboEx/sdh.git
大體採用的開源項目有:Spring + SpringMVC + Druid + JPA(Hibernate Impl)。
1. 採用到的開源項目漫談
Spring 迷人的依賴註入特性, 使其已經穩穩的占據在 JavaEE 項目引用開源項目列表中的上層位置。
秉承低耦合高內聚的遵旨, Spring 提倡的對象工廠解耦類關係的思想已深入到每個攻城獅的心中。
SpringMVC 做為 Spring 的乾兒子,最讓我沉醉的是她強大的擴展能力,深邃的像大海一樣。
前端無論是 freemarker/velocity/jsp...,後端 DAO 層無論是傳統的 ORM 還是新近上位的領域模型。
她的態度始終如一,給你360度最貼心的呵護,有一人對你如此,此生足矣。
官網地址:http://projects.spring.io/spring-framework/
項目中關於 SpringMVC + Spring 的依賴:
<!--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>
這上面我想說的是 AspectJ 這個東東, AspectJ 是最早、功能比較強大的 AOP 實現之一。
在 Java 領域,AspectJ 中的很多語法結構基本上已成為 AOP 領域的標準。
Spring 也有自己的 Spring-AOP,Spring-AOP 採用運行時生成代理類,底層可以選用 JDK 或者 CGLIB 動態代理。
通俗點,AspectJ 在編譯時增強要切入的類,而 Spring-AOP 是在運行時通過代理類增強切入的類,效率和性能可想而知。
所以 Spring 在 2.0 的時候就已經開始支持 AspectJ ,現在到 4.X 的時代已經很完美的和 AspectJ 結合到一起。
有興趣的可以在接著讀讀:https://www.oschina.net/translate/comparative_analysis_between_spring_aop_and_aspectj?cmp
Druid 出自阿裡巴巴技術團隊之手,個人認為是比較好的資料庫連接池之一,尤其是監控部分是我的最愛。
官方 github 地址:https://github.com/alibaba/druid/wiki/常見問題
項目中的 web.xml 配置監控配置和監控界面:
<!--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>
JPA 作為 Sun 公司引入的 ORM 規範,就像是 JDBC 之於各種資料庫驅動 Jar,
不要去在意使用了什麼樣的資料庫,用 JDBC 提供的規範方法去擼代碼即可。
JPA 制定持久層規範,相同與抽象介面,有 ORM 框架擼具體的實現層。
Sun 想實現 ORM 技術統一,可能不遠的將來,你不用在糾結選擇什麼樣子的 ORM 框架。
而現有熱門的 ORM 框架會漸漸失去光澤,這畢竟是個漫長的過程,讓我們拭目以待。
2. 方案整體一覽
方案中所有的類都位於 SpringContext 中,由 Spring 統一進行管理。
讓 Spring 統一管理的前提是你要告訴有這樣一個類需要它管理,目前我接觸到的告訴途徑有兩種。
傳統的 xml 配置和註解方式,xml 配置和註解方式各有優劣,比如 xml 配置的優點:
a. 如果你公司項目在引用另外一個公司的 jar,這時候,唯一可行方式為 xml 配置。
b. 如果類之間的依賴關係變動頻繁,xml 配置是比較優秀的,改動代碼和改動配置文件,無論是技術上還是風險上,xml 都穩贏註解。
註解聲明的方式優點:代碼和聲明在一起,開發的時候不用切來切去,比 xml 配置聲明要簡單明瞭的多。
現在很多主流的框架都引入了註解,但也無法擯棄 xml 配置聲明的方式。
在這個方案中我使用乾凈簡單註解的方式,controller 包下使用註解@controller,dao-impl 包下使用@Repository,service 包下使用@service。
控制層註入服務實例,服務層註入數據訪問層對象,持久層對象由 JAP 進行註解,頁面通過控制層來傳輸和獲取數據。
web.xml:
<?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
maven pom.xml:
<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
3. DAO 層的種種設計思路
controller 和 service 層非常容易理解,就是不贅述了。
DAO 層中 BasePo 和 是希望將一些共有的屬性抽象在父類當中(屬性由具體項目需求決定)。
@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
BaseDaoImpl 希望將一些公共的數據訪問方法實現在父類當中(我這裡的方法可能有點少,可以由具體項目增加)。
@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
使用 JAP 註解編寫業務使用到的持久層對象。
@Entity
@Table(name = "t_user")
public class User extends BasePO {
@Column(nullable = false)
String name;
@Column(nullable = false)
String pwd;
....getter/setter
}
配置啟動時掃描 POJO 的動作,至於是新建還是更新都有配置選項,可以自己查閱相關文檔。
<!-- 配置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>
操縱資料庫最主要的事務管理,採用 AOP 聲明方式,在執行含有數據變動的方法前後進行攔截。
採用 AOP 聲明方式進行攔截的好處,不用去關註資料庫事務的開啟和關閉,將重心放到業務邏輯上面。
<!-- 配置事務管理器 -->
<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>
上面配置文件的大體意思是說,在包 com.rambo.sdh.service..*Impl.* 下所執行的已 add/save/update.....開頭的方法。
方法在執行前後都會被 HibernateTransactionManager 攔截住,進行事務的開啟和關閉。
當然還有一些其他的事情,有興趣可以 debug 源碼去一探究竟。
貌似說的也差不多了,該方案為 javaweb 後端解決方案,前端用你想用的渲染技術即可。
項目開源 GIT 地址已在最上面給出,如果有興趣的可以檢出到本地跑一跑,該方案中小公司其實都挺適合,上手和開發速度快。