spring INTERFACES 或 TARGET_CLASS:我应该选择哪种代理模式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21759684/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
INTERFACES or TARGET_CLASS: Which proxyMode should I choose?
提问by Youssef
I am looking for a way to store my object and it seems that the best approach is to use proxies. I found 2 annotation in the internet, which one should I use :
我正在寻找一种存储对象的方法,似乎最好的方法是使用代理。我在互联网上找到了 2 个注释,我应该使用哪一个:
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
or
或者
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )
Moreover, is it true that the proxies is the best way to use than using @Component
@Scope("session")
or using @SessionAttributes
?
此外,代理是否是最好的使用方式而不是 using@Component
@Scope("session")
或 using @SessionAttributes
?
采纳答案by Sotirios Delimanolis
You'll need to understand what each of those annotations does to choose for yourself. See the javadoc, here. Continue for a more detailed explanation.
您需要了解每个注释的作用才能为自己选择。请参阅此处的 javadoc 。继续更详细的解释。
The first
首先
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
creates
创造
a JDK dynamic proxy implementing allinterfaces exposed by the class of the target object
实现目标对象的类公开的所有接口的JDK动态代理
In other words, the proxy will be a subtype of the interfaces that the target object's class implements, but won't be a subclass of the target object's class itself.
换句话说,代理将是目标对象的类实现的接口的子类型,但不会是目标对象的类本身的子类。
Essentially Spring does the following
基本上 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();
}
}
The proxy returned won't be of type Foo
and you therefore can't inject it into any targets of that type. For example, Spring will fail to inject it into a field like
返回的代理不是类型的Foo
,因此您不能将其注入该类型的任何目标。例如,Spring 将无法将其注入到像
@Autowired
private Foo foo;
but will successfully inject the proxy into a field like
但会成功地将代理注入到一个字段中
@Autowired
private Printable printable;
All calls to the proxy will be handled by the InvocationHandler
(which usually performs some use case specific logic then delegates to the target object).
对代理的所有调用都将由 处理InvocationHandler
(通常执行一些特定于用例的逻辑,然后委托给目标对象)。
The second annotation
第二个注释
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )
creates
创造
a class-based proxy (uses CGLIB).
基于类的代理(使用 CGLIB)。
In addition to interfaces, with CGLIB Spring will be able to create a proxy whose class is a subclass of the target's class. In essence, it does the following
除了接口之外,使用 CGLIB Spring 将能够创建一个代理,其类是目标类的子类。本质上,它执行以下操作
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 creates a new class that is a subclass of Foo
and instantiates it (invoking the constructor of Foo
). All calls to the proxy will be intercepted by the provided callback (which usually performs some use case specific logic and then delegates to the target object).
CGLIB 创建一个新类,它是 的子类Foo
并实例化它(调用 的构造函数Foo
)。对代理的所有调用都将被提供的回调拦截(通常执行一些用例特定的逻辑,然后委托给目标对象)。
Since the proxy class extends Foo
, Spring can inject the proxy into a field (or constructor/method parameter) like
由于代理类 extends Foo
,Spring 可以将代理注入到一个字段(或构造函数/方法参数)中,例如
@Autowired
private Foo injectMe;
All this to say, if you're programming to interfaces, then ScopedProxyMode.INTERFACES
will be sufficient. If you're not, then use ScopedProxyMode.TARGET_CLASS
.
综上所述,如果您正在对接口进行编程,那么ScopedProxyMode.INTERFACES
就足够了。如果不是,请使用ScopedProxyMode.TARGET_CLASS
.
As for using @SessionAttributes
, it is not an alternative to session scoped beans. Session attributes are just objects, they are not beans. They don't possess the full lifecycle, injection capabilities, proxying behavior that a bean may have.
至于 using @SessionAttributes
,它不是会话作用域 bean 的替代品。会话属性只是对象,它们不是 bean。它们不具备 bean 可能具有的完整生命周期、注入功能和代理行为。
回答by Angular University
If you want to store the whole bean in the session, use @Scope, otherwise use @SessionAttributes. In the case of using @Scope, if the class implements some interfaces then use INTERFACES proxy mode, if not use TARGET_CLASS.
如果要将整个 bean 存储在会话中,请使用 @Scope,否则使用 @SessionAttributes。在使用@Scope的情况下,如果类实现了一些接口则使用INTERFACES代理模式,如果没有使用TARGET_CLASS。
Usually your service implements an interface, which allows the use of JDK proxies (INTERFACES mode). But if that is not the case then use TARGET_CLASS, which creates a CGLIB proxy.
通常你的服务实现了一个接口,它允许使用 JDK 代理(INTERFACES 模式)。但如果情况并非如此,则使用TARGET_CLASS,它会创建一个 CGLIB 代理。
Using INTERFACES should be used if possible and TARGET as last resort if the bean does not implement interfaces.
如果可能,应该使用 INTERFACES,如果 bean 没有实现接口,则应使用 TARGET 作为最后的手段。
回答by nilup
While going through a blog postprovided in the comments above, I found a comment stating cons of interface-based proxies.
在浏览上述评论中提供的博客文章时,我发现了一条评论,说明了基于接口的代理的缺点。
On the post, user Flemming J?nsson posted this:
在帖子中,用户 Flemming J?nsson 发布了以下内容:
Be careful with using interface-based proxies.
If you are using Spring Security or Spring Transactions you might experience oddities when using interface-based proxies.
E.g. if you have a bean T and that bean has methods a() and b() that are both annotated transactional. Calls from other beans directly to a() or b() will behave properly (as configured). However if you introduce an internal call - where a() calls b() then b's transactional metadata will have no effect. The reason is that when you are using interface-based proxies the internal call will not go through the proxy - and thus the transactional interceptor will not have a chance to start a new transaction.
The same goes for Security. If method a() only requires USER-role but calls b() that requires ADMIN-role, then the internal call from a to b will be performed for any USER with no warnings. Same reason as above, internal calls do not go through the proxy and thus the security interceptor does not have a chance to act upon the call to b() from a().
To solve issues like these use targetClass.
使用基于接口的代理时要小心。
如果您使用 Spring Security 或 Spring Transactions,则在使用基于接口的代理时可能会遇到奇怪的情况。
例如,如果你有一个 bean T 并且这个 bean 有方法 a() 和 b() ,它们都被注释为事务性的。从其他 bean 直接调用 a() 或 b() 将正常运行(如配置)。但是,如果您引入内部调用 - 其中 a() 调用 b() 那么 b 的事务元数据将无效。原因是当您使用基于接口的代理时,内部调用将不会通过代理 - 因此事务拦截器将没有机会启动新事务。
安全性也是如此。如果方法 a() 只需要 USER-role 但调用需要 ADMIN-role 的 b() ,则将对任何 USER 执行从 a 到 b 的内部调用,而不会发出警告。与上述相同的原因,内部调用不通过代理,因此安全拦截器没有机会对从 a() 到 b() 的调用采取行动。
要解决此类问题,请使用 targetClass。