Druudik
Druudik

Reputation: 1015

Dependency injection Spring

I am building a web application using spring MVC which is connected to the database. This is part of my bean config file.

 <!-- Define Database DataSource / connection pool -->
    <bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
          destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java_task?useSSL=false" />
        <property name="user" value="me" />
        <property name="password" value="me" /> 

        <!-- these are connection pool properties for C3P0 -->
        <property name="minPoolSize" value="5" />
        <property name="maxPoolSize" value="20" />
        <property name="maxIdleTime" value="30000" />
    </bean>  

    <!-- Define Hibernate session factory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource" />
        <property name="packagesToScan" value="com.javatask.entity" />
        <property name="hibernateProperties">
           <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
              <prop key="hibernate.show_sql">true</prop>
           </props>
        </property>
   </bean>   

As you can see my data source is defined in a hard way. In code I am using @Autowired annotation to inject sessionFactory. But I would like to inject sessionFactory (jdbc, username, password etc.) with data which I will obtain from user during run time. For example he will write me those data to the textfeild and then I will create sessionFactory (based on his data) which will be connected to his database.

I was looking for an answer but unluckily I have not found anything that would fit to this problem.

Upvotes: 0

Views: 204

Answers (2)

There are multiple ways that spring bean definition can be changed in run time. One way is to refresh the Spring Application Context in run time which would have the bean definition specific to the functionality rather than all bean definition.

((ConfigurableApplicationContext)applicationContext).refresh();

Let me explain using one simple use case:

  • Create a PropertyBean which will load value from external properties file.
  • Create PropertiesApplicationContext which would have only PropertyBean definition config rather than all beans belongs the application.
  • Use ConfigurableApplicationContext refresh() method to reload ProperiesBean definition in run time.

More details about below example code snippet:

  • Web Application should display a Property Value from a bean which will read the value from a external property file.
  • Property value will change anytime in external properties file.
  • Web Application should not restarted.
  • Here, bean definition should be changed in run time.

Spring Boot Application Code:

@SpringBootApplication(scanBasePackages = {"vijay.controller","vijay.configuration"})
public class SpringContextRefreshApplication extends SpringBootServletInitializer{

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(SpringContextRefreshApplication.class); 
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringContextRefreshApplication.class, args);
    }
}

MVC Configuration:

package vijay.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

/**
 * MVC Configuration
 * @author Vijay
 */
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter{

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        registry.viewResolver(resolver);
    }    
}

Custom (Property) Application Context Aware:

package vijay.configuration;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * PropertyApplicationContext Class to load Property Configuration XML.
 * @author Vijay
 */
public class PropertyApplicationContext implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    private static PropertyApplicationContext propertyApplicationContext;

    private PropertyApplicationContext(){
        applicationContext = new ClassPathXmlApplicationContext("property-config.xml");
    }

    @Override
    public void setApplicationContext(ApplicationContext ac) throws BeansException {
        if(applicationContext == null){
            this.applicationContext = ac;
        }
    }

    public ApplicationContext getApplicationContext(){
        return applicationContext;
    }

    public static PropertyApplicationContext getInstance(){
        if(propertyApplicationContext == null){
            propertyApplicationContext = new PropertyApplicationContext();
        }
        return propertyApplicationContext;
    }

}

MVC Controller:

package vijay.controller;

import vijay.beans.PropertyDto;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import vijay.configuration.PropertyApplicationContext;

/**
 * Property List Controller.
 * @author Vijay
 */
@Controller
public class PropertyController {   

    @RequestMapping("/")
    public String testController(){
        System.out.println("vijay.controller.PropertyController.testController()");
        return "sample";
    }


    @RequestMapping(path = "/getProperties") 
    public String testPropertiesController(ModelMap modelMap){
        ApplicationContext applicationContext = PropertyApplicationContext.getInstance().getApplicationContext();
        PropertyDto propertyDto = (PropertyDto) applicationContext.getBean("propertyBean");
        System.out.println("vijay.controller.PropertyController.testPropertiesController() " + propertyDto);
        modelMap.addAttribute("message", propertyDto.getKey());
        return "sample";
    }

    /**
     * Method will refresh ApplicationContext
     * @param modelMap as ModelMap
     * @return viewName as String
     */
    @RequestMapping(path = "/refreshProperties") 
    public String refreshBean(ModelMap modelMap){
        ApplicationContext applicationContext = PropertyApplicationContext.getInstance().getApplicationContext();
        // Refresh Application Context
        ((ConfigurableApplicationContext)applicationContext).refresh();
        PropertyDto propertyDto = (PropertyDto) applicationContext.getBean("propertyBean");
        System.out.println("vijay.controller.PropertyController.refreshBean()" + propertyDto);
        modelMap.addAttribute("message", propertyDto.getKey());
        return "sample";
    }

}

Spring Bean (Property-config.xml) definition File:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" >

    <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="file:C://sample.properties" />
    </bean>

    <context:annotation-config/>
    <context:component-scan base-package="vijay.beans"/>

</beans>

View JSP (sample.jsp):

<!DOCTYPE html>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<html lang="en">
<body>
    <div>
        <div>
            <h1>Spring Boot JSP Example</h1>
            <h2>Property Value :: ${message} !</h2>
        </div>
    </div>
</body>
</html>

Here, MVC Controller has /refreshProperties which will refresh the PropertiesBean defined in PropertiesApplicationContext. As properties-config.xml the only bean definition defined in PropertiesApplicationContext, propertiesBean alone will be refreshed and no other bean definition will be refreshed.

Similar way, you can create Application Context by extending ApplicationContextAware which will have bean definition of sessionFactory. You can get SessionFactory wherever required from this context and refresh the context wherever required.

This is one way to achieve the bean definition change in run time.

Upvotes: 1

ibra
ibra

Reputation: 606

Did you try this [Change SessionFactory datasource jdbcurl late in runtime] (Change SessionFactory datasource jdbcurl late in runtime)?

Upvotes: 0

Related Questions