我有一个带有 @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)。但是,如果 HttpServletRequest
和 HttpServletResponse
对象不是我正在拦截的方法中的参数,我将无权访问它们。
在 Spring 填充其属性之前,我是否可以将另一个回调注入(inject)到 Spring bean 创建逻辑中以注入(inject)我自己的代理 Controller ? 我需要能够访问 HttpServletRequest
和 HttpServletResponse
对象,无论 Controller 处理程序方法是否在其定义中包含它,即。作为论据。
注意 @Autowired
字段也是一个代理,它带有 @Transactional
注释,因此 Spring 代理它。
编辑: AOP 解决方案可以很好地拦截方法调用,但我找不到访问 HttpServletRequest
和 HttpServletResponse
的方法> 对象,如果它们还不是方法参数。
我可能最终会使用 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
注释的方法,ALSO 将 HttpServletRequest
作为参数。此外,它会将这个 HttpServletRequest
作为传入参数绑定(bind)到 Aspect 方法中。
我了解您可能同时关注 HttpServletRequest
和 HttpServletResponse
,因此您应该能够修改 args
表达式以同时接受两者请求和响应。
关于java - Spring - 拦截 bean 创建和注入(inject)自定义代理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15594071/