项目中的一个AOP的编写案例(配置+案例)

来源:互联网 发布:贵阳大数据产业园区 编辑:程序博客网 时间:2024/05/02 10:18

AOP和Spring事务处理2008年05月01日 星期四 00:40二. AOP

1. AOP是什么?
  AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

2. 切面意义何在?
  就可以在这层切面上进行统一的集中式权限管理。而业务逻辑组件则无需关心权限方面的问题。也就是说,通过切面,我们可以将系统中各个不同层次上的问题隔离开来,实现统一集约式处理。各切面只需集中于自己领域内的逻辑实现。这一方面使得开发逻辑更加清晰,专业化分工更加易于进行;另一方面,由于切面的隔离,降低了耦合性,我们就可以在不同的应用中将各个切面组合使用,从而使得代码可重用性大大增强。
3. AOP应用范围

  Authentication 权限
  Caching 缓存
  Context passing 内容传递
  Error handling 错误处理
  Lazy loading 懒加载
  Debugging  调试
  logging, tracing, profiling and monitoring 记录跟踪 优化 校准
  Performance optimization 性能优化
  Persistence  持久化
  Resource pooling 资源池
  Synchronization 同步
  Transactions 事务

 

applicationContext.xml配置:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd     
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-3.0.xsd     
      http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop-3.0.xsd     
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
 <!-- setting annotation -->
 <context:annotation-config />
 <!-- auto proxy configuration -->
 <!-- 启动对@Aspect-->
 <aop:aspectj-autoproxy />

 <!--
  scan annotation for @Controller(action), @Service(service),
  @Repository(dao)
 -->
 <context:component-scan base-package="cn.com" />

 <!-- 属性文件读入 -->
 <bean id="propertyConfigurer"
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
   <list>
    <value>classpath:jdbc.properties</value>
   </list>
  </property>
 </bean>
 <!-- pls config the parameter for c3p0 setting -->
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
  destroy-method="close">
  <!-- c3p0 type -->
  <property name="driverClass" value="${jdbc.driverClass}" />
  <property name="jdbcUrl" value="${jdbc.jdbcUrl}" />
  <property name="user" value="${jdbc.user}" />
  <property name="password" value="${jdbc.password}" />
  <!-- 连接池中保留的最小连接数 -->
  <property name="minPoolSize" value="${c3p0.minPoolSize}" />
  <!--连接池中保留的最大连接数。Default: 15 -->
  <property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
  <!--初始化时获取的连接数,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->
  <property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
  <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
  <property name="maxIdleTime" value="${c3p0.maxIdleTime}" />
  <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->
  <property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
  <!--
   JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements
   属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
   如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0
  -->
  <property name="maxStatements" value="${c3p0.maxStatements}" />
  <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->
  <property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}" />
  <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
  <property name="acquireRetryAttempts" value="${c3p0.acquireRetryAttempts}" />

  <!--
   获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
   保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试
   获取连接失败后该数据源将申明已断开并永久关闭。Default: false
  -->
  <property name="breakAfterAcquireFailure" value="${c3p0.breakAfterAcquireFailure}" />
  <!--
   因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
   时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
   等方法来提升连接测试的性能。Default: false
  -->
  <property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}" />
 </bean>
 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource">
  </property>
  <property name="hibernateProperties">
   <props>
    <!--
     <prop
     key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    -->
    <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
    <prop key="hibernate.hbm2ddl.auto">update</prop>
    <prop key="javax.persistence.validation.mode">none</prop>
    <!-- default 10 -->
    <prop key="hibernate.jdbc.fetch_size">50</prop>
    <prop key="hibernate.jdbc.batch_size">30</prop>
    <prop key="hibernate.connection.autocommit">true</prop>
    <!--
     ON_CLOSE:Hibernate session在第一次需要进行JDBC操作的时候获取连接,然后
     持有他,直到session关闭。不鼓励使用
     AFTER_TRANSACTION:在org.hibernate.Transaction结束后释放连接。这一个不应该在JTA环境下使用。
     AFTER_STATEMENT:(也被称做积极释放),在每一条语句被执行后就释放连接。
    -->
    <prop key="hibernate.connection.release_mode">after_transaction</prop>
   </props>
  </property>
  <property name="packagesToScan">
   <list>
    <value>cn.com.css.misps.domain</value>
   </list>
  </property>
 </bean>

 <!-- Transaction configuration-->
 <bean id="txManager"
  class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
 </bean>
 <!-- auto transaction manager configuration -->
 <tx:annotation-driven transaction-manager="txManager" />
 
 <!-- 引入web Service的定义xml -->
 <import resource="graph.xml" />
 
