如何检查在 Spring 中使用 SCOPE_PROTOTYPE 和 ScopedProxyMode.TARGET_CLASS 的对象实例是否不同?

问题描述

我有两个相互依赖的 bean PersonDAOJdbcConnectionPersonDAO 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());

JDBCConnectionDAO01 的输出 DAO02 具有相同的哈希码:1596179075 但实际上它们是不同的实例:JdbcConnection@58a55449JdbcConnection@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;

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...