Spring - Intercepting bean creation and injecting custom proxy

后端 未结 4 1618
孤独总比滥情好
孤独总比滥情好 2021-02-13 16:07

I have a @Controller with @Autowired fields and handler methods that I want to annotate with custom annotations.

For example,

         


        
相关标签:
4条回答
  • 2021-02-13 16:38

    Taking into account your comment under the question all you need is HandlerInterceptor.

    http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html

    You need to implement that interface and add it to your configuration, for example:

    <mvc:interceptors>
        <bean id="customInterceptor" class="com.example.interceptors.CustomInterceptor"/>
    </mvc:interceptors>
    

    This interface provides method preHanlde, which has request, response and HandlerMethod. To check if the method is annotated just try this:

    HandlerMethod method = (HandlerMethod) handler;
    OnlyIfXYZ customAnnotation = method.getMethodAnnotation(OnlyIfXYZ.class);
    
    0 讨论(0)
  • 2021-02-13 16:40

    InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation will short-circuit the bean creation approach. The only processing applied is postProcessAfterInitialization. Which means that, autowiring won't happen because AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues will never be called. Therefore, you should manually inject or autowire the properties of the proxied beans in postProcessAfterInitialization method.

    Question: Does moving the proxying logic in postProcessAfterInitialization method have an impact to your business requirements? If none, I suggest you do the proxying there.

    FYI: If you are not building an API, do the annotation approach as suggested by @nicholas.hauschild.

    0 讨论(0)
  • 2021-02-13 16:48

    Take a look at Spring AOP. It has exactly the facilities you are after. For your example, you could do something like this:

    @Aspect
    @Component
    public class MyAspect {
        @Around("@annotation(path.to.your.annotation.OnlyIfXYZ)")
        public Object onlyIfXyz(final ProceedingJoinPoint pjp) throws Exception {
            //do some stuff before invoking methods annotated with @OnlyIfXYZ
            final Object returnValue = pjp.proceed();
            //do some stuff after invoking methods annotated with @OnlyIfXYZ
            return returnValue;
        }
    }
    

    It is worth noting that Spring will only apply the proxy to classes that are a part of its application context. (which it appears is the case in your example)

    You can also use Spring AOP to bind parameters to your aspect method. This can be done in various ways, but the one you are after is probably args(paramName).

    @Aspect
    @Component
    public class MyAspect2 {
        @Around("@annotation(path.to.your.annotation.OnlyIfXYZ) && " +
            "args(..,request,..)")
        public Object onlyIfXyzAndHasHttpServletRequest(final ProceedingJoinPoint pjp,
                final HttpServletRequest request) throws Exception {
            //do some stuff before invoking methods annotated with @OnlyIfXYZ
            //do something special with your HttpServletRequest
            final Object returnValue = pjp.proceed();
            //do some stuff after invoking methods annotated with @OnlyIfXYZ
            //do more special things with your HttpServletRequest
            return returnValue;
        }
    }
    

    This aspect should do a part of what you are after. It will proxy methods annotated with @OnlyIfXYZ that ALSO take in a HttpServletRequest as a parameter. Further, it will bind this HttpServletRequest into the Aspect method as a passed in parameter.

    I understand that you are after potentially both HttpServletRequest and HttpServletResponse, so you should be able to modify the args expression to take in both request and response.

    0 讨论(0)
  • 2021-02-13 16:53

    I think that not, but I supose that you could autowire the proxy after creating it.

    public class MyProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements BeanFactoryAware {
    
        private AutowireCapableBeanFactory beanFactory;
    
         @Override
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
                // This is where I thought I would do it, but it then skips setting fields alltogether
                if (beanClass.isAnnotationPresent(Controller.class)) {
                    Object proxy = Enhancer.create(beanClass, new MyInterceptor());
                    // autowire
                    beanFactory.autowireBean(proxy);
    
                    return proxy;
                }
                return null;
            }
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
    
        }
    
    }
    

    Other alternative is to create a Spring AOP Proxy (using ProxyFactory) in postProcessAfterInitialization method. For this way AbstractAutoProxyCreator could be useful. See BeanNameAutoProxyCreator as sample. But imho, an annotation pointcut (Nicholas answer) do the same and is simpler.

    0 讨论(0)
提交回复
热议问题