简体   繁体   中英

Spring boot: Managing bean scope “manually” for dynamically creating beans at runtime with differing states. Is there a problem with our solution?

So we essentially want to have a generic JDBC repo which can be instantiated with different database details at run-time. Currently the setup is essentially " Get a GenericRepo bean with the details x, y, z. If a bean exists with those details, use that bean instead and use already existing one. If it doesn't exist, create it."

This seems like the sort of things custom scopes could be used for, but is there anything inherently "wrong" with our solution that might cause issues?

This is a web app that may have multiple people using the GenericRepo at once.

The Repo

@Component
public class GenericDatabaseDAO {

JdbcTemplate data;

public GenericDatabaseDAO() {}

public GenericDatabaseDAO(String username, String password, String url, String driver) {
    this.data = new JdbcTemplate(
                    DataSourceBuilder.create()
                    .username(username)
                    .password(password) 
                    .url(url)
                    .driverClassName(driver)
                    .build()
                );
}

public GenericDatabaseTableModel runQuery(String query) {

    return new GenericDatabaseTableModel(data.queryForList(query));
}



@Service
public static class DAOService implements ApplicationContextAware {

    ApplicationContext context;
    AutowireCapableBeanFactory factory;

    HashMap<String,GenericDatabaseDAO> beans = new HashMap<String,GenericDatabaseDAO>();

    public GenericDatabaseDAO getDAO(String username, String password, String url, String driver) {

        GenericDatabaseDAO dao = beans.get(username+'@'+url);

        if(dao == null) {

            dao = new GenericDatabaseDAO(username, password, url, driver);
            factory.autowireBean(dao);
            factory.initializeBean(dao, username+'@'+url);

            beans.put(username+'@'+url, dao);
        }

        return dao;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
        this.factory = context.getAutowireCapableBeanFactory();
    }

}
 }

How we use the repo

//service class for running sql statements
@Autowired 
GenericDatabaseDAO.DAOService factory;


public void runSqlOnDB(String username, String password, String url, String driver){
GenericDatabaseDAO gdbdao = factory.getDAO(username,password,url,driver);
gbdao.runQuery("sql here");

 }

What you probably need to do, is look into @Configuration annotations, so you can create new beans.

@Bean annotation makes sure that what is returned is a Spring bean. Finally, the @Scope annotation tells Spring to create a new instance each time.

What I did not add, but which you can easily investigate is an @Cacheable annotation to avoid multiple instances of a certain bean. Note: for caching you need some extra Spring jars in your Maven or Gradle. You also need @EnableCaching .

@Configuration
public DaoFactory {

   @Bean
   @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
   // @Cacheable here !
   public getDao(String name, String password, String url, String driver) {
     return new GenericDatabaseDAO(name, password, url, driver);
   }

}

If you want to use it, do something like

@Autowired DaoFactory daoFactory;
... 

GenericDatabaseDAO dao = daoFactory.getDao("user", "pwd", "url", "driver");

Sorry, I used @Autowired because it made the example simpler :)

Final note: you do not need an @Component annotation on GenericDatabaseDAO class.

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