Spring框架研究總結之AOP(二)
Spring是一個開源的企業應用框架,是一個分層架構,由7個定義穩定的模塊所組成,而所有的模塊都是在覈心容器基礎上搭建;其體系本身提供了強大的IOC(控制反轉)、AOP(面向切面)以及DI(依賴注入)等較顯著的功能,那麼下面就來詳細介紹下Spring框架的模塊組成及相關特點。
l AspectJ使用
l AOP的例子
一、AspectJ使用
AspectJ是一個註解插件包,它可以與Spring很好的結合,並能輕鬆靈活的實現Spring AOP的功能。默認下,Spring並未支持AspectJ註解,我們需要顯示的將其開啓,另外,我們還需要aspectjrt和aspectjweaver兩個依賴包。下面以實現上一篇文章的攔截通知爲例進行說明它的使用。
1、項目結構
2、Bean準備
CartBo接口:
public interface CartBo {
void addProduct();
String addProductReturnValue();
void addProductThrowException() throwsException;
void addProductAround(String name);
}
CartBoImpl類:
public class CartBoImpl implements CartBo {
@Override
public void addProduct() {
System.out.println("addProduct()is now running...");
}
@Override
public String addProductReturnValue() {
System.out.println("addProductReturnValue()is now running...");
return "this is a test forusing aspectJ";
}
@Override
public voidaddProductThrowException() throws Exception {
System.out.println("addProductThrowException()is now running...");
throw newException("Error is begining!");
}
@Override
public voidaddProductAround(String name) {
System.out.println("addProductAround()is now running..."+",args:"+name);
}
}
3、啓用AspectJ
在Spring AOP中,要啓用AspectJ註解的支持,我們必須做兩件事兒,一件是導入必須的jar,這裏爲aspectjrt和aspectjweaver兩個依賴包;另一件是在applicationContext.xml配置中,添加如下內容:
<!--啓用AspectJ註解支持 -->
<aop:aspectj-autoproxy />
4、如何使用
若要使用AspectJ,除了開啓AspectJ的支持、導入必須的依賴包以及相關的Bean之外,我們還要準備的工作如下:
完整的XML配置:
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<!-- 啓用AspectJ註解支持 -->
<aop:aspectj-autoproxy />
<!-- 定義目標對象的Bean -->
<bean id="cartBo" class="com.spring.aop.impls.CartBoImpl"/>
<!-- 定義Aspect攔截器 -->
<bean id="cartAspect"class="com.spring.aop.aspect.CartAspect" />
</beans>
A、@Before:
Aspect攔截器:
@Aspect
public class CartAspect {
@Before("execution(*com.spring.aop.impls.CartBo.addProduct(..))")
public void cartBefore() {
System.out.println("aspectmethod of cartBefore is running!");
}
}
如何驗證:
CartBocartBo = (CartBo) context.getBean("cartBo");
cartBo.addProduct();
執行結果:
B、@After:
Aspect攔截器:
@Aspect
public class CartAspect {
@After("execution(*com.spring.aop.impls.CartBo.addProduct(..))")
public void cartAfter() {
System.out.println("aspectmethod of cartAfter is running!");
}}
如何驗證:
CartBocartBo = (CartBo) context.getBean("cartBo");
cartBo.addProduct();
執行結果:
C、@AfterReturning:
Aspect攔截器:
@Aspect
public class CartAspect {
@AfterReturning(pointcut="execution(*com.spring.aop.impls.CartBo.addProductReturnValue(..))",returning="result")
public voidcartAfterReturning() {
System.out.println("aspectmethod of cartAfterReturning is running!");
}
}
如何驗證:
CartBocartBo = (CartBo) context.getBean("cartBo");
cartBo.addProductReturnValue();
執行結果:
D、@AfterThrowing:
Aspect攔截器:
@Aspect
public class CartAspect {
@AfterThrowing(pointcut="execution(*com.spring.aop.impls.CartBo.addProductThrowException(..))",throwing="error")
public voidcartAfterThrowing() {
System.out.println("aspectmethod of cartAfterThrowing is running!");
}}
如何驗證:
try {
CartBo cartBo = (CartBo) context.getBean("cartBo");
cartBo.addProductThrowException();
} catch (Exception e) {
e.printStackTrace();
}
執行結果:
E、@Around:
Aspect攔截器:
@Aspect
public class CartAspect {
@Around("execution(*com.spring.aop.impls.CartBo.addProductAround(..))")
public voidcartAround(ProceedingJoinPoint pjp) throws Throwable {
// 通過pjp對象獲取Signature對象,該對象封裝了連接點的信息。
// 比如通過getDeclaringType獲取連接點所在類的class對象
System.out.println("aspectmethod of cartAround is running");
System.out.println("aspectmethod of name:"+pjp.getSignature().getName());
System.out.println("aspectmethod of argument:"+Arrays.toString(pjp.getArgs()));
System.out.println("Aroundbefore is running!");
// 繼續執行攔截動作
pjp.proceed();
System.out.println("Aroundafter is running!");
}}
如何驗證:
CartBocartBo = (CartBo) context.getBean("cartBo");
cartBo.addProductAround("MacBook Pro");
執行結果:
二、AOP的例子
這裏就以通過SpringAOP來實現Hibernate的事務操作,目的只爲保證數據庫的數據操作的完整性和一致性。好了,這裏新建一個Java項目,並導入需要的SpringAOP及Hibernate依賴包即可,項目結構圖如下:
1、數據源屬性文件
既然使用了Hibernate來管理與數據的通信,那麼少不了數據庫源的配置,在這裏,我們以properties(database.properties)文件來存儲數據源的信息,具體內容如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=
這個配置較簡單,這裏不做贅述。
2、數據源初始化
上面已經新建一個數據源配置屬性文件,該文件僅僅是存儲信息的獨立文件,並不能自動加載和初始化,所以需要使用PropertyPlaceholderConfigurer 來加載屬性文件,同時使用DriverManagerDataSource 來初始數據源連接信息(DataSource.xml),後續就可以使Hibernate訪問和操作數據庫了,內容如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 加載數據源配置文件 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>properties/database.properties</value>
</property>
</bean>
<!-- 初始化數據源的連接 -->
<bean id="dataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"value="${jdbc.driverClassName}" />
<property name="url"value="${jdbc.url}" />
<property name="username"value="${jdbc.username}" />
<property name="password"value="${jdbc.password}" />
</bean>
</beans>
3、Hibernate引用數據源
數據源被初始化之後,我們需要使Hibernate能夠引用和控制jdbc數據源(Hibernate.xml),當然,這裏不單單隻實現數據源實例的引用,還包括指定數據庫的類型以及初始化對象到數據表的ORM映射實現,因爲我們知道Hibernate管理與數據庫的交互,一個最爲重要的概念就是ORM映射模型,該模型能夠自動檢索和匹配指定的類對象實例到數據庫的表的初始化映射,實力強大吧!具體內容如下:
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 配置Hibernate的Session工廠 -->
<bean id="sessionFactory"class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- 引用數據源實例 -->
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<!-- 設置數據庫類型及sql日誌 -->
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<!-- 對象到數據表的映射(ORM) -->
<!-- ... -->
</bean>
</beans>
類的對象映射到數據表部分,在下面繼續介紹。
4、Hibernate的ORM映射(數據表)
product表:
CREATETABLE `test`.`product` (
`PRODUCT_ID` bigint(20) unsigned NOT NULLAUTO_INCREMENT,
`PRODUCT_CODE` varchar(20) NOT NULL,
`PRODUCT_DESC` varchar(255) NOT NULL,
PRIMARY KEY (`PRODUCT_ID`) USING BTREE
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
product_qoh表:
CREATETABLE `test`.`product_qoh` (
`QOH_ID` bigint(20) unsigned NOT NULLAUTO_INCREMENT,
`PRODUCT_ID` bigint(20) unsigned NOT NULL,
`QTY` int(10) unsigned NOT NULL,
PRIMARY KEY (`QOH_ID`),
KEY `FK_product_qoh_product_id`(`PRODUCT_ID`),
CONSTRAINT `FK_product_qoh_product_id`FOREIGN KEY (`PRODUCT_ID`)
REFERENCES `product` (`PRODUCT_ID`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
5、Hibernate的ORM映射(類對象)
Product類:
public class Product implements java.io.Serializable {
private long productId;
private String productCode;
private String productDesc;
public Product(){
}
public Product(StringproductCode,String productDesc) {
this.productCode = productCode;
this.productDesc = productDesc;
}
public long getProductId() {
return productId;
}
public void setProductId(longproductId) {
this.productId = productId;
}
public String getProductCode() {
return productCode;
}
public voidsetProductCode(String productCode) {
this.productCode = productCode;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(StringproductDesc) {
this.productDesc = productDesc;
}
}
ProductQoh類:
public class ProductQoh implements java.io.Serializable {
private long qohId;
private long productId;
private int qty;
public ProductQoh() {
}
public ProductQoh(longproductId,int qty) {
this.productId = productId;
this.qty = qty;
}
public long getQohId() {
return qohId;
}
public void setQohId(longqohId) {
this.qohId = qohId;
}
public long getProductId() {
return productId;
}
public void setProductId(longproductId) {
this.productId = productId;
}
public int getQty() {
return qty;
}
public void setQty(intqty) {
this.qty = qty;
}
}
6、Hibernate的ORM映射配置
Product.hbm.xml:
<!--ORM Mapping 關係映射 -->
<hibernate-mapping>
<class name="com.spring.product.model.Product"table="product" catalog="test">
<id name="productId" type="java.lang.long">
<column name="PRODUCT_ID"/>
<generator class="identity"/>
</id>
<property name="productCode"type="string">
<column name="PRODUCT_CODE"length="20" not-null="true" />
</property>
<property name="productDesc"type="string">
<column name="PRODUCT_DESC"not-null="true" />
</property>
</class>
</hibernate-mapping>
ProductQoh.hbm.xml:
<!--ORM Mapping 關係映射 -->
<hibernate-mapping>
<class name="com.spring.product.model.ProductQoh"table="product_qoh" catalog="test">
<id name="qohId" type="java.lang.Long">
<column name="QOH_ID"/>
<generator class="identity"/>
</id>
<property name="productId"type="long">
<column name="PRODUCT_ID"not-null="true" />
</property>
<property name="qty"type="int">
<column name="QTY"not-null="true" />
</property>
</class>
</hibernate-mapping>
注意:
還記得上面第4部分中的對象到表的映射嗎?現在可以爲其配置內容了,內容如下:
<!--對象到數據表的映射(ORM) -->
<property name="mappingResources">
<list>
<value>/hibernate/Product.hbm.xml</value>
<value>/hibernate/ProductQoh.hbm.xml</value>
</list>
</property>
7、Spring的事務配置
這裏主要做兩件事兒,一件是配置Spring的事務攔截器支持,使其能夠攔截hibernate的事務動作;另一件是Spring需要引用已經被實例化的dataSource和sessionFactory,做到徹底的與Hibernate的通信工作,內容如下所示:
<!--事務攔截配置-->
<bean id="transactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="save">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 事務管理配置 -->
<bean id="transactionManager"class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource"ref="dataSource" />
<property name="sessionFactory"ref="sessionFactory" />
</bean>
8、Dao層接口封裝
Dao層是Spring AOP直接與Hibernate接觸的,通過它直接調用hibernate的數據操作Api,即可間接與數據庫通信,也達到低耦合高拓展的作用。
ProductDao:
public interface ProductDao {
void save(Product product);
}
ProductQohDao:
public interface ProductQohDao {
void save(ProductQoh productQoh);
}
ProductDaoImpl:
public class ProductDaoImpl extends HibernateDaoSupport implementsProductDao {
@Override
public void save(Product product){
getHibernateTemplate().save(product);
}
}
ProductQohDaoImpl:
public class ProductQohDaoImpl extends HibernateDaoSupport implementsProductQohDao {
@Override
public void save(ProductQoh productQoh){
getHibernateTemplate().save(productQoh);
}
}
9、業務層接口封裝
ProductBo:
public interface ProductBo {
void save(Product product,int qoh);
}
ProductQohBo:
public interface ProductQohBo {
void save(ProductQoh productQoh);
}
ProductBoImpl:
public class ProductBoImpl implements ProductBo {
ProductDao productDao;
ProductQohBo productQohBo;
@Override
public void save(Product product,int qoh) {
productDao.save(product);
System.out.println("DaoProduct Intered");
ProductQoh productQoh = newProductQoh();
productQoh.setProductId(product.getProductId());
productQoh.setQty(qoh);
productQohBo.save(productQoh);
System.out.println("BoProduct Intered");
}
public voidsetProductDao(ProductDao productDao) {
this.productDao = productDao;
}
public voidsetProductQohBo(ProductQohBo productQohBo) {
this.productQohBo = productQohBo;
}
}
ProductQohBoImpl:
public class ProductQohBoImpl implements ProductQohBo {
ProductQohDao productQohDao;
@Override
public void save(ProductQoh productQoh){
productQohDao.save(productQoh);
System.out.println("DaoProductQoh Inserted");
}
public voidsetProductQohDao(ProductQohDao productQohDao) {
this.productQohDao = productQohDao;
}
}
10、業務及Dao層實例化
正如上面已經封裝好業務層和Dao層的操作,那麼接下來就是初始化它們,並使用代理工廠ProxyFactoryBean實現AOP的目標對象和設置攔截器,這裏的攔截爲事務攔截器,具體操作如下:
Product.xml:
<!--業務操作對象Bean-->
<bean id="productBo" class="com.spring.product.bo.impl.ProductBoImpl">
<property name="productDao"ref="productDao" />
<property name="productQohBo"ref="productQohBo" />
</bean>
<!-- Dao層對象Bean -->
<bean id="productDao" class="com.spring.product.dao.impl.ProductDaoImpl">
<property name="sessionFactory"ref="sessionFactory" />
</bean>
<!-- 目標對象及攔截器 -->
<bean id="productBoProxy"class="org.springframework.aop.framework.ProxyFactoryBean" >
<property name="target"ref="productBo" />
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
ProductQoh.xml:
<!--業務操作對象Bean-->
<bean id="productQohBo" class="com.spring.product.bo.impl.ProductQohBoImpl">
<property name="productQohDao"ref="productQohDao" />
</bean>
<!-- Dao層對象Bean -->
<bean id="productQohDao"class="com.spring.product.dao.impl.ProductQohDaoImpl" >
<property name="sessionFactory"ref="sessionFactory" />
</bean>
11、全部Beans初始化
這裏是用來初始化上面的數據庫配置以及相關的Beans的初始化工作,具體如下內容:
<!--數據庫配置-->
<import resource="../database/DataSource.xml"/>
<import resource="../database/Hibernate.xml"/>
<!-- Beans聲明 -->
<import resource="../beans/Transaction.xml"/>
<import resource="../beans/Product.xml"/>
<import resource="../beans/ProductQoh.xml"/>
12、如何驗證使用
代碼如下:
public class App {
private staticApplicationContext appContext;
public static voidmain(String[] args) {
// 加載相關的Bean配置到IOC容器,完成初始化實例
appContext = newClassPathXmlApplicationContext("spring/config/BeanLocation.xml");
// 新建一個產品Product,進行測試AOP的事務
Product product = new Product();
product.setProductCode("PDT0001");
product.setProductDesc("This is thetesting of Code PDT0001!");
// 調用業務邏輯,由其調用Dao層及事務
ProductBo productBo = (ProductBo) appContext.getBean("productBoProxy");
productBo.save(product, 1000);
}
}
執行結果:
13、需要的依賴包
Spring框架之AOP(二)就介紹到這裏,由於作者水平有限,如有問題請在評論發言或是QQ羣(245389109(新))討論,謝謝。