Migrating Spring to Spring Boot

How to migrate from Spring to Spring Boot.

As more and more applications move towards platform-as-a-service(PAAS), many companies are looking to migrate from Spring MVC to Spring Boot.

Migrating from Spring MVC to Spring Boot involves quite a few changes affecting the entire application.

In this article I will explain my experience on migrating Spring MVC to Spring Boot in my current company and the issues I faced doing the same.

1. POM Changes

First thing to do, is the POM changes. We need to add the spring starter maven artifact and other spring related artifacts.

  1. Below Spring Starter artifact needs to be added.
<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.4.RELEASE</version>
	</parent>

Remove the Spring MVC related dependencies and based on your requirements add below dependencies.

  1. If it’s a REST API application, replace it with below dependency,
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
		      <exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
		   </exclusions>
		</dependency>
  1. If there is a log4J dependency in your previous Spring MVC application, add below dependency,
<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<dependency>
  1. If Spring security features are needed or present in old Spring MVC project, replace it with below dependency,
<dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-security</artifactId>
		</dependency>
  1. If hibernate was used, replace it with below maven dependency,
<dependency>
		   <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-jpa</artifactId>
		    <exclusions>
		        <exclusion>
		           <groupId>org.apache.tomcat</groupId>
		           <artifactId>tomcat-jdbc</artifactId>
		        </exclusion>
		     </exclusions>
		</dependency>
  1. If there was a SOAP service dependency, replace it with below dependency,
<dependency>
	      <groupId>org.springframework.boot</groupId>
	      <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
  1. If there was a quartz dependency, replace it with below dependency,
<dependency>
	      <groupId>org.springframework.boot</groupId>
	      <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2. Spring Initializer

Next, we need to create a new class at the root of the project. This class should extend SpringBootServletInitializer.

Also this class should be annotated with @SpringBootApplication. Example of this class is as shown below,

@SpringBootApplication
@ComponentScan("com.test")
public class TestRestInitializer extends SpringBootServletInitializer{
	public static void main(String[] args) {
		SpringApplication.run(TestRestInitializer .class, args);
	}

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

Usually this class is placed at project root. But if we don’t want to place this class at project root, we can make use of @ComponentScan and specify the package that we want Spring Boot to initialize.

3. Application YAML

Spring Boot allows us to define configuration details in a single file called application.yml or application.properties file. In my case I created application.yml file.

The application.yaml file needs to be added in project resources folder. As mentioned before, in this file we define configurations. If in our previous Spring MVC project we had a custom configuration class , we replace it with application.yaml . This application.yaml will take care of configurations in Spring Boot.

To read data from application.yaml. We can use below mentioned methods,

  1. Using Environment class,

    for example, Consider the below application.yml file,

    app:
        : MyApp
    

    To read these values in a class use below code,

    @Component
    public class TestClass  {
        private final Environment env;
        public TestClass(Environment env) {
            System.out.println(env.getProperty("app.name"));
        }
    }
    
  2. Using @Value. We can use @Value annotation to read dat from application.yml file. Here we are reading name variable from application.yml.

    @Value("${app.name}")
    private String name;
    
  3. Using @ConfigurationProperties

    We can use @ConfigurationProperties to read data from application.yml. Declare a class with @ConfigurationProperties annotation.

    Consider the application.yml. Here we have define a custom variable called app

    app:
        : MyApp
        1
    

Then declare Configuration Property class as below. Using the getter methods we can fetch the values from application.yml.

@ConfigurationProperties("app")
public class AppConfiguration{
	private String name;
	private int id;

	public String getName() {
	  return name;
	}
	public void setName(String name) {
	  this.name = name;
	}
	public int getId() {
	  return id;
	}
	public void setId(int id) {
	  this.id = id;
	}
	
}

As we can see the variable name should kept similar or same as the ones defined in the application.yml file. This allows Spring Boot to set the values for these variables names (Also declare set methods as well).

We can access these variable using the get Method. Example of same is as shown below

@Component
public class TestClass{
	@Autowired
	private AppConfiguration appConfiguration;
	
