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(新))讨论,谢谢。