最近手頭的工作不太繁重,自己試著倒騰了一套用開源框架組建的 JavaWeb 後端解決方案。
感覺還不錯的樣子,但實踐和項目實戰還是有很大的落差,這裡只做拋磚引玉之用。
項目 git 地址:https://git.oschina.net/LanboEx/sdh.git
大體采用的開源項目有:Spring + SpringMVC + Druid + JPA(Hibernate Impl)。
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 框架會漸漸失去光澤,這畢竟是個漫長的過程,讓我們拭目以待。
方案中所有的類都位於 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 Codemaven 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 CodeController 和 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 } BasePOBaseDaoImpl 希望將一些公共的數據訪問方法實現在父類當中(我這裡的方法可能有點少,可以由具體項目增加)。
@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 地址已在最上面給出,如果有興趣的可以檢出到本地跑一跑,該方案中小公司其實都挺適合,上手和開發速度快。