java - Spring - 拦截 bean 创建和注入(inject)自定义代理

标签 java spring spring-mvc spring-aop

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

例如,

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

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

其中 @OnlyIfXYZ 是自定义注释的示例。我在想我会拦截 Controller bean 创建,传递我自己的 CGLIB 代理,然后 Spring 可以在该代理上设置属性,例如 Autowiring 字段。

我尝试使用 InstantiationAwareBeanPostProcessor,但该解决方案效果不佳,因为 postProcessBeforeInstantiation() 使其余过程短路。我尝试使用 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(对我来说有点 hacky)。但是,如果 HttpServletRequestHttpServletResponse 对象不是我正在拦截的方法中的参数,我将无权访问它们。

在 Spring 填充其属性之前,我是否可以将另一个回调注入(inject)到 Spring bean 创建逻辑中以注入(inject)我自己的代理 Controller ? 我需要能够访问 HttpServletRequestHttpServletResponse 对象,无论 Controller 处理程序方法是否在其定义中包含它,即。作为论据。

注意 @Autowired 字段也是一个代理,它带有 @Transactional 注释,因此 Spring 代理它。

编辑: AOP 解决方案可以很好地拦截方法调用,但我找不到访问 HttpServletRequestHttpServletResponse 的方法> 对象,如果它们还不是方法参数。

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

最佳答案

看看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 将参数绑定(bind)到您的切面方法。这可以通过多种方式完成,但您所追求的可能是 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;
    }
}

这方面应该做你所追求的一部分。它将代理使用 @OnlyIfXYZ 注释的方法,ALSOHttpServletRequest 作为参数。此外,它会将这个 HttpServletRequest 作为传入参数绑定(bind)到 Aspect 方法中。

我了解您可能同时关注 HttpServletRequestHttpServletResponse,因此您应该能够修改 args 表达式以同时接受两者请求和响应。

关于java - Spring - 拦截 bean 创建和注入(inject)自定义代理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15594071/

相关文章:

java - Spring MVC : Dynamic Param Name or Param Name using Regex?

java - Spring 3.0 MVC 绑定(bind)嵌套对象

java - Spring 警告 : Request method 'HEAD' not supported

Spring Data Redis SET命令支持EX和NX

java - Hadoop Reducer在JAVA中两次迭代

java - struts.xml映射问题

java - 1.4 Commons-DBCP 被引入而不是 1.3

java - Spring Data JPA 如何传递日期参数

java - Spring MVC 中的 @ImportResource 路径

java - 为什么 IntelliJ 能够看到我的应用程序上下文,但当我运行测试时他们却看不到它?