简体   繁体   中英

how to check if object instances using SCOPE_PROTOTYPE and ScopedProxyMode.TARGET_CLASS in Spring are different?

I have two beans PersonDAO and JdbcConnection with dependency on each other. The PersonDAO bean is a singleton bean using @Component . But the JdbcConnection is a prototype and because it is injected inside the PersonDAO I am using proxyMode = ScopedProxyMode.TARGET_CLASS to ensure that it is a different instance (not a singleton).

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.");
    }

}

When I print the object instance of them the PersonDAO is a singleton and the JdbcConnection is a different instance. But the hashcode of the JdbcConnection says that it is a singleton. Why is that?

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());

output JDBCConnection of DAO01 and DAO02 has the same hashcode: 1596179075 but actually they are different instances: JdbcConnection@58a55449 and 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 uses CGLIB by default when creating a proxy for the target prototype object. It seems this is a limitation by CGLIB where the hashCode() and equals methods are always intercepted by special interceptors that do comparison in such a way that does not go through the target object.

If you switch to JDK interface-based proxies , which requires that JdbcConnection implements an interface, you can declare hashCode() and equals() in the interface:

@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's JdkDynamicAopProxy will then call the hashCode() and equals() on the target object.

and use the @Qualifier("jdbcConnection") with the interface on the PersonDAO .

@Component
public class PersonDAO {

    @Autowired
    @Qualifier("jdbcConnection")
    private IJdbcConnection jdbcConnection;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM