如何编写像 ibatis Select 注释一样工作的 spring 自定义注释?

How to code spring custom annotation which works like ibatis Select annotation?

我正在尝试制作像 ibatis 这样的自定义注释 @Select

总之,目标是

先看看终点——ArtistNodeRepository.java

@Repository
public interface ArtistNodeRepository {

    @CreateNode(tid = "artist")
    public Node create(Map data) throws Exception;
}

想用CreateNode注解做什么,把data.put("type", "artist")放到参数Map里。

这是注释 - CreateNode.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface CreateNode {
    String[] values() default "";
    String tid();
}

为了控制器注释,我准备了这个BeanPostProcessor - NodeAnnotationProcessor.java

@Component
public class NodeAnnotationProcessor implements BeanPostProcessor {

    private ConfigurableListableBeanFactory configurableListableBeanFactory;

    @Autowired
    public NodeAnnotationProcessor(ConfigurableListableBeanFactory configurableListableBeanFactory) {
        super();
        this.configurableListableBeanFactory = configurableListableBeanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        this.scanNodeAnnotation(bean, beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
       // this.scanNodeAnnotation(bean, beanName);
        return bean;
    }

    protected void scanNodeAnnotation(Object bean, String beanName){
        this.configureMethodAction(bean);
    }

    private void configureMethodAction(Object bean){
        Class<?> managedBeanClass = bean.getClass();
        ReflectionUtils.MethodCallback methodCallback = new NodeMethodCallback(configurableListableBeanFactory, bean);
        ReflectionUtils.doWithMethods(managedBeanClass, methodCallback);
    }
}

我不清楚 MethodCallbackpostProcessBeforeInitializationpostProcessAfterInitialization 的位置。在我看来,它会在 after 中,因为我试图操纵方法的参数

最后,这是 MethodCallback - NodeMethodCallback.java

public class NodeMethodCallback implements ReflectionUtils.MethodCallback {

    private Logger logger = LoggerFactory.getLogger(NodeMethodCallback.class);

    private ConfigurableListableBeanFactory beanFactory;
    private Object bean;
    private static int AUTOWIRE_MODE = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

    public NodeMethodCallback(ConfigurableListableBeanFactory beanFactory, Object bean) {
        this.beanFactory = beanFactory;
        this.bean = bean;
    }

    @Override
    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

        logger.info("doWith method info :: " + String.valueOf(bean) + "." + bean.getClass().getName());
        /*
            What I expected is Printing ArtistNodeRepository Class with create Method
            But It prints something like ...

            SessionFlashMapManager
            DefaultRequestToViewNameTranslator
            ...
        */


        try {
           logger.info("When I call you :: " + method.getName()); // I expect method which contains @CreateNode annotation, but it is not ...
            Annotation[] methodAnnotations = method.getDeclaredAnnotations();
            boolean isTarget = false;
            String tid = "";
            for(Annotation anno : methodAnnotations) {
                logger.info("annotation Class :: " + anno.getClass().getName());
                if(isTarget) break;
                if(anno instanceof CreateNode) {
                    logger.info("CreateNode annotation found");
                    CreateNode createNode = method.getDeclaredAnnotation(CreateNode.class);
                    tid = createNode.tid();
                    isTarget = true;
                } 
            }
            if(!isTarget) return;
            ReflectionUtils.makeAccessible(method);

            /*
                Do Somthing with Parameter ...
                Do Somthing with Parameter ...
                Do Somthing with Parameter ...
                Do Somthing with Parameter ...
                Do Somthing with Parameter ...
            */
        } catch (Exception e ){
            logger.error("ERROR", e);
        }
    }
}

问题是...在 doWith 我找不到 ArtistNodeRepository 实例。

MethodCallbackBeanPostProcessor应该怎么做才能达到目的?

好的示例代码和好的答案一样好。

我认为您误解了 ReflectionUtils.doWithMethods 的用法。这意味着如果匹配回调,则迭代 class 方法。而不是在调用方法回调时。

  public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
        // Keep backing up the inheritance hierarchy.
        Method[] methods = getDeclaredMethods(clazz);
        for (Method method : methods) {
            if (mf != null && !mf.matches(method)) {
                continue;
            }
            try {
                mc.doWith(method);
            }
            catch (IllegalAccessException ex) {
                throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
            }
        }
        if (clazz.getSuperclass() != null) {
            doWithMethods(clazz.getSuperclass(), mc, mf);
        }
        else if (clazz.isInterface()) {
            for (Class<?> superIfc : clazz.getInterfaces()) {
                doWithMethods(superIfc, mc, mf);
            }
        }
    }

我认为你可以使用 aspect。像这样。

    @Around("execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))")
    // @Around("@annotation(Repository)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        CreateNode createNode = method.getAnnotation(CreateNode.class);
        if(createNode != null) {
            Object[] args = joinPoint.getArgs();
            // do your business

        }
        return joinPoint.proceed();
    }

希望能帮到你