	public TestClass{
		System.out.println(appConfiguration.getName());
	}
}

3. DataSource vs application.yaml

In Spring Boot we can define our DB configurations in application.yml instead of defining it as a DataSource and referring it from external tomcat config file. Spring Boot comes with embedded tomcat and we can use the same by following the below steps,

  1. Remove Datasourcefrom code

    Remove class with annotation @EnableTransactionManagement. An example for the same is as shown below,

    @Configuration
    @EnableTransactionManagement
    @ComponentScan({ "com.test.configuration" })
    public class JPAConfiguration {
    
    	@Bean
    	public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
            entityManager.setDataSource(dataSource());
            entityManager.setPackagesToScan(new String[] {"com.test.entity" });
            entityManager.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            entityManager.setJpaProperties(hibernateProperties());
            return entityManager;
    	}
    
    	@Bean
    	@Primary
    	public JpaTransactionManager transactionManager() {
            JpaTransactionManager jpaTxManager = new JpaTransactionManager(entityManagerFactory().getObject());
            return jpaTxManager ;
    	}
    
    	@Bean
    	public AnnotationTransactionAspect annotationTransactionAspect() {
            AnnotationTransactionAspect aspectjTx = AnnotationTransactionAspect.aspectOf();
            aspectjTx.setTransactionManager(transactionManager());
            return aspectjTx;
    	}
    
    	@Bean(destroyMethod="")
    	public DataSource dataSource() {
            DataSource dataSource = null;
            JndiTemplate jndi = new JndiTemplate();
            try {
                dataSource = (DataSource) jndi.lookup("java:comp/env/test.datasource");
            } catch (NamingException e) {
            }
            return dataSource;
    	}
    
    	private Properties hibernateProperties() {
            Properties properties = new Properties();
            properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle8iDialect");
            properties.put("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
            properties.put("hibernate.current_session_context_class","thread");
            properties.put("hibernate.show_sql", "false");
            return properties;
    	}
    
    }
    
  2. And replace it with below mentioned YAML, substituting it with your configurations.

    spring:
    datasource:
        er-class-oracle:
            jdbc:
                driver: OracleDriver
         jdbc:oracle:thin:@test-test-test.test.exampleonline.net:1234/qwer
        name: testUser
        word: testPassword
    jpa: 
        base-platform: org.hibernate.dialect.Oracle12cDialect
        -sql: true
        rnate:
            ddl-auto: none
    

    ``

    In above configuration, we have defined the DB URL, username, password and DB dialect. Also I’ve added configuration to display the SQL statements executed by Hibernate.

4. Swagger Changes

For swagger to work in Spring Boot we need to do below changes,

  1. Migrate below dependencies

    <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${springfox.version}</version>
    
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${springfox.version}</version>
            </dependency>
    
  2. If Sprint Security is used, we need add below mentioned swagger URLs in exception list. Without these, Spring Boot Swagger URL won’t be able to open.

    "/configuration/ui" , "/configuration/security" , "/swagger-resources" , "/v2/api-docs" , "/cot/v1/serviceParameters"), "/swagger-ui.html" , "/webjars/springfox-swagger-ui"

5. Change Transactional Annotation

If JPA is used in Spring MVC , then @Transactional annotation might be used. While migrating from Spring to Spring Boot we need to replace Transactional import statements everywhere in the application.

Change import statement

javax.transaction.Transactional;

to

import org.springframework.transaction.annotation.Transactional;

Issues

1. JPA Mixed strategy Issues

In Spring Boot, parameterized and numerical parameters cannot be used in same statement.

For example

"select TEST1 FROM TEST WHERE TEST.TEST_ID=? AND TEXT.TEXT_NAME =:testName"

This HQL statement needs to be changed to,

"select TEST1 FROM TEST WHERE TEST.TEST_ID=:testId AND TEXT.TEXT_NAME =:testName"

We can’t use different JPA strategies in Spring Boot.

2. @Lazy annotation changes

For some entities we might get a cyclic error for some beans. To resolve this we would need to initialize the bean only when its actually required. To do this, mark these beans as @Lazy.

These are the main steps and challenges I faced when I migrated an application from Spring MVC to Spring Boot.

The number features of Spring MVC you have in your project, the more challenges you will face. Please comment below if there any any other issues other the once mentioned that you found while migrating from Spring to Spring Boot.