</beans>

这里是要做aop的一个类:

package cn.com.css.misps.graph.service.impl;

import java.util.Date;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.css.misps.domain.TCoGraph;
import cn.com.css.misps.graph.dao.ITCoGraphDao;
import cn.com.css.misps.graph.dao.ITProductLDao;
import cn.com.css.misps.graph.dao.ITTaskStateDao;
import cn.com.css.misps.graph.dao.impl.TCoGraphDaoImpl;
import cn.com.css.misps.graph.service.ITCoGraphService;
import cn.com.css.misps.graph.util.ProductLUtils;

/**
 * @brief ITCoGraphServiceImpl.java 操作流程配置的接口
 * @attention
 * @author 涂作权
 * @date 2013-9-23
 * @note begin modify by null
 */
@Transactional
@Service("iTCoGraphService")
public class TCoGraphServiceImpl extends TCoGraphDaoImpl implements ITCoGraphService {
 @Resource
 private ITCoGraphDao tCoGraphDao;
 @Resource(name = "iTTaskStateDao")
 private ITTaskStateDao taskStateDao;
 @Resource(name = "iTProductLDao")
 private ITProductLDao productLDao;
 
 /**
  * \brief 获得图形制作的参数
  *
  * @return
  * @attention 通过
  * @author 涂作权
  * @date 2013-9-23
  * @note begin modify by null
  */
 public Object[] getServiceParametersByAccountIdAndDataType(Long accountId,String code) {
  TCoGraph tCoGraph = tCoGraphDao.getConfigByCondition(accountId, code);
  String[] parameters = tCoGraph.getCserviceParameters()
    .split("##");
  
  String[] serviceParameters = new String[parameters.length + 1];
  
  //将服务参数保存到数组中
  for (int i = 0; i < parameters.length; i++) {
   serviceParameters[i] = parameters[i].split("=")[1];
  }

  // 通过这个serviceId来判断到底是使用哪个服务
  String serviceId = tCoGraph.getVserviceId().toString();
  serviceParameters[parameters.length] = serviceId;

  //这个地方表示获取数据(包括MICAPS日志数据和服务配置数据)完成,服务参数为20
  new ProductLUtils().saveOrUpdateTaskStateLAndProductL(
    taskStateDao, //操作任务表的Dao
    productLDao,  //操作产品日志库的Dao
    accountId,    //用户账号
    code,         //产品CODE
    "20",         //状态标识
    new Date(),   //最后制作时间
    "GRAPH",      //产品类别
    "数据获取完成" //操作类型
   );
  return serviceParameters;
 }

}
对上面的一个里做AOP操作

package cn.com.css.misps.graph.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * @brief GraphAop.java 这是出图过程中的切面
 * @attention
 * @author 涂作权
 * @date 2013-9-21
 * @note begin modify by null
 */
@Aspect
@Component(value = "generateGraphAop")
@SuppressWarnings("unused")
public class GraphAop {

