Spring MVC + separated front-end

Case study

Piotr Lewandowski

@piotlr

Back-end

  • Java 8
  • Spring 4 + Spring boot
  • Apache POI
  • Thymeleaf
  • Gradle

Front-end

 

  • bootstrap
  • require.js
  • npm
  • bower
  • gulp

Java 8

Java 7 way

Java 8 way

List<Human> humans = ...
Collections.sort(humans, new Comparator<Human>() {
    @Override
    public int compare(Human h1, Human h2)
        return h1.getName().compareTo(h2.getName());
    }
}
List<Human> humans = ...
Collections.sort(humans, (Human h1, Human h2) -> {
    h1.getName().compareTo(h2.getName())
});

Streams API

List<Person> persons = ...;
Stream tenPersonsOver18 = persons.stream()
                           .filter(p -> p.getAge() > 18)
                           .limit(10);
boolean isEmptyRow = csvRowEntries.stream()
                     .allMatch(str -> str.isEmpty());
if (isEmptyRow) {
    return null;
}

Streams API

// set id, location
employees.stream()
    .filter(e -> e.getId().equals(id))
    .findFirst()
    .ifPresent(e -> {
	e.setLocation(location);
    });
public List<String> getErrors() {
    return parseErrors.stream()
            .map(pe -> pe.getMessage())
	    .collect(Collectors.toList());
}

Streams in tests

// arrange
// ...
List<MissingEmployee> divergenceReport;
List<String> missingEmployees;

// act
divergenceReport = service.getDivergenceReport();
missingEmployees = divergenceReport.stream()
		    .map(MissingEmployee::getId)
		    .collect(Collectors.toList());

// assert
assertThat(missingEmployees.contains("Robert Smith"), is(true));

Confusions

  • Closure
  • Custom lambdas

Closures

int i = 0;

persons.forEach(person -> {
    if ( person == null) {
        i++; // Error: Variable must be final or effective final
    }   
    ...
});

Custom lamdas

public class Main {
  public static void runCalc( ??? calc) {
    // ???
  }

  public static void main(String[] args) {
    BigDecimal a = new BigDecimal();
    runCalc(a -> a.multiply(a));
  }
}

Custom lambdas

Scala

def runCalc(calc: (BigInt => BigInt)) {
  // ...
}

Java

  interface MyCalcLambda {
    BigInteger run(BigInteger input);
  }

  public static void runCalc(MyCalcLambda calc) {
    // ...
  }

  public static void main(String[] args) {
    runCalc(a -> a.multiply(a));
  }

Running custom lambdas

Scala

def runCalc(calc: (BigInt => BigInt)) {
  System.out.println(calc(10))
}

Java

public static void runCalc(MyCalcLambda calc) {
  System.out.println(calc(BigInteger.TEN));
}

Spring 4

with Spring boot

Long story short

Configuration

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
}
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application extends WebMvcConfigurerAdapter {

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

start.spring.io

Thymeleaf

  • Primitive
  • Maps
  • Lists
  • i18n config constans
  • loops
  • conditionals
  • formats
  • modules

What can we put?

What can we do?

<div th:replace="fragments/header :: header"> </div>
<table>
<thead>
    <tr>
        <th th:text="#{project.name}">Name</th>
        <th th:text="#{project.cost}">Cost</th>
        <th th:text="#{project.hours}">Hours</th>
    </tr>
</thead>
<tbody>
    <tr th:each="project : ${projects}">
        <td th:text="${project.projectId}">Project ID</td>
        <td th:text="${#numbers.formatDecimal(project.salary, 1, 'COMMA', 2, 'POINT')}">
            1000.0
        </td>
        <td th:text="${#numbers.formatDecimal(project.hours, 1, 'COMMA', 2, 'POINT')}">
            353
        </td>
    </tr>
</tbody>
</table>

HTML5 way

<table>
    <!--/*/ <th-block data-th-each="user : ${users}"> /*/-->
    <tr>
        <td data-th-text="${user.login}">...</td>
        <td data-th-text="${user.name}">...</td>
    </tr>
    <tr>
        <td data-th-text="${user.address}">...</td>
    </tr>
    <!--/*/ </th-block> /*/-->
</table>

Gradle

Worries

  • Do I need to learn groovy?
  • Can I still use my dependencies?
  • If my IDE supports gradle?

gradle hello world

apply plugin: 'java'

sourceCompatibility = 1.8

jar {
    baseName = 'demo'
    version = '0.0.1-SNAPSHOT'
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}
repositories {
    maven {
        credentials {
            username 'user'
            password 'password'
        }
        url "http://10.10.0.1/your-maven-repo"
    }
}

Custom repostiories

So why gradle?

  • Clean
  • Efficient
  • Easily expandable with plugins

Front-end

Building front-end

  • Modularity
  • Dependency managment
  • Production-ready
  • Tests

Cons

  • Many, many tools
  • Frequent changes

Pros

  • Many, many tools
  • Frequent changes

Front-end structure

src
├── css
├── js
dist
├── lib
├── css
├── js
gulpfile.js
bower.json
package.json

Build tools demo

Building process

What are doing when we type `gradle build`

gradle build

  1. Compille front-end
    1. Download tools with NPM
    2. Download dependencies with bower
    3. Build front-end with gulp
    4. Pack into JAR
  2. Compile the rest with front-end as a dependency

Thanks

Java 8 + Spring boot

By Piotr Lewandowski

Java 8 + Spring boot

Case study

  • 662