记一个简单的自定义spring注解的实现方案

13 篇文章 1 订阅

什么是自定义注解?

注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记,通过Java的反射原理而进行一些特殊的处理。例如标注在方法上可以实现接口权限的校验。自定义注解是我们为了实现一些原有注解无法实现的自定义需求,可以自行定义一个新的注解。

这是一个为了实现mq发送保证必达性的组件,在本地业务事务中保存一条消息到本地消息表,然后在事务提交之后马上触发发送,发送失败的通过本地消息表轮询进行重试。

  1. 先加载一个实现了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;
    }
}
  1. 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;
    }
}
  1. @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 {};
}

其他部分源码先放在了我的百度云盘里,这里主要目的是讲一下自定义注解的实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值