 /**
  * \brief 将micaps发布日志中信息进行拼装整合的过程
  *
  * @attention
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
 @Pointcut(value = "execution(* cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl.getServiceParametersByAccountIdAndDataType(long,String))&&args(accountId,code)", argNames = "accountId,code")
 private void micapsPathAndDatatypePointcut(Long accountId, String code) {
  System.out.println("操作日志表");
 }

 /**
  * \brief 只有发现有发布日志的时候才会接着执行下面的代码,否则不执行。所以这里就是就是数据获取的部分,
  * 并且是在服务参数也获取到了之后才算是数据获取阶段
  *
  * @param accountId
  *            :表示最后配置的系统管理员角色的用户的用户id
  * @param code
  *            :这里代表的时候是micaps日志文件中用来区分产品标识,这里希望是类似11节的产品CODE
  * @attention 这里的CODE代表的意义可能在MICAPS日志规范定下来了之后会变
  * @author 涂作权
  * @date 2013-9-23
  * @note begin modify by null
  */
 @AfterReturning(value = "micapsPathAndDatatypePointcut(accountId,code)", argNames = "accountId,code")
 private void serviceParametersAdvice(Long accountId, String code) {
  System.out.println("数据获取中");
 }

 // /**
 // * \brief 获取图形流程配置表中的数据信息
 // *
 // * @attention 这里面的参数可变
 // * @author 涂作权
 // * @date 2013-9-22
 // * @note begin modify by null
 // *
 // @Pointcut("execution(* cn.com.css.misps.graph.dao.impl.TCoGraphDaoImpl.getConfigByCondition(..))")
 // private void tCoGraphPointcut() {
 // }
 //
 // /**
 // * \brief 图形生成的过程
 // *
 // * @attention
 // * @author 涂作权
 // * @date 2013-9-22
 // * @note begin modify by null
 // */
 // @Pointcut("execution(* cn.com.css.misps.graph.service.impl.GraphServiceImpl.generateGraphs(..))")
 // private void generateGraphsPointcut() {
 // }
 //
 // /**
 // * \brief 生成获取micaps文件的路径和数据类型的日志
 // *
 // * @attention 将micaps文件的路径和数据类型保存到数据库中
 // * @author 涂作权
 // * @date 2013-9-22
 // * @note begin modify by null
 // */
 // @Before("micapsPathAndDatatypePointcut()")
 // public void micapsPathAndDatatypeAdvice() {
 // System.out.println("获取micaps文件路径和数据类型的拼接字符串......");
 // }
 //
 // /**
 // * \brief 生成图形前做日志
 // *
 // * @attention 将日志信息保存到制作图形产品的日志表中
 // * @author 涂作权
 // * @date 2013-9-22
 // * @note begin modify by null
 // */
 // @Before("generateGraphsPointcut()")
 // public void generateGraphsAdvice() {
 // System.out.println("生成图形产品.....");
 // }
}

这里是另外一个AOP的写法:

package cn.com.css.misps.graph.aop;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.tempuri.IMPService;

/**
 * @brief GraphUtilsAop.java 这个图形制作工具类的切面
 * @attention
 * @author 涂作权
 * @date 2013-9-26
 * @note begin modify by null
 */
@SuppressWarnings("unused")
@Aspect
@Component("GraphUtilsAop")
public class GraphUtilsAop {

 /**
  * \brief 图形制作的切入点
  *
  * @attention 这里是图形制作切入点
  * @author 涂作权
  * @date 2013-9-26
  * @note begin modify by null
  */
 @Pointcut(value = "execution(* cn.com.css.misps.graph.util.GraphUtils.getGraphBytesByMpServiceFile(Long,String))&&args(accountId,code)",
   argNames = "accountId,code")
 private void getGraphBytesByMpServiceFilePointcut(Long accountId,
   String code) {
 }

 @Before("getGraphBytesByMpServiceFilePointcut(accountId,code)")
 private void getGraphBytesByMpServiceFileAdvice(Long accountId,
   String code) {
  System.out.println("accountId = " + accountId);
  System.out.println("code = " + code);
 }
}

这里是另外一个AOP的写法:

 

 

package cn.com.css.misps.graph.aop;

