如何解决如何检查在 Spring 中使用 SCOPE_PROTOTYPE 和 ScopedProxyMode.TARGET_CLASS 的对象实例是否不同?
我有两个相互依赖的 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: {},personDAO02,personDAO02.hashCode(),personDAO02.getJdbcConnection().hashCode(),personDAO02.getJdbcConnection());
JDBCConnection
和 DAO01
的输出 DAO02
具有相同的哈希码: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。似乎这是一个 limitation by 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;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。