众所周知,Spring使用代理添加功能(例如@transactional
和@schedule
)。有两种选择--使用JDK动态代理(类必须实现非空接口),或者使用CGLIB代码生成器生成子类。我一直认为proxyMode允许我在JDK动态代理和CGLIB之间进行选择。
但我能创造一个例子,说明我的假设是错误的:
单身:
@Service
public class MyBeanA {
@Autowired
private MyBeanB myBeanB;
public void foo() {
System.out.println(myBeanB.getCounter());
}
public MyBeanB getMyBeanB() {
return myBeanB;
}
}
@Service
@Scope(value = "prototype")
public class MyBeanB {
private static final AtomicLong COUNTER = new AtomicLong(0);
private Long index;
public MyBeanB() {
index = COUNTER.getAndIncrement();
System.out.println("constructor invocation:" + index);
}
@Transactional // just to force Spring to create a proxy
public long getCounter() {
return index;
}
}
MyBeanA beanA = context.getBean(MyBeanA.class);
beanA.foo();
beanA.foo();
MyBeanB myBeanB = beanA.getMyBeanB();
System.out.println("counter: " + myBeanB.getCounter() + ", class=" + myBeanB.getClass());
constructor invocation:0
0
0
counter: 0, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$2f3d648e
这里我们可以看到两点:
MyBeanB
只实例化了一次。mybeanb
添加@transactional
功能,Spring使用了cglib。让我更正mybeanb
定义:
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class test.pack.MyBeanB$$EnhancerBySpringCGLIB$$b06d71f2
MyBeanB
实例化了3次。mybeanb
添加@transactional
功能,Spring使用了cglib。你能解释一下是怎么回事吗?代理模式到底是如何工作的?
我读过文档:
/**
* Specifies whether a component should be configured as a scoped proxy
* and if so, whether the proxy should be interface-based or subclass-based.
* <p>Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
* that no scoped proxy should be created unless a different default
* has been configured at the component-scan instruction level.
* <p>Analogous to {@code <aop:scoped-proxy/>} support in Spring XML.
* @see ScopedProxyMode
*/
public interface MyBeanBInterface {
long getCounter();
}
@Service
public class MyBeanA {
@Autowired
private MyBeanBInterface myBeanB;
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES)
public class MyBeanB implements MyBeanBInterface {
constructor invocation:0
0
constructor invocation:1
1
constructor invocation:2
counter: 2, class=class com.sun.proxy.$Proxy92
这里我们可以看到两点:
MyBeanB
实例化了3次。mybeanb
添加@transactional
功能,Spring使用了JDK动态代理。为@transactional
行为生成的代理与作用域代理的作用不同。
@transactional
代理是包装特定bean以添加会话管理行为的代理。所有方法调用都将在委托给实际bean之前和之后执行事务管理。
如果你举例说明,它看起来像
main -> getCounter -> (cglib-proxy -> MyBeanB)
@scope
代理的行为不同。文件说明:
[...]您需要注入一个代理对象,该代理对象公开与作用域对象相同的公共接口,但也可以从相关作用域(如HTTP请求)检索真正的目标对象,并将方法调用委托到真正的对象上。
Spring真正做的是为代表代理的工厂类型创建一个单例bean定义。但是,对应的代理对象在上下文中查询每个调用的实际bean。
main -> getCounter -> (cglib-scoped-proxy -> context/bean-factory -> new MyBeanB)
由于mybeanb
是一个原型bean,上下文将始终返回一个新实例。
出于这个答案的目的,假设您直接使用以下命令检索mybeanb
MyBeanB beanB = context.getBean(MyBeanB.class);
这实质上是Spring为满足@autowired
注入目标所做的工作。
@Service
@Scope(value = "prototype")
public class MyBeanB {
默认值为scopedproxymode.default
,这通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。
因此Spring没有为生成的bean创建一个有作用域的代理。您可以用
MyBeanB beanB = context.getBean(MyBeanB.class);
您现在有了对Spring创建的新MyBeanB
对象的引用。这与任何其他Java对象一样,方法调用将直接转到被引用的实例。
在第二个示例中,
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyBeanB {
您声明了一个通过CGLIB实现的有作用域的代理。当从Spring请求这种类型的bean时
MyBeanB beanB = context.getBean(MyBeanB.class);
Spring知道mybeanb
是一个作用域代理,因此返回一个满足mybeanb
API(即实现其所有公共方法)的代理对象,该对象内部知道如何为每个方法调用检索mybeanb
类型的实际bean。
System.out.println("singleton?: " + (context.getBean(MyBeanB.class) == context.getBean(MyBeanB.class)));
这将返回true
,暗示Spring返回的是一个单例代理对象(而不是原型bean)。
在代理实现内部的方法调用中,Spring将使用一个特殊的GetBean
版本,该版本知道如何区分代理定义和实际的MyBeanB
bean定义。这将返回一个新的MyBeanB
实例(因为它是一个原型),Spring将通过反射(经典的method.invoke
)将方法调用委托给它。
第三个示例与第二个示例基本相同。
问题内容: 正如我们所知道Spring使用代理来增加功能(和举例)。有两种选择- 使用JDK动态代理(该类必须实现非空接口),或使用CGLIB代码生成器生成子类。我一直认为proxyMode允许我在JDK动态代理和CGLIB之间进行选择。 但是我能够创建一个示例,说明我的假设是错误的: 情况1: 单身人士: 原型: 主要: 输出: 在这里我们可以看到两件事: 只实例化了 一次 。 为了添加的功能,
问题内容: 有人可以解释什么是无作用域及其目的吗? 假设我有一个豆子 并说我没有将任何作用域bean n1注入到上述每个作用域中,然后我发现当实例化其父bean [r1 / s1 / a1]时,将为每个父bean实例化n1。 由于a1是应用程序作用域,因此a1中的作用域bean在整个a1中都不可用。直到不销毁s1并再次创建s1时,s1中的scope Bean才可用。 这是正确的吗? 以及使用它的目
问题内容: 我想知道我什么时候应该在Spring中准确使用范围?我了解,如果需要Bean,则返回相同的对象实例。 那我们为什么要考虑呢? 通过示例进行解释将有助于您理解其必要性。 问题答案: 要明确简单的定义: 原型范围=每次注入/查找新对象时都会创建一个。每次都会使用new 。 单例范围=每次注入/查找相同对象时,都会返回该对象。在这里它将实例化一个实例,然后每次返回它。 原型bean是在使用时
我在spring中尝试使用会话范围的bean时出错,错误是: 我的配置是:网络。xml: 豆子: 我一直在寻找这个问题,我所找到的是添加以下行web.xml: 这解决了问题,但副作用是,我所有的@Scheduled注释方法都运行了两次,我认为这是因为spring创建了两个上下文。 阅读Spring文档它说: DispatcherServlet、RequestContextListener和Requ
Azure中的一些区域被称为EUAP区域,但我无法找到任何关于这意味着什么的定义。EUAP代表什么? https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.documents.locationnames.centraluseuap?view=azure-网络 从上面的链接: Azure Cosmos DB服务中Azure美国中部EU
主要内容:域名扩展每台服务或网站都有一个IP地址,域名可以理解为IP地址的一个别名,它是一种容易记住的别名。域名是在线地址的一部分,访问者将使用它来轻松找到您的网站。 例如,小牛知识库域名是:,它的IP地址是:,显然我们更容易记住,而不是IP地址: 。域名是独一无二的。 一旦您注册了,如果继续更新续费使用,其他人是不可以注册。 注册一个域名很容易,因为可以选择任何你想要的名字,但是一般选择对你的商业未来或你的博客网