Java Spring-拦截bean创建和注入自定义代理
我有一个带有Java Spring-拦截bean创建和注入自定义代理,java,spring,spring-mvc,spring-aop,Java,Spring,Spring Mvc,Spring Aop,我有一个带有@Autowired字段和处理程序方法的@Controller,我想用自定义注释对其进行注释 比如说, @Controller public class MyController{ @Autowired public MyDao myDao; @RequestMapping("/home") @OnlyIfXYZ public String onlyForXYZ() { // do something retu
@Autowired
字段和处理程序方法的@Controller
,我想用自定义注释对其进行注释
比如说,
@Controller
public class MyController{
@Autowired
public MyDao myDao;
@RequestMapping("/home")
@OnlyIfXYZ
public String onlyForXYZ() {
// do something
return "xyz";
}
}
其中,@onlyfxyz
是自定义注释的一个示例。我想我会拦截控制器bean的创建,传递我自己的CGLIB代理,然后Spring可以在其上设置属性,比如autowired字段
我尝试使用实例化WarebeanPostProcessor
,但该解决方案效果不佳,因为后处理BeforeInstantiation()
会缩短流程的其余部分。我尝试了后处理AfterInitialization()
,如下所示
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;
}
}
公共类MyProcessor实现BeanPostProcessor{
@凌驾
公共对象后处理BeforeInitialization(对象bean、字符串beanName)抛出BeanException{
//这里已经设置了bean自动连接字段
返回豆;
}
@凌驾
公共对象后处理初始化后(对象aBean,字符串aBeanName)抛出BeansException{
类clazz=aBean.getClass();
//仅适用于控制器,可能仅适用于带有自定义注释的控制器
如果(!clazz.isAnnotationPresent(Controller.class))
返回aBean;
objectproxy=Enhancer.create(clazz,newmymethodinterceptor());
Field[]fields=clazz.getDeclaredFields();
用于(字段:字段){
字段。setAccessible(true);
试一试{
//获取字段并将其复制到代理
objectToCopy=field.get(aBean);
field.set(代理,objectToCopy);
}捕获(IllegalArgumentException | IllegalAccessException e){
返回aBean;
}
}
返回代理;
}
}
这个解决方案使用反射将目标bean的所有字段复制到代理bean(我觉得有点像黑客)。但是如果我截取的方法中没有参数,我就无法访问HttpServletRequest
和HttpServletResponse
对象
在Spring填充其属性之前,我是否可以向Springbean创建逻辑中注入另一个回调来注入我自己的代理控制器我需要能够访问HttpServletRequest
和HttpServletResponse
对象,无论控制器处理程序方法是否将其定义为参数。
N.B自动连线的@Autowired
字段也是一个代理,它用@Transactional
注释,因此Spring将其代理起来
EDIT:AOP解决方案可以很好地拦截方法调用,但是我找不到访问HttpServletRequest
和HttpServletResponse
对象的方法,如果它们还不是方法参数的话
我可能最终会使用HandlerInterceptorAdapter,但我希望我可以用OOP来实现,这样就不会给不需要它的方法增加开销。看一看。它的设施正是你想要的。例如,您可以这样做:
@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将只将代理应用于作为其应用程序上下文一部分的类。(在您的示例中似乎就是这样)
您还可以使用SpringAOP将参数绑定到aspect方法。这可以通过多种方式完成,但您所追求的可能是args(paramName)
这相位应该是你追求的一部分。它将代理带有@onlyfxyz
注释的方法,也将HttpServletRequest
作为参数。此外,它将把这个HttpServletRequest
作为传入参数绑定到Aspect方法中
我知道您可能同时关注
HttpServletRequest
和HttpServletResponse
,因此您应该能够修改args
表达式以同时接收请求和响应。我认为没有,但我希望您可以在创建代理后自动连接代理
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;
}
}
公共类MyProcessor扩展实例化WarebeanPostProcessorAdapter
实现BeanFactoryAware{
私人自动布线beanFactory beanFactory;
@凌驾
公共对象后处理BeforeInstantiation(类beanClass,字符串beanName)抛出BeanException{
//这是我想我会做的,但它会跳过所有设置字段
if(beanClass.isAnnotationPresent(Controller.class)){
objectproxy=Enhancer.create(beanClass,newmyinterceptor());
//自动连线
autowireBean(代理);
返回代理;
}
返回null;
}
@凌驾
public void setBeanFactory(BeanFactory BeanFactory)抛出BeanException{
this.beanFactory=(AutowireCapableBeanFactory)beanFactory;
}
}
另一种选择是在
postprocessinessafterinitialization
方法中创建Spring AOP代理(使用ProxyFactory
)。为此,AbstractAutoProxyCreator
可能很有用。请参见BeanNameAutoProxyCreator作为示例。但是,注释切入点(Nicholas answer)也是这样做的,而且更简单。实例化WarebeanPostProcessor。实例化前的后处理将缩短bean创建方法。应用的唯一处理是后处理afterInitialization
。这意味着,自动连线不会发生,因为永远不会调用AutowiredNotationBeanPostProcessor.PostProcessPropertyValue
。因此,您应该在postprocesseafterinitialization
方法中手动注入或自动连接代理bean的属性
问题:在postProce中移动代理逻辑是否正确
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;
}
}
<mvc:interceptors>
<bean id="customInterceptor" class="com.example.interceptors.CustomInterceptor"/>
</mvc:interceptors>
HandlerMethod method = (HandlerMethod) handler;
OnlyIfXYZ customAnnotation = method.getMethodAnnotation(OnlyIfXYZ.class);