Spring - 拦截豆子创建和注入自定义代理

2022-09-03 14:18:13

我有一个带有字段和处理程序的方法,我想用自定义批注进行批注。@Controller@Autowired

例如

@Controller
public class MyController{
    @Autowired
    public MyDao myDao;

    @RequestMapping("/home")
    @OnlyIfXYZ
    public String onlyForXYZ() {
        // do something
        return "xyz";
    }
}

其中 是自定义批注的示例。我想我会拦截控制器bean的创建,传递我自己的CGLIB代理,然后Spring可以在其上设置属性,例如自动连接字段。@OnlyIfXYZ

我尝试使用一个,但该解决方案效果不佳,因为该过程的其余部分短路。我试过,如下所示InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation()postProcessAfterInitialization()

public class MyProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // Here the bean autowired fields are already set
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object aBean, String aBeanName) throws BeansException {
        Class<?> clazz = aBean.getClass();
        // only for Controllers, possibly only those with my custom annotation on them
        if (!clazz.isAnnotationPresent(Controller.class))
            return aBean;

        Object proxy = Enhancer.create(clazz, new MyMethodInterceptor());
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            try {
                // get the field and copy it over to the proxy
                Object objectToCopy = field.get(aBean);
                field.set(proxy, objectToCopy);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                return aBean;
            }
        }   
        return proxy;
    }
}

该解决方案使用反射将目标Bean的所有字段复制到代理Bean(对于我的口味来说有点黑客)。但是,如果 and 对象不是我正在截取的方法中的参数,则我无法访问 and 对象。HttpServletRequestHttpServletResponse

在Spring填充其属性之前,是否可以将另一个回调注入Spring Bean创建逻辑以注入我自己的代理控制器?我需要能够访问 HttpServletRequestHttpServletResponse 对象,而不管 Controller handler 方法是否在其定义中包含它,即作为参数。

注意:该字段也是一个代理,它被注释为Spring代理它。@Autowired@Transactional

编辑:AOP解决方案非常适合拦截方法调用,但是如果它们不是方法参数,我找不到访问和对象的方法。HttpServletRequestHttpServletResponse

我可能最终会使用HandlerInterceptorAdapter,但我希望我能用OOP做到这一点,这样就不会给不需要它的方法增加开销。


答案 1

看看Spring AOP。它拥有您所需要的设施。对于您的示例,您可以执行如下操作:

@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;
    }
}

值得注意的是,Spring只会将代理应用于作为其应用程序上下文一部分的类。(在您的示例中似乎就是这种情况)

您还可以使用 Spring AOP 将参数绑定到坡向方法。这可以通过各种方式完成,但是您所追求的可能是 。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;
    }
}

这方面应该做你所追求的一部分。它将代理带有NOTE的方法,该方法采用a作为参数。此外,它将它作为传入参数绑定到 Aspect 方法中。@OnlyIfXYZHttpServletRequestHttpServletRequest

我知道您可能同时追求和,因此您应该能够修改表达式以同时接受请求和响应。HttpServletRequestHttpServletResponseargs


答案 2

考虑到您在问题下的评论,您所需要的只是HandlerInterceptor。

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

您需要实现该接口并将其添加到您的配置中,例如:

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

该接口提供了方法preHanlde,它具有请求,响应和处理方法。要检查该方法是否带有注释,请尝试以下操作:

HandlerMethod method = (HandlerMethod) handler;
OnlyIfXYZ customAnnotation = method.getMethodAnnotation(OnlyIfXYZ.class);

推荐