什么是自定义注解?
注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记,通过Java的反射原理而进行一些特殊的处理。例如标注在方法上可以实现接口权限的校验。自定义注解是我们为了实现一些原有注解无法实现的自定义需求,可以自行定义一个新的注解。
这是一个为了实现mq发送保证必达性的组件,在本地业务事务中保存一条消息到本地消息表,然后在事务提交之后马上触发发送,发送失败的通过本地消息表轮询进行重试。
- 先加载一个实现了MethodInterceptor的接口的类作为AOP拦截类,这样在这个类中实现具体的方法调用。
public class MethodEventPostProcessor extends AbstractAdvisingBeanPostProcessor
implements InitializingBean, ApplicationContextAware {
private ApplicationContext applicationContext;
private static final long serialVersionUID = 4899348144652105362L;
private Class<? extends Annotation> eventAnnotationType = Event.class;
/**
* 在这里把MethodEventInterceptor加到AOP中的切点上作为通知,所有注解了@Event的方法都会被代理
*/
public void afterPropertiesSet() {
Pointcut pointcut = new AnnotationMatchingPointcut(null, this.eventAnnotationType);
Advice advice = applicationContext.getBean(MethodEventInterceptor.class);
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
- MethodEventInterceptor类实现了MethodInterceptor接口,重写invoke方法。在这里可以嵌套事务管理进去,可以在事务成功提交后执行。
@Component
public class MethodEventInterceptor implements MethodInterceptor, ApplicationContextAware {
private Logger logger = LoggerFactory.getLogger(getClass());
private Map<Method, Event> eventCache = Maps.newConcurrentMap();
@Resource
private Dispatcher messageDispatcher;
@Resource
private Builder messageBuilder;
@Autowired
private SendEventMessageStoreService sendEventMessageStoreService;
private ApplicationContext applicationContext;
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
Object[] arguments = invocation.getArguments();
Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass());
EventMessageContext.addMethod(method);
Event event = eventCache.get(method);
Object result = null;
try {
if (event == null) {
event = AnnotationUtils.findAnnotation(method, Event.class);
eventCache.put(method, event);
}
//是否需要可靠性,这里用作判断是否需要事务
if (!event.reliability()) {
//非事务中执行,不需要开启事务
result = invocation.proceed();
EventMessage<?> eventMessage = messageBuilder.build(event, target, method, arguments, result, EventMessageContext.getMessage(method));
messageDispatcher.dispatch(event.queue(), event.exchange(), event.amqpTemplate(), eventMessage);
} else {
// 事务开始
// 获取transactionMananger
String txManangerName = event.value();
PlatformTransactionManager transactionManager = null;
if (StringUtils.isNotEmpty(txManangerName)) {
transactionManager = applicationContext.getBean(txManangerName, PlatformTransactionManager.class);
} else {
transactionManager = applicationContext.getBean(PlatformTransactionManager.class);
}
// 设置事务属性
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setIsolationLevel(event.isolation().value());
transactionTemplate.setPropagationBehavior(event.propagation().value());
transactionTemplate.setTimeout(event.timeout());
transactionTemplate.setReadOnly(event.readOnly());
final EventMessage<?>[] eventMessage = new EventMessage<?>[1];
final Event finalEvent = event;
// 业务方法与消息在一个事务中
result = transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
Object methodResult;
try {
// 执行方法
long startTime = System.currentTimeMillis();
logger.info("开始执行方法{}, {}", method.getName(), startTime);
methodResult = invocation.proceed();
long afterMethod = System.currentTimeMillis();
logger.info("结束执行方法{},{}, cost:{}", method.getName(), afterMethod, afterMethod - startTime);
Object message = EventMessageContext.getMessage(method);
if(message == null){//如果消息体为空,不做发送处理
return methodResult;
}
eventMessage[0] = messageBuilder.build(finalEvent, target, method, arguments, methodResult, message);
// 对消息进行落库
sendEventMessageStoreService.add(eventMessage[0]);
long afterEvent = System.currentTimeMillis();
logger.info("event{}落库方法{},{}, cost:{}", eventMessage[0].getId(), method.getName(), afterMethod, afterEvent - afterMethod);
return methodResult;
} catch (Throwable e) {
// 默认所有异常都回滚事务
throw new IllegalStateException(e);
}
}
});
//如果message不为空,则证明需要发送这条消息
if(EventMessageContext.getMessage(method) != null) {
long beforeSend = System.currentTimeMillis();
try {
logger.info("event{}开始发送,{}", eventMessage[0].getId(), method.getName(), beforeSend);
messageDispatcher.dispatch(event.queue(), event.exchange(), event.amqpTemplate(), eventMessage[0]);
sendEventMessageStoreService.updateStatus(eventMessage[0].getId(), EventStatus.SUCCESS);
} catch (Exception e) {
sendEventMessageStoreService.updateStatus(eventMessage[0].getId(), EventStatus.FAIL);
throw e;
}
long afterSend = System.currentTimeMillis();
logger.info("event{}结束发送,{}, {}, cost:{}", eventMessage[0].getId(), method.getName(), afterSend, afterSend - beforeSend);
}
// TODO MQ confirm 时间调用长度 嵌套 threadlocal 不支持的方式 - 抛出异常告知
}
return result;
} catch (Exception e) {
logger.error(e.getMessage(), e);
if (event.throwException()) {
throw new IllegalStateException(e.getMessage(), e);
} else {
return result;
}
}finally {
EventMessageContext.remove();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
- @Event的代码,里面封装了事务注解的属性,用于操作事务。
/**
* 事件消息
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Event {
String contentType() default "object/javabean";
/**
* 队列
*/
String queue() default "";
/**
* exchange
*/
String exchange() default "";
/**
* amqpTemplate bean 名称
*/
String amqpTemplate() default "";
/**
* 消息版本
*/
String version() default EventHeaders.DEFAULT_VERSION;
String eventType() default "";
String eventId() default "";
String subjectId() default "";
/***
* 是否使用持久化保证消息的可靠性
*/
boolean reliability() default false;
/**
* 是否抛出异常
*/
boolean throwException() default true;
/**
* A qualifier value for the specified transaction.
* <p>
* May be used to determine the target transaction manager, matching the qualifier value (or the
* bean name) of a specific {@link org.springframework.transaction.PlatformTransactionManager}
* bean definition.
*/
String value() default "";
/**
* The transaction propagation type. Defaults to {@link Propagation#REQUIRED}.
*
* @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
*/
Propagation propagation() default Propagation.REQUIRED;
/**
* The transaction isolation level. Defaults to {@link Isolation#DEFAULT}.
*
* @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
*/
Isolation isolation() default Isolation.DEFAULT;
/**
* The timeout for this transaction. Defaults to the default timeout of the underlying
* transaction system.
*
* @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
/**
* {@code true} if the transaction is read-only. Defaults to {@code false}.
* <p>
* This just serves as a hint for the actual transaction subsystem; it will <i>not
* necessarily</i> cause failure of write access attempts. A transaction manager which cannot
* interpret the read-only hint will <i>not</i> throw an exception when asked for a read-only
* transaction.
*
* @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
*/
boolean readOnly() default false;
/**
* Defines zero (0) or more exception {@link Class classes}, which must be a subclass of
* {@link Throwable}, indicating which exception types must cause a transaction rollback.
* <p>
* This is the preferred way to construct a rollback rule, matching the exception class and
* subclasses.
* <p>
* Similar to
* {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}
*/
Class<? extends Throwable>[] rollbackFor() default {Exception.class};
/**
* Defines zero (0) or more exception names (for exceptions which must be a subclass of
* {@link Throwable}), indicating which exception types must cause a transaction rollback.
* <p>
* This can be a substring, with no wildcard support at present. A value of "ServletException"
* would match {@link javax.servlet.ServletException} and subclasses, for example.
* <p>
* <b>NB: </b>Consider carefully how specific the pattern is, and whether to include package
* information (which isn't mandatory). For example, "Exception" will match nearly anything, and
* will probably hide other rules. "java.lang.Exception" would be correct if "Exception" was
* meant to define a rule for all checked exceptions. With more unusual {@link Exception} names
* such as "BaseBusinessException" there is no need to use a FQN.
* <p>
* Similar to
* {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}
*/
String[] rollbackForClassName() default {};
/**
* Defines zero (0) or more exception {@link Class Classes}, which must be a subclass of
* {@link Throwable}, indicating which exception types must <b>not</b> cause a transaction
* rollback.
* <p>
* This is the preferred way to construct a rollback rule, matching the exception class and
* subclasses.
* <p>
* Similar to
* {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}
*/
Class<? extends Throwable>[] noRollbackFor() default {};
/**
* Defines zero (0) or more exception names (for exceptions which must be a subclass of
* {@link Throwable}) indicating which exception types must <b>not</b> cause a transaction
* rollback.
* <p>
* See the description of {@link #rollbackForClassName()} for more info on how the specified
* names are treated.
* <p>
* Similar to
* {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}
*/
String[] noRollbackForClassName() default {};
}
其他部分源码先放在了我的百度云盘里,这里主要目的是讲一下自定义注解的实现。