import javax.annotation.Resource;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import cn.com.css.misps.domain.TProductL;
import cn.com.css.misps.graph.dao.ITMicapsLDao;
import cn.com.css.misps.graph.dao.ITProductLDao;

/**
 * @brief MicapsAOP.java 这个类是关于MICAPS日志相关的aop
 * @attention
 * @author 涂作权
 * @date 2013-9-22
 * @note begin modify by null
 */
@Aspect
@Component(value = "micapsAOP")
@SuppressWarnings("unused")
public class MicapsAOP {
 @Resource(name = "iTProductLDao")
 private ITProductLDao productLDao; 
 @Resource(name = "iTMicapsLDao")
 private ITMicapsLDao tMicapsLDao;

 /**
  * \brief 查找状态是"发布"的,并且扫描状态为0的MICAPS日志,
  * 0表示未扫描过的,也就是说是新产生的MICAPS制作日志文件
  *
  * @attention 扫描MICAPS制作日志文件
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
 @Pointcut(value = "execution(* cn.com.css.misps.graph.dao.impl.TMicapsLDaoImpl.findTMicapsLByState(String,String))&&args(state,ifScanned)", argNames = "state,ifScanned")
 private void dataSourcePointcut(String state, String ifScanned) {}

 /**
  * \brief 说明一直在进行
  *
  * @attention 注意这里的参数是cn.com.css.misps.graph.dao.impl.TMicapsLDaoImpl.
  *            findTMicapsLByState(..)方法中的参数
  *            还可以直接通过:@Before("dataSourcePointcut()&&args(state,ifScanned)"
  *            )方式, 在上面的切入点部分直接写execution表达式,也就是: execution(*
  *            cn.com.css.misps
  *            .graph.dao.impl.TMicapsLDaoImpl.findTMicapsLByState(..))<br/>
  *
  *            这里不用再写:args(state,ifScanned)
  *
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
// @Before(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
// public void dataSourceBeforeAdvice(String state, String ifScanned) {
//  System.out.println("state=======" + state);
//  System.out.println("ifScanned===" + ifScanned);
//  System.out.println("按照条件查找micaps日志的dao.impl....");
// }

 /**
  * \brief 这里是后置通知
  *
  * @param state
  *            标识状态的参数
  * @param ifScanned
  *            表示是否扫描过,如果为0表示扫描过,如果为1表示扫描过
  * @attention 通过后置通知将查询过T_MICAPS_L表中的ifScanned参数变为1,表示扫描过
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
 @AfterReturning(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
 public void dataSourceAfterReturningAdvice(String state, String ifScanned) {
  //更新状态
  tMicapsLDao.updateFieldOfIfScanned();
 }

 /**
  * \brief 这里是一个最终通知
  *
  * @param state
  *            标识状态的参数
  * @param ifScanned
  *            表示是否扫描过,如果为0表示扫描过,如果为1表示扫描过
  * @attention
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
// @After(value = "dataSourcePointcut(state,ifScanned)", argNames = "state,ifScanned")
// public void dataSourceAfterAdvice(String state, String ifScanned) {
//  System.out.println("这里是MICAPS相关的最终通知,暂时不做任何动作..............");
// }

 /**
  * \brief 这里是一个异常通知
  *
  * @attention 如果调用中出现了异常,则执行这一句
  * @author 涂作权
  * @date 2013-9-22
  * @note begin modify by null
  */
// @AfterThrowing(value = "dataSourcePointcut(state,ifScanned)", throwing = "ex")
// public void dataSourceThrowMethod(String state, String ifScanned,
//   Throwable ex) {
//  System.out.println(ex.getMessage());
//  System.out.println("如果在查询MICAPS日志信息的时候出现了异常,那么将执行这一句......");
// }
}

这里是另外一个AOP的写法:

package cn.com.css.misps.graph.aop;

import java.util.Date;

