如何检查在 Spring 中使用 SCOPE_PROTOTYPE 和 ScopedProxyMode.TARGET_CLASS 的对象实例是否不同?
how to check if object instances using SCOPE_PROTOTYPE and ScopedProxyMode.TARGET_CLASS in Spring are different?
我有两个 bean PersonDAO
和 JdbcConnection
相互依赖。 PersonDAO
bean 是一个使用 @Component
的单例 bean。但是 JdbcConnection
是 prototype
并且因为它被注入到 PersonDAO
内部,所以我使用 proxyMode = ScopedProxyMode.TARGET_CLASS
来确保它是一个不同的实例(不是单例)。
import com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonDAO {
@Autowired
private JdbcConnection jdbcConnection;
public PersonDAO(JdbcConnection jdbcConnection) {
this.jdbcConnection = jdbcConnection;
}
public JdbcConnection getJdbcConnection() {
return jdbcConnection;
}
public void setJdbcConnection(JdbcConnection jdbcConnection) {
this.jdbcConnection = jdbcConnection;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class JdbcConnection {
private static final Logger LOGGER = LoggerFactory.getLogger(JdbcConnection.class);
public JdbcConnection() {
// LOGGER.info("This is my JdbcConnection that is not a singleton bean.");
}
}
当我打印它们的对象实例时,PersonDAO
是一个单例,而 JdbcConnection
是一个不同的实例。但是 JdbcConnection
的哈希码表明它是一个单例。这是为什么?
ApplicationContext applicationContext = SpringApplication.run(ExploreSpringApplication.class, args);
PersonDAO personDAO01 = applicationContext.getBean(PersonDAO.class);
PersonDAO personDAO02 = applicationContext.getBean(PersonDAO.class);
LOGGER.info("DAO 01: {}, {}, JDBCConnection: {}, {}", personDAO01, personDAO01.hashCode(), personDAO01.getJdbcConnection().hashCode(), personDAO01.getJdbcConnection());
LOGGER.info("DAO 02: {}, {}, JDBCConnection: {}, {}", personDAO02, personDAO02.hashCode(), personDAO02.getJdbcConnection().hashCode(), personDAO02.getJdbcConnection());
DAO01
和 DAO02
的输出 JDBCConnection
具有相同的哈希码:1596179075
但实际上它们是不同的实例:JdbcConnection@58a55449
和 JdbcConnection@5949eba8
:
2021-03-08 18:13:29.527 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 01: 1187972599, com.github.felipegutierrez.explore.spring.basics.dao.PersonDAO@46cf05f7
2021-03-08 18:13:29.527 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 01 JDBCConnection: 1596179075, com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection@58a55449
2021-03-08 18:13:29.529 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 02: 1187972599, com.github.felipegutierrez.explore.spring.basics.dao.PersonDAO@46cf05f7
2021-03-08 18:13:29.529 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 02 JDBCConnection: 1596179075, com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection@5949eba8
Spring在为目标原型对象创建代理时默认使用CGLIB。看起来这是一个 ,其中 hashCode()
和 equals
方法总是被特殊拦截器拦截,这些拦截器以不通过目标对象的方式进行比较。
如果切换到JDK interface-based proxies,需要JdbcConnection
实现接口,可以在接口中声明hashCode()
和equals()
:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES)
public class JdbcConnection implements IJdbcConnection {
...
}
interface IJdbcConnection {
@Override
boolean equals(Object obj);
@Override
int hashCode();
}
Spring 的 JdkDynamicAopProxy
将调用目标对象上的 hashCode()
和 equals()
。
并使用@Qualifier("jdbcConnection")
与PersonDAO
上的界面。
@Component
public class PersonDAO {
@Autowired
@Qualifier("jdbcConnection")
private IJdbcConnection jdbcConnection;
我有两个 bean PersonDAO
和 JdbcConnection
相互依赖。 PersonDAO
bean 是一个使用 @Component
的单例 bean。但是 JdbcConnection
是 prototype
并且因为它被注入到 PersonDAO
内部,所以我使用 proxyMode = ScopedProxyMode.TARGET_CLASS
来确保它是一个不同的实例(不是单例)。
import com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PersonDAO {
@Autowired
private JdbcConnection jdbcConnection;
public PersonDAO(JdbcConnection jdbcConnection) {
this.jdbcConnection = jdbcConnection;
}
public JdbcConnection getJdbcConnection() {
return jdbcConnection;
}
public void setJdbcConnection(JdbcConnection jdbcConnection) {
this.jdbcConnection = jdbcConnection;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class JdbcConnection {
private static final Logger LOGGER = LoggerFactory.getLogger(JdbcConnection.class);
public JdbcConnection() {
// LOGGER.info("This is my JdbcConnection that is not a singleton bean.");
}
}
当我打印它们的对象实例时,PersonDAO
是一个单例,而 JdbcConnection
是一个不同的实例。但是 JdbcConnection
的哈希码表明它是一个单例。这是为什么?
ApplicationContext applicationContext = SpringApplication.run(ExploreSpringApplication.class, args);
PersonDAO personDAO01 = applicationContext.getBean(PersonDAO.class);
PersonDAO personDAO02 = applicationContext.getBean(PersonDAO.class);
LOGGER.info("DAO 01: {}, {}, JDBCConnection: {}, {}", personDAO01, personDAO01.hashCode(), personDAO01.getJdbcConnection().hashCode(), personDAO01.getJdbcConnection());
LOGGER.info("DAO 02: {}, {}, JDBCConnection: {}, {}", personDAO02, personDAO02.hashCode(), personDAO02.getJdbcConnection().hashCode(), personDAO02.getJdbcConnection());
DAO01
和 DAO02
的输出 JDBCConnection
具有相同的哈希码:1596179075
但实际上它们是不同的实例:JdbcConnection@58a55449
和 JdbcConnection@5949eba8
:
2021-03-08 18:13:29.527 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 01: 1187972599, com.github.felipegutierrez.explore.spring.basics.dao.PersonDAO@46cf05f7
2021-03-08 18:13:29.527 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 01 JDBCConnection: 1596179075, com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection@58a55449
2021-03-08 18:13:29.529 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 02: 1187972599, com.github.felipegutierrez.explore.spring.basics.dao.PersonDAO@46cf05f7
2021-03-08 18:13:29.529 INFO 10329 --- [ main] c.g.f.e.spring.ExploreSpringApplication : DAO 02 JDBCConnection: 1596179075, com.github.felipegutierrez.explore.spring.basics.beans.JdbcConnection@5949eba8
Spring在为目标原型对象创建代理时默认使用CGLIB。看起来这是一个 hashCode()
和 equals
方法总是被特殊拦截器拦截,这些拦截器以不通过目标对象的方式进行比较。
如果切换到JDK interface-based proxies,需要JdbcConnection
实现接口,可以在接口中声明hashCode()
和equals()
:
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.INTERFACES)
public class JdbcConnection implements IJdbcConnection {
...
}
interface IJdbcConnection {
@Override
boolean equals(Object obj);
@Override
int hashCode();
}
Spring 的 JdkDynamicAopProxy
将调用目标对象上的 hashCode()
和 equals()
。
并使用@Qualifier("jdbcConnection")
与PersonDAO
上的界面。
@Component
public class PersonDAO {
@Autowired
@Qualifier("jdbcConnection")
private IJdbcConnection jdbcConnection;