14

カスタム アノテーションでアノテーションを付けたい with フィールドとハンドラ メソッドがあります@Controller@Autowired

例えば、

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

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

カスタムアノテーション@OnlyIfXYZの例を次に示します。Controller Bean の作成を傍受し、独自の CGLIB プロキシを渡し、Spring が autowired フィールドなどのプロパティを設定できるようにすることを考えていました。

を使用してみましたが、残りのプロセスを短絡させる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 にコピーします (私の好みではハックです)。しかし、インターセプトしているメソッドの引数でない場合、 HttpServletRequestandオブジェクトにアクセスすることはできません。HttpServletResponse

Spring Bean 作成ロジックに注入して、Spring がそのプロパティを設定する前に独自のプロキシ コントローラーを注入できる別のコールバックはありますか? コントローラー ハンドラー メソッドの定義に含まれているかどうかに関係なく、オブジェクトとオブジェクトにアクセスできる必要があります。引数として。HttpServletRequestHttpServletResponse

NB@Autowiredフィールドはプロキシでもあり、注釈が付けられている@Transactionalため、Spring はそれをプロキシします。

編集: AOP ソリューションは、メソッド呼び出しをインターセプトするためにうまく機能しますが、メソッド引数がまだない場合HttpServletRequest、オブジェクトにアクセスする方法が見つかりません。HttpServletResponse

私はおそらくHandlerInterceptorAdapterを使用することになりますが、OOPを必要としないメソッドにオーバーヘッドを追加しないように、OOPでそれができることを望んでいました.

4

4 に答える 4

7

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

この側面は、あなたが求めていることの一部を行う必要があります. @OnlyIfXYZそのALSOで注釈が付けられたプロキシ メソッドは、パラメーターとして aを受け取りHttpServletRequestます。さらに、これをHttpServletRequest渡されたパラメーターとして Aspect メソッドにバインドします。

と の両方を求めている可能性があることを理解しています。そのため、リクエストとレスポンスの両方を取り込むように式をHttpServletRequest変更HttpServletResponseできるはずです。args

于 2013-03-24T01:27:03.167 に答える
6

質問の下のコメントを考慮すると、必要なのは 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>

このインターフェースは、リクエスト、レスポンス、および HandlerMethod を持つメソッド preHanlde を提供します。メソッドに注釈が付けられているかどうかを確認するには、次を試してください。

HandlerMethod method = (HandlerMethod) handler;
OnlyIfXYZ customAnnotation = method.getMethodAnnotation(OnlyIfXYZ.class);
于 2013-03-28T13:24:59.650 に答える
2

InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiationBean 作成アプローチをショートさせます。適用される唯一の処理はpostProcessAfterInitialization. つまり、AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues呼び出されることはないため、自動配線は行われません。したがって、メソッドでプロキシされた Bean のプロパティを手動で注入または自動配線する必要がありますpostProcessAfterInitialization

質問: メソッド内のプロキシ ロジックを移動するpostProcessAfterInitializationと、ビジネス要件に影響がありますか? ない場合は、そこでプロキシを行うことをお勧めします。

参考までに: API を作成していない場合は、@nicholas.hauschild が提案するアノテーション アプローチを使用してください。

于 2013-03-27T11:51:58.737 に答える
2

そうではないと思いますが、プロキシを作成した後に自動配線できると思います。

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;

    }

}

別の方法として、Spring AOP Proxy (を使用ProxyFactory) inpostProcessAfterInitializationメソッドを作成することもできます。この方法AbstractAutoProxyCreatorは便利です。サンプルとして BeanNameAutoProxyCreator を参照してください。しかし、注釈ポイントカット(ニコラスの回答)は同じことを行い、より簡単です。

于 2013-03-24T19:52:11.340 に答える