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

The Evolution of Spring Framework from Dependency Injection to Microservices

The Evolution of Spring Framework from Dependency Injection to Microservices

This presentation takes the audience on Spring Framework‘s journey from dependency injection to providing support for Microservices.

* What is Dependency Injection?

The story arc begins with Dependency Injection (DI) and proceeds backwards in time from nowadays to the dark era of early Java EE when EJBs where counterproductive to use.

* Spring’s Evolution

With setting the scene and depicting the circumstances of Spring Framework’s genesis, we move on to Spring Boot.

* Monolith

After arriving at present day’s status of the Spring Ecosystem, we look into why teams – having such strong tools like Java, Spring (and other frameworks) – ended up with creating very large monolithic applications.

* Microservices

We discuss briefly what the Microservices Architecture is and why messaging as an abstract form of communication between individual services make developers and architects think about the very nature of that communication.

* Spring Integration

Enterprise Integration Patterns are briefly introduced as the basis of Spring Integration. After that Spring Integration is used to decouple three application components in a way that they don’t know about each other, but they’re exchanging messages instead.

* Spring Cloud Stream

In the last part Spring Cloud Stream is introduced to the audience as a combination of Spring Integration and Spring Boot. We evolve our sample application further into a set of message-driven Microservices.

Laszlo Csontos

December 18, 2018
Tweet

Other Decks in Programming

