前面的注解之注解可以看到注解是标记,既然是标记那么就可以用来区分信息的
区分信息之后我们的动作更加丰富,我们可以使用注解来标记类,这些类就是我们的切点切出来的面上的元素.
本节内容呢我们使用注解来完成切点功能,然后使用aop计算凡是被标记的方法的执行时间
准备知识
aop基本知识回顾
1.aop的知识 ,切点,advice ,切点和advice的绑定即 advisor
这部分知识大家都不会陌生的
之前我们做项目总在xml中进行配置的
典型的写法
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.xxx.service.impl.*ServiceImpl.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
这是一个典型的配置方式,我们定义的切点使用了execution表达式完成
然后再定义一个advice ,这里我们仍然以典型的事务管理的advice来看
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有可写的方法都加入事务管理 -->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="sync*" propagation="REQUIRED"/>
<tx:method name="execute*" propagation="REQUIRED"/>
<!-- 其它方法为只读事务 -->
<tx:method name="*" read-only="true" />
<!--
事务传播行为类型
REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
-->
</tx:attributes>
</tx:advice>
2.后端java涉及的类
JoinPoint , ProceedingJoinPoint , MethodInterceptor , MethodInvocation , RunTimeAnno , MethodSignature
@Around , @Pointcut @Aspect ...
下面我们看如何使用注解来完成一个计算 方法的执行时间的aop配置
一 定义一个注解的类用于标记 TimeSpend
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TimeSpend {
String value() default "";
}
@Aspect
@Component
public class TimeSpendAspect {
@Pointcut("execution(* cn.XXXX..*.*(..))")
public void pointcut() {
}
@Around(value = "@annotation(cn.creditease.zma.trade.common.annotation.TimeSpend)")
public Object timeSpend(ProceedingJoinPoint jp) throws Throwable {
Logger logger = LoggerFactory.getLogger(this.getClass(jp));
Long begin = new Date().getTime();
String className = this.getClassName(jp);
String methodName = this.getMethodName(jp);
String output = "----------调用【" + className + "】类的【" + methodName + "】方法,";
String annoValue = getAnnoValue(jp);
if (annoValue != null) {
output = "----------" + annoValue + ",";
}
Object result = jp.proceed();
Long end = new Date().getTime();
Long spendTime = (end - begin) / Constants.MILLON_INT;
logger.info(output + "耗时:" + spendTime + "s----------");
return result;
}
public Class getClass(JoinPoint jp) {
return jp.getTarget().getClass();
}
public String getClassName(JoinPoint jp) {
return jp.getTarget().getClass().getName();
}
public String getMethodName(JoinPoint jp) {
return jp.getSignature().getName();
}
public String getAnnoValue(ProceedingJoinPoint jp) {
Signature signature = jp.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
if (targetMethod.isAnnotationPresent(TimeSpend.class)) {
TimeSpend timeSpend = (TimeSpend) targetMethod.getAnnotation(TimeSpend.class);
return timeSpend.value();
}
return null;
}
}
三 使用注解来标记方法
@TimeSpend("流水入库以及自动匹配")
@Override
public void doStorageAndMatch() {
// 1.流水入库
try {
arrivalFlowStorageBatch.arrivalFlowStorageJob();
} catch (Exception e) {
LOGGER.error("流水入库异常",e);
}
// 2.自动匹配
flowAutoMatchBatchAbstract.autoMatch();
}