Upgrade to Pro — share decks privately, control downloads, hide ads and more …

De zéro à héros avec Spring Boot

De zéro à héros avec Spring Boot

Session université à Devoxx France 2015 avec Brian Clozel (@brianclozel)

Examples: https://github.com/snicoll-demos/spring-boot-uni-devoxxfr

Stéphane Nicoll

April 08, 2015
Tweet

More Decks by Stéphane Nicoll

Other Decks in Programming

Transcript

  1. @snicoll @brianclozel
    #springboot
    De zéro à héros avec
    Spring Boot
    Stéphane Nicoll & Brian Clozel
    Spring team
    Pivotal

    View full-size slide

  2. @snicoll @brianclozel
    #springboot
    Hello!
    @snicoll @brianclozel
    [snicoll,bclozel]@pivotal.io

    View full-size slide

  3. @snicoll @brianclozel
    #springboot
    Agenda
    • Part #1: Booting sequence
    • [slides] a short intro to Spring Boot
    • [live coding] building an app from scratch
    • — ☕️ break —
    • Part #2: More on Spring Boot
    • Boot autoconfig and starters
    • Externalized configuration

    View full-size slide

  4. Part #1: Booting sequence
    let’s work one hour on a Spring Boot application

    View full-size slide

  5. @snicoll @brianclozel
    #springboot
    Spring Boot lets you pair-program with the Spring team.

    Josh Long, @starbuxman

    View full-size slide

  6. @snicoll @brianclozel
    #springboot
    Introduction to Spring Boot
    • Single point of focus
    • Getting started quickly with Spring
    • Common non-functional requirements for a "real" application
    • Exposes a lot of useful features by default
    • Gets out of the way quickly if you want to change defaults
    • An opportunity for Spring to be opinionated

    View full-size slide

  7. @snicoll @brianclozel
    #springboot
    ಠ_ಠ Spring Boot is NOT
    • A prototyping tool
    • Only for embedded container apps
    • Sub-par Spring experience
    • For Spring beginners only

    View full-size slide

  8. @snicoll @brianclozel
    #springboot
    Installation
    • Requirements:
    • JDK6+
    • Maven 3.2+ / Gradle 1.12+
    • Tools:
    • Spring Tool Suite
    • IntelliJ IDEA
    • spring CLI (from https://start.spring.io or gvm)

    View full-size slide

  9. @snicoll @brianclozel
    #springboot
    Getting started quickly
    @RestController
    class HomeController {
    @RequestMapping("/")
    String home() {
    'Hello Devoxx France'
    }
    }
    $ spring run app.groovy

    View full-size slide

  10. @snicoll @brianclozel
    #springboot
    What just happened?
    import ...
    @Grab(“org.springframework.boot:spring-boot-starter-web:1.2.3.RELEASE")
    @EnableAutoConfiguration
    @RestController
    class HomeController {
    @RequestMapping("/")
    String home() {
    "Hello Devoxx France";
    }
    public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
    }
    }

    View full-size slide

  11. @snicoll @brianclozel
    #springboot
    Let’s write some code!

    View full-size slide

  12. @snicoll @brianclozel
    #springboot
    Now with Java
    @RestController
    public class HomeController {
    @Value("${conference.name:devoxx}")
    private String conference;
    @RequestMapping("/")
    public String home() {
    return "Hello " + conference;
    }
    }
    curl http://localhost:8080/

    View full-size slide

  13. @snicoll @brianclozel
    #springboot
    First integration test
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = DemoApplication.class)
    @WebIntegrationTest(randomPort = true)
    public class HomeControllerIntegrationTest {
    @Value("${local.server.port}")
    private int port;
    @Test
    public void runAndInvokeHome() {
    String url = "http://localhost:" + port + "/";
    String body = new RestTemplate()
    .getForObject(url, String.class);
    assertThat(body, is("Hello devoxx"));
    }
    }

    View full-size slide

  14. @snicoll @brianclozel
    #springboot
    Add spring-data-jpa

    org.springframework.boot
    spring-boot-starter-data-jpa


    com.h2database
    h2
    runtime

    View full-size slide

  15. @snicoll @brianclozel
    #springboot
    Add a Speaker Entity
    @Entity
    public class Speaker {
    @GeneratedValue
    @Id
    private Long id;
    private String firstName;
    private String lastName;
    private String twitter;
    @Column(columnDefinition = "TEXT")
    private String bio;
    public Speaker(String firstName, String lastName, String twitter) {
    // initialize attributes
    }
    // getters and setters
    }

    View full-size slide

  16. @snicoll @brianclozel
    #springboot
    Add a Speaker Repository
    public interface SpeakerRepository
    extends CrudRepository {
    Speaker findByTwitter(String twitter);
    Collection findByLastName(String lastName);
    }

    View full-size slide

  17. @snicoll @brianclozel
    #springboot
    Test that repository
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = DemoApplication.class)
    public class SpeakerRepositoryTest {
    @Autowired
    private SpeakerRepository speakerRepository;
    @Test
    public void testFindByTwitter() throws Exception {
    Speaker stephane = speakerRepository.save(
    new Speaker("Stephane", "Nicoll", "snicoll"));
    assertThat(speakerRepository.findByTwitter("snicoll").getId(),
    is(stephane.getId()));
    }
    }

    View full-size slide

  18. @snicoll @brianclozel
    #springboot
    Add spring-data-rest

    org.springframework.boot
    spring-boot-starter-data-rest

    View full-size slide

  19. @snicoll @brianclozel
    #springboot
    RESTful repository
    public interface SpeakerRepository extends CrudRepository {
    @RestResource(path = "by-twitter")
    Speaker findByTwitter(@Param("id") String twitter);
    Collection findByLastName(@Param("name") String lastName);
    }

    View full-size slide

  20. @snicoll @brianclozel
    #springboot
    Add spring-security

    org.springframework.boot
    spring-boot-starter-security

    View full-size slide

  21. @snicoll @brianclozel
    #springboot
    Spring Security config
    @Configuration
    public class SecurityConfig extends GlobalAuthenticationConfigurerAdapter {
    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
    .withUser("hero").password("hero").roles("HERO", "USER").and()
    .withUser("user").password("user").roles("USER");
    }
    }

    View full-size slide

  22. @snicoll @brianclozel
    #springboot
    Fix our web test
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = DemoApplication.class)
    @WebIntegrationTest(randomPort = true)
    public class HomeControllerIntegrationTest {
    @Value("${local.server.port}")
    private int port;
    @Test
    public void runAndInvokeHome() {
    String url = "http://localhost:" + port + "/";
    String body = new TestRestTemplate("user", "user")
    .getForObject(url, String.class);
    assertThat(body, is("Hello devoxx"));
    }
    }

    View full-size slide

  23. @snicoll @brianclozel
    #springboot
    Enabling @Secured
    @Configuration
    @EnableGlobalMethodSecurity(securedEnabled = true)
    public class SecurityConfig extends GlobalAuthenticationConfigurerAdapter {
    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
    .withUser("hero").password("hero").roles("HERO", "USER").and()
    .withUser("user").password("user").roles("USER");
    }
    }

    View full-size slide

  24. @snicoll @brianclozel
    #springboot
    Securing an endpoint
    @RestController
    public class HomeController {
    @Value("${conference.name:devoxx}")
    private String conference;
    @RequestMapping("/")
    @Secured("ROLE_HERO")
    public String home() {
    return "Hello " + conference;
    }
    }

    View full-size slide

  25. @snicoll @brianclozel
    #springboot
    Add spring-boot-actuator

    org.springframework.boot
    spring-boot-starter-actuator

    View full-size slide

  26. @snicoll @brianclozel
    #springboot
    Adding a Health Indicator
    @Bean
    public HealthIndicator devoxxHealthIndicator() {
    return () -> {
    if (new Random().nextBoolean()) {
    return Health.up().build();
    }
    else {
    return Health.down().withDetail("Boooo",42).build();
    }
    };
    }

    View full-size slide

  27. @snicoll @brianclozel
    #springboot
    Add remote shell

    org.springframework.boot
    spring-boot-remote-shell

    View full-size slide

  28. @snicoll @brianclozel
    #springboot
    Best experience with PaaS
    • Spring Boot features get a lot done for 12factor.net
    • PaaS friendly: fast startup, devops oriented
    $ mvn package
    $ cf push devoxx -p target/devoxx-0.0.1-SNAPSHOT.jar
    $ cf scale devoxx -i 4

    View full-size slide

  29. Part #2: More on Boot
    Explaining the key concepts behind Boot
    and how to use them in your application

    View full-size slide

  30. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    Not only web Apps

    View full-size slide

  31. @snicoll @brianclozel
    #springboot
    All types of apps
    • web
    • command line
    • batch
    • redis/gemfire/jpa/elasticsearch
    • integration
    • JMS, AMQP
    • etc

    View full-size slide

  32. @snicoll @brianclozel
    #springboot
    Quick example

    View full-size slide

  33. @snicoll @brianclozel
    #springboot
    Command Line apps
    @Component
    public class Startup implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
    System.out.println("Hello World!");
    }
    }

    View full-size slide

  34. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    AutoConfiguration and Starters

    View full-size slide

  35. @snicoll @brianclozel
    #springboot
    Starter POMs

    org.springframework.boot
    spring-boot-starter-web

    • standard POM / gradle files: define dependencies
    • many available: batch, integration, web, ampq…
    • starter data-jpa = spring-data-jpa + JSR-303 + hibernate
    compile("org.springframework.boot:spring-boot-starter-web")

    View full-size slide

  36. @snicoll @brianclozel
    #springboot
    @EnableAutoConfiguration
    • attempts to autoconfigure your app
    • backs off as you define your own beans
    • Regular @Configuration classes, @Conditional* annotations
    @Configuration
    @ComponentScan
    @EnableAutoConfiguration
    public class MyApplication {
    }
    @SpringBootApplication
    public class MyApplication {
    }

    View full-size slide

  37. @snicoll @brianclozel
    #springboot
    Spring 4 @Conditional
    @Configuration
    @Conditional(CustomCondition.class)
    public class AppConfiguration {
    // @Bean methods
    }
    // defined in Boot
    @ConditionalOnClass(ObjectMapper.class)
    @ConditionalOnMissingBean("aBeanName")

    View full-size slide

  38. @snicoll @brianclozel
    #springboot
    Writing your own starter
    • Add support for X in Boot with a PR!
    • Distribute a client lib in your company
    • Standardize usage of component X in a platform
    • [your use case here]

    View full-size slide

  39. @snicoll @brianclozel
    #springboot
    Let’s write some code!

    View full-size slide

  40. @snicoll @brianclozel
    #springboot
    New autoconfig project

    org.springframework.boot
    spring-boot-autoconfigure

    • Create a new hello-service-auto-configuration project
    • Only one mandatory dependency
    • Should contain specific dependencies and autoconfiguration
    classes

    View full-size slide

  41. @snicoll @brianclozel
    #springboot
    Add custom service interface
    public interface HelloService {
    String sayHello();
    }
    • This is the part that we’re trying to auto-configure
    • In a typical use case, this interface comes from a 3rd party lib

    View full-size slide

  42. @snicoll @brianclozel
    #springboot
    Create a default impl.
    public class ConsoleHelloService implements HelloService {
    public String sayHello() {
    System.out.println("Hello Console");
    }
    }
    • This default implementation will be shipped with the autoconfig
    project but should not be used if the application provides one.

    View full-size slide

  43. @snicoll @brianclozel
    #springboot
    AutoConfig class example
    @Configuration
    @ConditionalOnClass(HelloService.class)
    public class HelloServiceAutoConfiguration {
    @ConditionalOnMissingBean
    @Bean
    public HelloService helloService() {
    return new ConsoleHelloService();
    }
    }

    View full-size slide

  44. @snicoll @brianclozel
    #springboot
    Declare AutoConfigs
    META-INF/spring.factories:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    demo.hello.HelloServiceAutoConfiguration
    // one can order AutoConfigurations
    // with those annotations
    @AutoConfigureBefore
    @AutoConfigureAfter

    View full-size slide

  45. @snicoll @brianclozel
    #springboot
    Back to main project
    • Add our autoconfig project as a dependency in our main
    project

    org.test.devoxx
    hello-service-auto-configuration
    ...

    View full-size slide

  46. @snicoll @brianclozel
    #springboot
    Replace the Startup class
    @Component
    public class Startup implements CommandLineRunner {
    @Autowired private HelloService helloService;
    @Override
    public void run(String... args) throws Exception {
    helloService.sayHello();
    }
    }
    • Our command line app now uses a Hello Service
    • running this will use the default implementation

    View full-size slide

  47. @snicoll @brianclozel
    #springboot
    Override default impl.
    @Bean

    public HelloService helloService() {

    return () -> 

    LoggerFactory.getLogger(DemoApplication.class)

    .info("Hello from logs");

    }
    • Add a @Bean definition in our DemoApplication class
    • We provide our own impl, so the default one won’t be created

    View full-size slide

  48. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    Externalized Configuration

    View full-size slide

  49. @snicoll @brianclozel
    #springboot
    Externalized Configuration
    • Goal: same code, different environments
    • properties and YAML formats
    • Dozens of out-of-the-box configuration keys
    • Supports inheritance/overriding
    • Type safe configuration with @ConfigurationProperties
    • IDE support with configuration metadata

    View full-size slide

  50. @snicoll @brianclozel
    #springboot
    Adding properties
    @RestController
    public class HomeController {
    @Value("${conference.name:devoxx}")
    private String conference;
    //...
    }
    # application.yml example
    conference:
    name: devoxx france

    View full-size slide

  51. @snicoll @brianclozel
    #springboot
    Per-Profile config values
    # application.yml - default profile
    server:
    address: 192.168.1.100
    ---
    spring:
    profiles: development
    server:
    address: 127.0.0.1
    ---
    spring:
    profiles: production
    server:
    address: 192.168.1.120

    View full-size slide

  52. @snicoll @brianclozel
    #springboot
    Per-Profile config values
    # application.properties
    server.address=192.168.1.100
    # application-development.properties
    server.address=127.0.0.1
    # application-production.properties
    server.address=192.168.1.120

    View full-size slide

  53. @snicoll @brianclozel
    #springboot
    Configuration priority
    1.Command line arguments.
    2.JNDI attributes from java:comp/env.
    3.Java System properties (System.getProperties()).
    4.OS environment variables.
    5.application-{profile}.properties or application-{profile}.yml
    6.application.properties or application.yml
    7.@PropertySource annotations on your @Configuration classes.
    8.Default properties (specified using SpringApplication.setDefaultProperties).

    View full-size slide

  54. @snicoll @brianclozel
    #springboot
    Logging
    • Logback, Log4J, Log4J2, java.util.Logging
    • Logback with colored output
    • Default is INFO (use « -- debug » on the command line)
    • CONSOLE by default (can configure a file output)
    logging:
    file: logs/application_log
    level:
    ROOT: WARN
    org.example: INFO
    org.example.acme: DEBUG

    View full-size slide

  55. @snicoll @brianclozel
    #springboot
    Let’s write some code!

    View full-size slide

  56. @snicoll @brianclozel
    #springboot
    Update our starter
    public class ConsoleHelloService implements HelloService {
    @Value("${hello.target:console}")
    private String target;
    public String sayHello() {
    System.out.println("Hello " + this.target);
    }
    }
    • Delete the @Bean HelloService in the application
    • Update the default one with a property attribute

    View full-size slide

  57. @snicoll @brianclozel
    #springboot
    Override that prop value
    # application.yml example
    hello:
    name: devoxx france
    java -jar devoxx-0.0.1-SNAPSHOT.jar \
    --hello.name=Foo
    • set that property value with a application.yml key
    • or a command line argument!

    View full-size slide

  58. @snicoll @brianclozel
    #springboot
    Type safe properties
    @ConfigurationProperties("hello")
    public class HelloProperties {
    private String prefix = "Hello ";
    @NotNull
    private String target;
    // getters and setters
    }

    View full-size slide

  59. @snicoll @brianclozel
    #springboot
    Enable Config Properties
    @EnableConfigurationProperties(HelloProperties.class)
    @Configuration
    @ConditionalOnClass(HelloService.class)
    public class HelloServiceAutoConfiguration {

    View full-size slide

  60. @snicoll @brianclozel
    #springboot
    Update Starter impl
    public class ConsoleHelloService implements HelloService {
    @Autowired
    private HelloProperties properties;
    @Override
    public void run(String... strings) throws Exception {
    System.out.println(properties.getPrefix() +
    properties.getTarget());
    }

    View full-size slide

  61. @snicoll @brianclozel
    #springboot
    Generate config metadata

    org.springframework.boot
    spring-boot-configuration-processor
    true

    • annotation processor that generates meta-data
    • uses Javadoc to build keys descriptions
    • default values detected
    • manual declaration allowed

    View full-size slide

  62. @snicoll @brianclozel
    #springboot
    Document properties
    @ConfigurationProperties("hello")
    public class HelloProperties {
    /**
    * Prefix of welcome message.
    */
    private String prefix = "Hello ";
    /**
    * Target of welcome message.
    */
    @NotNull
    private String target;
    // getters and setters
    }

    View full-size slide

  63. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    Focus on Web Applications

    View full-size slide

  64. @snicoll @brianclozel
    #springboot
    Serving static resources
    # in classpath
    classpath:/static/**
    classpath:/public/**
    classpath:/resources/**
    classpath:/META-INF/resources/**
    # in WAR files
    src/main/webapp/
    static/
    public/

    View full-size slide

  65. @snicoll @brianclozel
    #springboot
    Template Engines
    • Thymeleaf
    • Groovy Markup Template
    • JMustache
    • Freemarker
    • Velocity

    View full-size slide

  66. @snicoll @brianclozel
    #springboot
    Example: Thymeleaf
    • Templates picked up from classpath:/templates
    • Useful configuration keys:
    • spring.thymeleaf.prefix
    • spring.thymeleaf.cache
    • Backs off when creating custom Beans

    View full-size slide

  67. @snicoll @brianclozel
    #springboot
    Error Handling
    • /error handles errors in a sensible way (HTML, JSON, etc)
    • Customize or extend with ErrorAttributes or an « error »
    view
    • Create dedicated error pages with

    EmbeddedServletContainerCustomizer

    View full-size slide

  68. @snicoll @brianclozel
    #springboot
    Let’s write some code!

    View full-size slide

  69. @snicoll @brianclozel
    #springboot
    Create a SpeakerController
    @Controller
    public class SpeakerController {
    private final SpeakerRepository speakerRepository;
    @Autowired
    public SpeakerController(SpeakerRepository speakerRepository) {
    this.speakerRepository = speakerRepository;
    }
    @RequestMapping("/ui/speakers/{id}")
    public String show(@PathVariable Long id, ModelMap model) {
    Speaker speaker = speakerRepository.findOne(id);
    if (speaker == null) {
    throw new SpeakerNotFoundException();
    }
    model.put("speaker", speaker);
    return "speakers/show";
    }
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public static class SpeakerNotFoundException extends RuntimeException {
    }
    }

    View full-size slide

  70. @snicoll @brianclozel
    #springboot
    Create a template


    View speaker



    Stephane Nicoll

    Sample Biography.


    @snicoll




    View full-size slide

  71. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    Deploying Boot Apps

    View full-size slide

  72. @snicoll @brianclozel
    #springboot
    Creating a WAR file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    war

    View full-size slide

  73. @snicoll @brianclozel
    #springboot
    Embedded server config
    • Registering Filters with @Bean methods
    • using application properties, configure:
    • ports
    • SSL
    • compression
    • and more
    • Configure Connectors with Java API

    View full-size slide

  74. @snicoll @brianclozel
    #springboot
    Use Undertow instead

    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-tomcat




    org.springframework.boot
    spring-boot-starter-undertow

    View full-size slide

  75. @snicoll @brianclozel
    #springboot
    http://spring.io powered by
    github.com/spring-io/sagan

    View full-size slide

  76. @snicoll @brianclozel
    #springboot
    Microservices?
    SPRING
    Toolbox for building Microservices/Cloud native apps
    • Distributed configuration
    • service registration and discovery
    • Load-balancing, circuit breakers and more!

    View full-size slide

  77. @snicoll @brianclozel
    #springboot
    Spring at Devoxx France
    • Hands-on Lab « Microservices Bootcamp »

    13:30-16:30 - Paris 221M

    with Josh Long and Sébastien Deleuze

    • Modern Enterprise Java architectures with Spring 4.1

    Thursday 13:00-13:50 - Amphi bleu

    with Juergen Hoeller and Stéphane Nicoll

    • The Spring BOF

    Thursday 19:30-20:30 - Neuilly 251

    View full-size slide

  78. @snicoll @brianclozel
    #springboot
    Spring conferences
    http://springio.net
    http://springone2gx.com

    View full-size slide

  79. @YourTwitterHandle
    @YourTwitterHandle
    @snicoll @brianclozel
    #springboot
    Q & A

    View full-size slide