Creating a spring boot starter for event sourcing
This commit is contained in:
@@ -17,7 +17,14 @@
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>spring-boot-starter-aws-lambda</module>
|
||||
<module>spring-boot-starter-data-events</module>
|
||||
</modules>
|
||||
</project>
|
||||
121
spring-boot-starters/spring-boot-starter-data-events/README.md
Normal file
121
spring-boot-starters/spring-boot-starter-data-events/README.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Spring Boot Starter Data Events
|
||||
|
||||
This starter project provides auto-configuration support classes for building event-driven Spring Data applications.
|
||||
|
||||
* Uses a familiar _Spring Data_ repository pattern for creating an `EventRepository<T, ID>`
|
||||
* The `EventRepository` provides trait specific features for managing an event log that is attached to an existing domain entity
|
||||
* Provides a set of event abstractions that can be extended to use any Spring Data repository (JPA, Mongo, Neo4j, Redis..)
|
||||
* Provides an `EventService` bean that can be used to publish events to a _Spring Cloud Stream_ output channel
|
||||
|
||||
## Usage
|
||||
|
||||
In your Spring Boot project, add the starter project dependency to your class path. For Maven, add the following dependency to your `pom.xml`.
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.kbastani</groupId>
|
||||
<artifactId>spring-boot-starter-data-events</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
...
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
Next, configure your _Spring Cloud Stream_ output bindings. Add the following snippet to the `application.properties|yaml` file of your Spring Boot application. Replace the destination value with the name of your message channel for the event stream.
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
stream:
|
||||
bindings:
|
||||
output:
|
||||
destination: payment
|
||||
```
|
||||
|
||||
Next, you'll need to create a custom `Event` entity. The snippet below extends the provided `Event<T, E, ID>` interface. This example uses Spring Data JPA, but you can use any Spring Data project for implementing your event entities.
|
||||
|
||||
```java
|
||||
@Entity
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class PaymentEvent extends Event<Payment, PaymentEventType, Long> {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long eventId;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private PaymentEventType type;
|
||||
|
||||
@OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
|
||||
@JsonIgnore
|
||||
private Payment entity;
|
||||
|
||||
@CreatedDate
|
||||
private Long createdAt;
|
||||
|
||||
@LastModifiedDate
|
||||
private Long lastModified;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
To start managing events you'll need to extend the `EventRepository<T, ID>` interface. The `PaymentEvent` is the JPA entity we defined in the last snippet.
|
||||
|
||||
```java
|
||||
public interface PaymentEventRepository extends EventRepository<PaymentEvent, Long> {
|
||||
}
|
||||
```
|
||||
|
||||
That's it! You're ready to start sending domain events to the stream binding's output channel using the auto-configured `EventService`. The example snippet below shows how to create and append a new `PaymentEvent` to a `Payment` entity before publishing the event over AMQP to the configured event stream's output channel.
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class PaymentService {
|
||||
private final EventService<PaymentEvent, Long> eventService;
|
||||
|
||||
public PaymentController(EventService<PaymentEvent, Long> eventService) {
|
||||
this.eventService = eventService;
|
||||
}
|
||||
|
||||
public PaymentEvent appendCreateEvent(Payment payment) {
|
||||
PaymentEvent paymentEvent = new PaymentEvent(PaymentEventType.PAYMENT_CREATED);
|
||||
paymentEvent.setEntity(payment);
|
||||
paymentEvent = eventService.save(event);
|
||||
|
||||
// Send the event to the Spring Cloud stream binding
|
||||
eventService.sendAsync(paymentEvent);
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
A default `EventController` is also provided with the starter project. The `EventController` provides a basic REST API with hypermedia resource support for managing the `Event` log of a domain entity over HTTP. The following cURL snippet gets the `PaymentEvent` we created in the last example from the `EventController`.
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:8082/v1/events/1"
|
||||
```
|
||||
|
||||
Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"eventId": 1,
|
||||
"type": "PAYMENT_CREATED",
|
||||
"createdAt": 1482749707006,
|
||||
"lastModified": 1482749707006,
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "http://localhost:8082/v1/events/1"
|
||||
},
|
||||
"payment": {
|
||||
"href": "http://localhost:8082/v1/payments/1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the snippet above we can see the `EventController` responded with a `hal+json` formatted resource. Since the `PaymentEvent` has a reference to the `Payment` entity, we see a _payment_ link is available to fetch the related resource.
|
||||
51
spring-boot-starters/spring-boot-starter-data-events/pom.xml
Executable file
51
spring-boot-starters/spring-boot-starter-data-events/pom.xml
Executable file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-boot-starter-data-events</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.kbastani</groupId>
|
||||
<artifactId>spring-boot-starters</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-hateoas</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-integration</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,69 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the {@link Event} entity.
|
||||
*
|
||||
* @param <T> is the entity this {@link Event} applies to
|
||||
* @param <E> is the type of event, typically an {@link Enum}
|
||||
* @param <ID> is the unique identifier type used to persist the {@link Event}
|
||||
* @author Kenny Bastani
|
||||
* @see org.springframework.stereotype.Repository
|
||||
* @see ResourceSupport
|
||||
*/
|
||||
public abstract class Event<T extends ResourceSupport, E, ID extends Serializable> extends ResourceSupport {
|
||||
|
||||
public Event() {
|
||||
}
|
||||
|
||||
public abstract ID getEventId();
|
||||
|
||||
public abstract void setEventId(ID eventId);
|
||||
|
||||
public abstract E getType();
|
||||
|
||||
public abstract void setType(E type);
|
||||
|
||||
public abstract T getEntity();
|
||||
|
||||
public abstract void setEntity(T entity);
|
||||
|
||||
public abstract Long getCreatedAt();
|
||||
|
||||
public abstract void setCreatedAt(Long createdAt);
|
||||
|
||||
public abstract Long getLastModified();
|
||||
|
||||
public abstract void setLastModified(Long lastModified);
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Link> getLinks() {
|
||||
List<Link> links = super.getLinks().stream().collect(Collectors.toList());
|
||||
links.add(getId());
|
||||
Class<T> clazz = (Class<T>) ((ParameterizedTypeImpl)
|
||||
this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
links.add(getEntity().getId().withRel(new EvoInflectorRelProvider().getItemResourceRelFor(clazz)));
|
||||
return links;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("links: %s", getLinks().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Link getId() {
|
||||
return linkTo(EventController.class).slash("events").slash(getEventId()).withSelfRel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* This class auto-configures a {@link EventServiceImpl} bean.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ EventRepository.class, Source.class, RestTemplate.class })
|
||||
@EnableConfigurationProperties(EventProperties.class)
|
||||
public class EventAutoConfig {
|
||||
|
||||
private EventRepository eventRepository;
|
||||
private Source source;
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
public EventAutoConfig(EventRepository eventRepository, Source source, RestTemplate restTemplate) {
|
||||
this.eventRepository = eventRepository;
|
||||
this.source = source;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Bean
|
||||
public EventService eventService() {
|
||||
return new EventServiceImpl(eventRepository, source, restTemplate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The default controller for managing {@link Event} entities.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/v1")
|
||||
public class EventController<T extends Event, ID extends Serializable> {
|
||||
|
||||
private final EventService<T, Long> eventService;
|
||||
|
||||
public EventController(EventService<T, Long> eventService) {
|
||||
this.eventService = eventService;
|
||||
}
|
||||
|
||||
@PostMapping(path = "/events/{id}")
|
||||
public ResponseEntity createEvent(@RequestBody T event, @PathVariable Long id) {
|
||||
return Optional.ofNullable(eventService.save(id, event))
|
||||
.map(e -> new ResponseEntity<>(e, HttpStatus.CREATED))
|
||||
.orElseThrow(() -> new RuntimeException("Event creation failed"));
|
||||
}
|
||||
|
||||
@PutMapping(path = "/events/{id}")
|
||||
public ResponseEntity updateEvent(@RequestBody T event, @PathVariable Long id) {
|
||||
return Optional.ofNullable(eventService.save(id, event))
|
||||
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
|
||||
.orElseThrow(() -> new RuntimeException("Event update failed"));
|
||||
}
|
||||
|
||||
@GetMapping(path = "/events/{id}")
|
||||
public ResponseEntity getEvent(@PathVariable Long id) {
|
||||
return Optional.ofNullable(eventService.findOne(id))
|
||||
.map(e -> new ResponseEntity<>(e, HttpStatus.OK))
|
||||
.orElse(new ResponseEntity<>(HttpStatus.NOT_FOUND));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "event")
|
||||
public class EventProperties {
|
||||
|
||||
@NestedConfigurationProperty
|
||||
private Props props;
|
||||
|
||||
public Props getProps() {
|
||||
return props;
|
||||
}
|
||||
|
||||
public void setProps(Props props) {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
public static class Props {
|
||||
// TODO: Implement
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.NoRepositoryBean;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Extension of {@link PagingAndSortingRepository} to provide additional support for persisting event logs to entities.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
* @see Event
|
||||
* @see EventService
|
||||
*/
|
||||
@NoRepositoryBean
|
||||
public interface EventRepository<E extends Event, ID extends Serializable> extends PagingAndSortingRepository<E, ID> {
|
||||
Page<E> findEventsByEntityId(@Param("entityId") ID entityId, Pageable pageable);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package demo.event;
|
||||
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Service interface for managing {@link Event} entities.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
* @see Event
|
||||
* @see Events
|
||||
* @see EventServiceImpl
|
||||
*/
|
||||
public interface EventService<T extends Event, ID extends Serializable> {
|
||||
|
||||
/**
|
||||
* Raises a synchronous domain event. An {@link Event} will be applied to an entity through a chain of HTTP
|
||||
* requests/responses.
|
||||
*
|
||||
* @param event
|
||||
* @param links
|
||||
* @return the applied {@link Event}
|
||||
*/
|
||||
<E extends ResourceSupport, S extends T> S send(S event, Link... links);
|
||||
|
||||
/**
|
||||
* Raises an asynchronous domain event. An {@link Event} will be applied to an entity through a chain of AMQP
|
||||
* messages.
|
||||
*
|
||||
* @param event
|
||||
* @param links
|
||||
* @return a flag indicating if the {@link Event} message was sent successfully
|
||||
*/
|
||||
<S extends T> Boolean sendAsync(S event, Link... links);
|
||||
|
||||
/**
|
||||
* Saves a given event entity. Use the returned instance for further operations as the save operation might have
|
||||
* changed the entity instance completely.
|
||||
*
|
||||
* @param event
|
||||
* @return the saved event entity
|
||||
*/
|
||||
<S extends T> S save(S event);
|
||||
|
||||
/**
|
||||
* Saves a given event entity. Use the returned instance for further operations as the save operation might have
|
||||
* changed the entity instance completely. The {@link ID} parameter is the unique {@link Event} identifier.
|
||||
*
|
||||
* @param id
|
||||
* @param event
|
||||
* @return the saved event entity
|
||||
*/
|
||||
<S extends T> S save(ID id, S event);
|
||||
|
||||
/**
|
||||
* Retrieves an {@link Event} entity by its id.
|
||||
*
|
||||
* @param id
|
||||
* @return the {@link Event} entity with the given id or {@literal null} if none found
|
||||
*/
|
||||
<EID extends ID> T findOne(EID id);
|
||||
|
||||
/**
|
||||
* Retrieves an entity's {@link Event}s by its id.
|
||||
*
|
||||
* @param entityId
|
||||
* @return a {@link Events} containing a collection of {@link Event}s
|
||||
*/
|
||||
<E extends Events> E find(ID entityId);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package demo.event;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.springframework.cloud.stream.messaging.Source;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.hateoas.Resource;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* Event service implementation of {@link EventService} for managing {@link Event} entities.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
* @see Event
|
||||
* @see Events
|
||||
* @see EventService
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
class EventServiceImpl<T extends Event, ID extends Serializable> implements EventService<T, ID> {
|
||||
|
||||
private static final Logger log = Logger.getLogger(EventServiceImpl.class);
|
||||
private static final String EVENT_PROCESSOR_URL = "http://localhost:8083/v1/events";
|
||||
|
||||
private final EventRepository<T, ID> eventRepository;
|
||||
private final Source eventStream;
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
EventServiceImpl(EventRepository<T, ID> eventRepository, Source eventStream, RestTemplate restTemplate) {
|
||||
this.eventRepository = eventRepository;
|
||||
this.eventStream = eventStream;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public <E extends ResourceSupport, S extends T> S send(S event, Link... links) {
|
||||
// Assemble request to the event stream processor
|
||||
RequestEntity<Resource<T>> requestEntity = RequestEntity.post(URI.create(EVENT_PROCESSOR_URL))
|
||||
.contentType(MediaTypes.HAL_JSON).body(new Resource<T>(event), Resource.class);
|
||||
|
||||
try {
|
||||
// Send the event to the event stream processor
|
||||
E entity = (E) restTemplate.exchange(requestEntity, event.getEntity().getClass()).getBody();
|
||||
// Set the applied entity reference to the event
|
||||
event.setEntity(entity);
|
||||
} catch (Exception ex) {
|
||||
log.error(ex);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
public <S extends T> Boolean sendAsync(S event, Link... links) {
|
||||
return eventStream.output().send(MessageBuilder.withPayload(event).build());
|
||||
}
|
||||
|
||||
public <S extends T> S save(S event) {
|
||||
event = eventRepository.save(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
public <S extends T> S save(ID id, S event) {
|
||||
event.setEventId(id);
|
||||
return save(event);
|
||||
}
|
||||
|
||||
public <S extends ID> T findOne(S id) {
|
||||
return eventRepository.findOne(id);
|
||||
}
|
||||
|
||||
public <E extends Events> E find(ID entityId) {
|
||||
return (E) new Events(entityId, eventRepository.findEventsByEntityId(entityId,
|
||||
new PageRequest(0, Integer.MAX_VALUE)).getContent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package demo.event;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.ResourceSupport;
|
||||
import org.springframework.hateoas.Resources;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* General helper to easily create a wrapper for a collection of {@link Event} entities.
|
||||
*
|
||||
* @author Kenny Bastani
|
||||
*/
|
||||
public class Events<T extends ResourceSupport, E, ID extends Serializable> extends Resources<Event<T, E, ID>> {
|
||||
|
||||
private ID entityId;
|
||||
|
||||
public Events(ID entityId, List<Event<T, E, ID>> content) {
|
||||
this(content);
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public Events(Iterable<Event<T, E, ID>> content, Link... links) {
|
||||
super(content, links);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public ID getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"groups": [
|
||||
{
|
||||
"name": "event",
|
||||
"type": "demo.event.EventProperties",
|
||||
"sourceType": "demo.event.EventProperties"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=demo.event.EventAutoConfig
|
||||
@@ -0,0 +1,39 @@
|
||||
package demo.event;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
|
||||
public class ConfigurationTest {
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if (this.context != null) {
|
||||
this.context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextLoads() {
|
||||
load(EmptyConfiguration.class);
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class EmptyConfiguration {
|
||||
}
|
||||
|
||||
private void load(Class<?> config, String... environment) {
|
||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
|
||||
applicationContext.register(config);
|
||||
applicationContext.refresh();
|
||||
this.context = applicationContext;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user