Transcript

  1. Nice to Meet You László Csontos • Ex-Liferay • Freelance

    ◦ Software Engineer ◦ Consultant • Chiefly Back-End Development ◦ Java, Spring ◦ Microservices ◦ Performance Tuning • Blogger https://craftingjava.com/ • Get in Touch ◦ @craftingjava ◦ [email protected] ◦ https://speakerdeck.com/craftingjava
  2. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  3. DI - Don't call us, we'll call you @Service class

    UserService { @Autowired UserRepository ur; @Autowired EmailService es; @Autowired AuditService as; User signUp(String email) { User user = ur.save(User.of(email)); es.sendConfirmation(user); as.logSignup(user); return user; } } • Also known as ◦ Inversion Of Control — IoC ◦ Hollywood Principle • Frameworks providing DI ◦ Spring ◦ Guice ◦ CDI • Advantages ◦ Decouples behaviour from construction ◦ Allows configurable client code ◦ Easy unit testing • Disadvantages ◦ Configuration details must be supplied ◦ More difficult to debug ◦ You need a framework
  4. DI - Nowadays @Service class UserService { @Autowired UserRepository ur;

    @Autowired EmailService es; @Autowired AuditService as; User signUp(String email) { User user = ur.save(User.of(email)); es.sendConfirmation(user); as.logSignup(user); return user; } } @Service class UserService { final UserRepository ur; final EmailService es; final AuditService as; @Autowired UserService(…) { … } User signUp(String email) { … } } Recommended way As of Spring 4.3
  5. DI - Old School class UserService { final UserRepository ur;

    final EmailService es; final AuditService as; UserService(...) { … } User signUp(String email) { … } } <beans> <bean id="ur" class="app.UserRepository" /> <bean id="es" class="app.EmailService" /> <bean id="as" class="app.AuditService" /> <bean id="us" class="app.UserService"> <constructor-arg ref="ur"/> <constructor-arg ref="es"/> <constructor-arg ref="as"/> </bean> </beans>
  6. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  7. Without DI (EJB 2 Example - Stone Age) class UserServiceImpl

    implements SessionBean, UserService { User signUp(String email) { User user = … InitialContext ctx = new InitialContext(); AuditServiceHome ash = (AuditServiceHome) ctx.lookup("AuditServiceHome"); AuditService as = ash.create(); as.logSignup(user); … return user; } }
  8. Without DI (EJB 2 Example - Stone Age) • UserServiceLocalHome

    (ext. javax.ejb.EJBLocalHome) • UserServiceHome (ext. javax.ejb.EJBHome) • UserServiceLocal (ext. javax.ejb.EJBLocalObject) • UserService (ext. javax.ejb.EJBObject) • UserServiceImpl (ext. javax.ejb.SessionBean) • ejb-jar.xml • vendor specific XML
  9. Spring’s evolution (Beginnings) • Reflected on the complexities of Java

    EE (J2EE - as it was called back then) • Provided a whole variety of useful tools that worked with J2EE without EJB • That became the Spring Framework https://www.amazon.com/Expert-One-Design-Development-Author/dp/B0140D720S
  10. Spring’s evolution (Spring Framework) • Pragmatically picks up the useful

    parts of Java EE and builds on them • Avoids or tries to reverse the other parts which aren’t recommended • They’re symbiotic in nature rather than being competitors ◦ Example: Contexts and Dependency (CDI) appeared in Java EE 6
  11. Spring’s evolution (Opinionated Development) • What does it take to

    bootstrap a new project? • How do we know that certain libraries (eg. Hibernate) work well with a certain Spring Framework version? • Are there possibly better ways to initialize and wire infrastructure components? • How do should we handle the observability concern in production? • We need a proven good way to handle all of there, an opinionated approach
  12. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  13. Spring Boot (Curated Dependencies) <project> … <groupId>com.craftingjava</ groupId> <artifactId>craftingjava-redirector</ artifactId>

    <version>1.0.5</version> <packaging>jar</packaging> … <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.1.RELEASE</version> <relativePath/> </parent> … <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> … <dependency> <groupId>org.springframework.boot</ groupId> <artifactId>spring-boot-starter-test</ artifactId> <scope>test</scope> </dependency> </dependencies> … <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
  14. Spring Boot (Auto-Configuration) package org.springframework.boot.autoconfigure.web.servlet; @Configuration @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({

    Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class } ) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter(...) public class WebMvcAutoConfiguration { ... } <dependencies> <dependency> <groupId>org.springframework.boot</ groupId> <artifactId>spring-boot-starter-web</ artifactId> </dependency> </dependencies> @SpringBootApplication class Application { ... } @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
  15. Spring Boot (Testing Support) @WebMvcTest @RunWith(SpringRunner.class) @ActiveProfiles("test") class RedirectControllerTest {

    @Autowired private MockMvc mockMvc; … } @BootstrapWith(WebMvcTestContextBootstrapper.class) @ExtendWith(SpringExtension.class) @OverrideAutoConfiguration(enabled = false) @TypeExcludeFilters(WebMvcTypeExcludeFilter.class) @AutoConfigureCache @AutoConfigureWebMvc @AutoConfigureMockMvc @ImportAutoConfiguration
  16. Spring Boot (Summary) • Bootstrapping a new project is easy

    - start.spring.io • Curated set of dependencies which were tested together • Smart defaults and convenient auto-configuration with the possibility to override or define your own autoconfiguration when needed • Actuator for monitoring in production • Testing support • Built upon by other projects (eg. Spring Cloud)
  17. Spring’s Evolution (Ecosystem) • Platform ◦ Spring Framework ▪ AOP

    ▪ Data Access ▪ Messaging ▪ MVC ▪ Web ▪ … ◦ Spring Boot • Web (Add-ons) ◦ Spring Security ◦ Spring Session ◦ Spring HATEOAS ◦ Spring Web Services ◦ Spring REST Docs ◦ … • Integration ◦ Spring Integration ◦ Spring Batch • Data ◦ Spring Data ◦ Spring LDAP ◦ Spring AMQP ◦ Spring Kafka • Cloud ◦ Spring Cloud ◦ Spring Cloud Dataflow • Others ◦ Spring Shell ◦ Spring Flo ◦ Spring Roo ◦ ...
  18. Now What? • Java is a powerful language ◦ Static

    typing ◦ Generics ◦ Lambdas ◦ Modules ◦ ... • Spring is comprehensive ◦ Simple things are simple ◦ Complex things are possible • Spring Boot ◦ Optionioned approach ◦ ~ Pair programming with the Spring team Monolith
  19. What’s wrong with the Monolith? • Slow development • Gathers

    technical debt quickly • All-or-nothing deployment
  20. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  21. What’s wrong with the Monolith? • Slow development • Gathers

    technical debt quickly • All-or-nothing deployment
  22. What are Microservices? In short, the microservice architectural style is

    an approach to developing a single application as a suite of small services, each running in its own process(es) and communicating with lightweight mechanisms, often an HTTP resource API. --- Martin Fowler
  23. What are Microservices? • Component Model • Properties ◦ Self-contained

    independently deployable ◦ Small code base can be built in a few iterations ◦ Uniform Communication with messages ◦ Composable new functionality with additivity
  24. What are Microservices? (Example) ps --no-headers -e -o pmem,comm |

    \ awk -F' ' '{ a[$2]+=$1 } END { for(i in a)print a[i]" "i; }' | \ sort -nr | \ head -5 49 chrome 5.2 slack 3.9 java 1.1 mysqld 0.4 dockerd Top-5 processes aggregated by process-type and ordered by memory usage in descending order.
  25. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  26. Message Driven Systems @Service class UserService { final UserRepository ur;

    final EmailService es; final AuditService as; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); es.sendConfirmation(user); as.logSignup(user); return user; } } “Objects communicate with one another by sending messages. A message is a method call from a message-sending object to a message-receiving object. A message-sending object is a sender while a message-receiving object is a receiver.“ Excerpt from the book Object-Oriented Programming and Java https://www.amazon.com/Object-Oriented-Programming-Java-Danny-Poo-ebook/dp/B00192QXTK
  27. Message Driven Systems (EIP) • Enterprise Integration Patterns • Released

    in 2003 • Documents 65 integration patterns • Integrating applications can be done with ◦ File transfer ◦ Shared Database ◦ RPC ◦ Messaging • EIP focuses on Messaging • Visual pattern language • https://www.enterpriseintegrationpatterns.com/ https://www.amazon.com/o/asin/0321200683/
  28. Message Driven Systems (Some EIP Concepts) • Message ◦ Arbitrary

    payload ◦ Headers • Message Channel • Channel Adapter ◦ Sending a message out to an external system (Outbound) ◦ Receiving a message from an external system (Inbound) • Service Activator ◦ A method or component capable of handling a message or payload
  29. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  30. Spring Integration • Spring Integration is a project that was

    inspired by the EIP book • Natural extension to Spring’s programming model • Messaging inside of an application • Messaging between multiple applications • Provides many in- and outbound adapters for various external systems ◦ File stores (eg. FTP) ◦ Databases (JDBC, MongoDB, etc.) ◦ Messaging systems (JMS, AMQP, etc.) ◦ Other protocols (eg. IMAP, SMTP, etc.)
  31. Spring Integration (EIP Concepts) • Message - org.springframework.messaging.Message<T> ◦ Arbitrary

    payload (T) ◦ Headers (~ Map<String, Object> ) • Message Channel - org.springframework.messaging.MessageChannel • Channel Adapter ◦ Sending a message out to an external system (Outbound) ◦ Receiving a message from an external system (Inbound) • Service Activator - @org.springframework.integration.annotation.ServiceActivator ◦ A method or component capable of handling a message or payload
  32. Spring Integration (Example) @Service class UserService { final UserRepository ur;

    final EmailService es; final AuditService as; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); es.sendConfirmation(user); as.logSignup(user); return user; } }
  33. Spring Integration (Example) @Service class UserService { final UserRepository ur;

    final MessageChannel ch; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); ch.send( MessageBuilder.withPayload(user).build()) ); return user; } } @Service class UserService { final UserRepository ur; final EmailService es; final AuditService as; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); es.sendConfirmation(user); as.logSignup(user); return user; } }
  34. Spring Integration (Example Cont’d) @Service class UserService { final UserRepository

    ur; final MessageChannel ch; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); ch.send( MessageBuilder.withPayload(user).build()) ); return user; } } @Service class EmailService { @ServiceActivator(inputChannel = “ch”) void sendConfirmation(Message<?> msg) { … } } @Service class AuditService { @ServiceActivator(inputChannel = “ch”) void logSignup(Message<?> msg) { … } }
  35. Spring Integration (Example Cont’d) @Service class UserService { final UserRepository

    ur; final MessageChannel ch; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); ch.send( MessageBuilder.withPayload(user).build()) ); return user; } } @Configuration public class UserRegistrationFlowConfig { @Bean public MessageChannel ch() { return new DirectChannel(); } @Bean public IntegrationFlow signupEventFlow( MessageChannel ch, AmqpTemplate at) { return IntegrationFlows .from(signupEventChannel) .transform(Transformers.toJson()) .handle( Amqp.outboundAdapter(amqpTemplate) .exchangeName(...) .routingKey(...) ).get(); } }
  36. Spring Integration • Great tool for building message-driven Microservices •

    Offers a lot more than needed (myriad of adapters) • We need integration only with message brokers and also easy config • Most Microservices work only with a single message channel ps --no-headers -e -o pmem,comm | \ awk -F' ' '{ a[$2]+=$1 } END { for(i in a)print a[i]" "i;}' | \ sort -nr | \ head -5 Source Processor(s) Sink
  37. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices
  38. Spring Cloud Stream Spring Cloud Stream = Spring Integration +

    Spring Boot + Binder(s) • Builds on Spring Integration and it leverages the same primitives like messages and channels • Utilizing Spring Boot off-loads the developer from having to wire these components together • Delivers an opinionated approach through broker specific binders and configures channels automatically
  39. Spring Cloud Stream (Concepts) • Publish / Subscribe model •

    Binder ◦ Abstraction layer ◦ Defines how channels are wired to actual resources a messaging middleware (eg. Kafka, RabbitMQ, Google Pub/Sub, etc.) manages ◦ Opinionated • Binding(s) ◦ Interfaces defining input and output channels ◦ Pre-defined bindings for simple uses cases ▪ Source ▪ Processor ▪ Sink
  40. Spring Integration (Example Publisher) @Service class UserService { final UserRepository

    ur; final MessageChannel ch; UserService(...) { ... } User signUp(String email) { User user = ur.save(User.of(email)); ch.send( MessageBuilder.withPayload(user).build()) ); return user; } } @Configuration public class UserRegistrationFlowConfig { @Bean public MessageChannel ch() { return new DirectChannel(); } @Bean public IntegrationFlow signupEventFlow( MessageChannel ch, AmqpTemplate at) { return IntegrationFlows .from(signupEventChannel) .transform(Transformers.toJson()) .handle( Amqp.outboundAdapter(amqpTemplate) .exchangeName(...) .routingKey(...) ).get(); } }
  41. Spring Cloud Stream (Example Publisher) @Service class UserService { final

    UserRepository ur; final MessageChannel ch; UserService(Source source) { ch = source.output(); } User signUp(String email) { User user = ur.save(User.of(email)); ch.send(MessageBuilder.withPayload( user).build())); return user; } interface Source { @Output("output") MessageChannel output(); } @SpringBootApplication @EnableBinding(Source.class) public class UserRegistrationApp { ... } spring: cloud: stream: bindings: output: destination: signups
  42. Spring Cloud Stream (Example Subscriber) @Service class AuditService { @ServiceActivator(inputChannel

    = “input”) @StreamListener(“input”) void logSignup(Message<?> msg) { } } interface Sink { @Input("input") MessageChannel input(); } @SpringBootApplication @EnableBinding(Sink.class) public class AuditServiceApp { ... } spring: cloud: stream: bindings: input: destination: signups
  43. Spring Cloud Stream • Siblings projects ◦ Spring Cloud Config

    ◦ Spring Cloud Function ◦ Spring Cloud Sleuth ◦ Spring Cloud Task ◦ Spring Cloud Kubernetes ◦ Spring Cloud AWS ◦ Spring Cloud GCP ◦ ...
  44. Story Arc Message Driven Systems Spring Integration Dependency Injection Spring’s

    Evolution Spring Boot Spring Cloud Stream Microservices