一尘不染

接口或TARGET_CLASS:我应该选择哪个proxyMode?

spring-mvc

我正在寻找一种存储对象的方法,看来最好的方法是使用代理。我在互联网上找到2个注释,我应该使用其中一个:

@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)

要么

@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )

而且,与使用相比,代理是最佳的@Component @Scope("session")使用方式@SessionAttributes吗?


阅读 1526

收藏
2020-06-01

共1个答案

一尘不染

您需要了解这些注释各自为自己选择的功能。在此处查看javadoc
。继续进行更详细的说明。

首先

@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)

创造

一个JDK动态代理,实现目标对象类公开的 所有 接口

换句话说,代理将是目标对象的类实现的接口的子类型,但不会是目标对象的类本身的子类。

本质上,Spring执行以下操作

public class Example {
    public static void main(String[] args) throws Exception {
        Foo target = new Foo();
        InvocationHandler proxyHandler = ... // some proxy specific logic, likely referencing the `target`

        // works fine
        Printable proxy = (Printable) Proxy.newProxyInstance(Example.class.getClassLoader(),
                target.getClass().getInterfaces(), proxyHandler);

        // not possible, ClassCastException
        Foo foo = (Foo) proxy; 
    }

    public static class Foo implements Printable {
        @Override
        public void print() {
        }
    }

    public interface Printable {
        void print();
    }
}

返回的代理将不是此类型Foo,因此您不能将其注入该类型的任何目标中。例如,Spring将无法将其注入到类似

@Autowired
private Foo foo;

但会成功将代理注入到类似

@Autowired
private Printable printable;

所有对代理的调用都将由处理InvocationHandler(通常会执行一些用例特定的逻辑,然后委托给目标对象)。


第二注

@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )

创造

基于类的代理(使用CGLIB)。

除了接口之外,Spring还可以使用CGLIB创建一个代理,该代理的类是目标类的子类。本质上,它执行以下操作

Foo target = new Foo();
net.sf.cglib.proxy.Enhancer enhancer = new net.sf.cglib.proxy.Enhancer();
enhancer.setInterfaces(target.getClass().getInterfaces());
enhancer.setSuperclass(target.getClass());
net.sf.cglib.proxy.MethodInterceptor interceptor = ... // some proxy specific logic, likely referencing the `target`
enhancer.setCallback(interceptor);

// works fine
Foo proxy = (Foo) enhancer.create();

CGLIB创建一个新类,Foo并将其实例化(调用的构造函数Foo)。提供的回调将拦截对代理的所有调用(该回调通常执行一些用例特定的逻辑,然后委托给目标对象)。

由于代理类已扩展Foo,Spring可以将代理注入到字段(或构造函数/方法参数)中,例如

@Autowired
private Foo injectMe;

这么说,如果您正在对interfaces进行编程,那么ScopedProxyMode.INTERFACES就足够了。如果不是,请使用ScopedProxyMode.TARGET_CLASS


至于使用@SessionAttributes,它不是会话范围的Bean的替代方案。会话属性只是对象,它们不是bean。它们不具备bean可能具有的完整生命周期,注入功能和代理行为。

2020-06-01