import javax.annotation.Resource;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import cn.com.css.misps.graph.dao.ITProductLDao;
import cn.com.css.misps.graph.dao.ITTaskStateDao;
import cn.com.css.misps.graph.util.ProductLUtils;

/**
 * @brief TCoGraphAOP.java 类或者接口的简要说明
 * @attention 如果没有找到这个CODE对应的产品提示:这种产品没有配置, 或者没有对产品进行管理,请管理员添加图形产品的配置
 *
 * @author 图形产品相关的日志信息
 * @date 2013-9-25
 * @note begin modify by null
 */
@Aspect
@Component("tCoGraphAOP")
public class TCoGraphAOP {
 @Resource(name = "iTTaskStateDao")
 private ITTaskStateDao taskStateDao;
 @Resource(name = "iTProductLDao")
 private ITProductLDao productLDao;
 
 @SuppressWarnings("unused")
 @Pointcut(value = "execution(* cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl.getServiceParametersByAccountIdAndDataType(*,Long,String))" +
   "&&args(accountId,code)",
   argNames = "accountId,code")
 private void queryGraphConfig(Long accountId, String code) {
 }

 /**
  * \brief 获取流程配置参数方法的前置通知,这个时候一定发现了MICAPS日志发布信息
  *
  * @param accountId
  * @param code
  * @attention 如果在BaseInfo表中没有找到这种产品,做出提示,告诉系统管理员进行图形产品配置
  * @author 涂作权
  * @date 2013-9-25
  * @note begin modify by null
  */
 @AfterReturning(value = "queryGraphConfig(accountId,code)",
   argNames = "accountId,code")
 public void queryGraphConfigAdvice(Long accountId, String code) {
  new ProductLUtils().saveOrUpdateTaskStateLAndProductL(
   taskStateDao, //操作任务表的Dao
   productLDao,  //操作产品日志库的Dao
   accountId,    //用户账号
   code,         //产品CODE
   "20",         //状态标识
   new Date(),   //最后制作时间
   "GRAPH",      //产品类别
   "数据获取完成" //操作类型
  );
 }
}
AOP的单元测试类:

package cn.com.css.misps.graph.aop;

import java.io.File;
import java.util.List;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.com.css.misps.domain.TMicapsL;
import cn.com.css.misps.graph.dao.ITMicapsLDao;
import cn.com.css.misps.graph.service.impl.TCoGraphServiceImpl;

/**
 * @brief PersonProxyTest.java 类或者接口的简要说明
 * @attention 使用注意事项
 * @author Administrator
 * @date 2013-9-22
 * @note begin modify by 修改人 修改时间 修改内容摘要说明
 */
public class ProxyTest {
 @Test
 public void test() {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
    "applicationContext.xml");
  PersonDao personDao = (PersonDao) applicationContext
    .getBean("personDao");
  personDao.deletePerson();
 }

 @Test
 public void test1() {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  "applicationContext.xml");
  TCoGraphServiceImpl coGraphService = (TCoGraphServiceImpl) applicationContext
  .getBean("iTCoGraphService");
  
  coGraphService.getServiceParametersByAccountIdAndDataType(1L, "SEVP_PWSC_SJCF_PRO_EQ2_TRD_L88_PB_001_01_PNG");
 }
 
 
 @Test
 public void test2() {
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
    "applicationContext.xml");
  ITMicapsLDao tMicapsLDao = (ITMicapsLDao) applicationContext
    .getBean("iTMicapsLDao");

  List<TMicapsL> tMicapsLByStateList = tMicapsLDao
    .findTMicapsLByState("发布","0");
  if (tMicapsLByStateList != null) {
   for (int i = 0; i < tMicapsLByStateList.size(); i++) {
    TMicapsL tMicapsL = tMicapsLByStateList.get(i);
    System.out.println(tMicapsL.getCoutputfolder() + File.separator
      + tMicapsL.getCoutputdata() + "##"
      + tMicapsL.getCcode());
   }
  } else {
   return;
  }
 }

}