Consistent use of tabs for sample code in the reference documentation
This commit is contained in:
parent
08c78554b9
commit
6f24c0de17
|
|
@ -4,6 +4,7 @@
|
|||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
This part of the reference documentation covers all of those technologies that are
|
||||
|
|
|
|||
|
|
@ -3822,7 +3822,6 @@ Find below the custom `BeanPostProcessor` implementation class definition:
|
|||
package scripting;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.BeansException;
|
||||
|
||||
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
[[databuffers]]
|
||||
= Data Buffers and Codecs
|
||||
|
||||
|
||||
|
||||
|
||||
== Introduction
|
||||
|
||||
The `DataBuffer` interface defines an abstraction over byte buffers.
|
||||
|
|
@ -9,6 +12,9 @@ Netty does not use `ByteBuffer`, but instead offers `ByteBuf` as an alternative.
|
|||
Spring's `DataBuffer` is a simple abstraction over `ByteBuf` that can also be used on non-Netty
|
||||
platforms (i.e. Servlet 3.1+).
|
||||
|
||||
|
||||
|
||||
|
||||
== `DataBufferFactory`
|
||||
|
||||
The `DataBufferFactory` offers functionality to allocate new data buffers, as well as to wrap
|
||||
|
|
@ -25,6 +31,9 @@ to be used on Netty platforms, such as Reactor Netty.
|
|||
The other implementation, the `DefaultDataBufferFactory`, is used on other platforms, such as
|
||||
Servlet 3.1+ servers.
|
||||
|
||||
|
||||
|
||||
|
||||
== The `DataBuffer` interface
|
||||
|
||||
The `DataBuffer` interface is similar to `ByteBuffer`, but offers a number of advantages.
|
||||
|
|
@ -35,7 +44,7 @@ writing, and a separate `flip()` operation to switch between the two I/O operat
|
|||
In general, the following invariant holds for the read position, write position, and the capacity:
|
||||
|
||||
--
|
||||
`0` <= _read position_ <= _write position_ <= _capacity_
|
||||
`0` <= _read position_ <= _write position_ <= _capacity_
|
||||
--
|
||||
|
||||
When reading bytes from the `DataBuffer`, the read position is automatically updated in accordance with
|
||||
|
|
@ -54,6 +63,8 @@ Netty platforms, such as Reactor Netty.
|
|||
The other implementation, the `DefaultDataBuffer`, is used on other platforms, such as Servlet 3.1+
|
||||
servers.
|
||||
|
||||
|
||||
|
||||
=== `PooledDataBuffer`
|
||||
|
||||
The `PooledDataBuffer` is an extension to `DataBuffer` that adds methods for reference counting.
|
||||
|
|
@ -67,6 +78,7 @@ buffers.
|
|||
These methods take a plain `DataBuffer` as parameter, but only call `retain` or `release` if the
|
||||
passed data buffer is an instance of `PooledDataBuffer`.
|
||||
|
||||
|
||||
[[databuffer-reference-counting]]
|
||||
==== Reference Counting
|
||||
|
||||
|
|
@ -95,23 +107,25 @@ throw exceptions:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
DataBufferFactory factory = ...
|
||||
DataBuffer buffer = factory.allocateBuffer(); <1>
|
||||
boolean release = true; <2>
|
||||
try {
|
||||
DataBufferFactory factory = ...
|
||||
DataBuffer buffer = factory.allocateBuffer(); <1>
|
||||
boolean release = true; <2>
|
||||
try {
|
||||
writeDataToBuffer(buffer); <3>
|
||||
putBufferInHttpBody(buffer);
|
||||
release = false; <4>
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (release) {
|
||||
DataBufferUtils.release(buffer); <5>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDataToBuffer(DataBuffer buffer) throws IOException { <3>
|
||||
private void writeDataToBuffer(DataBuffer buffer) throws IOException { <3>
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<1> A new buffer is allocated.
|
||||
<2> A boolean flag indicates whether the allocated buffer should be released.
|
||||
<3> This example method loads data into the buffer. Note that the method can throw an `IOException`,
|
||||
|
|
@ -121,6 +135,8 @@ released as part of sending the HTTP body across the wire.
|
|||
<5> If an exception did occur, the flag is still set to `true`, and the buffer will be released
|
||||
here.
|
||||
|
||||
|
||||
|
||||
=== DataBufferUtils
|
||||
|
||||
`DataBufferUtils` contains various utility methods that operate on data buffers.
|
||||
|
|
@ -129,6 +145,9 @@ It contains methods for reading a `Flux` of `DataBuffer` objects from an `InputS
|
|||
`DataBufferUtils` also exposes `retain` and `release` methods that operate on plain `DataBuffer`
|
||||
instances (so that casting to a `PooledDataBuffer` is not required).
|
||||
|
||||
|
||||
|
||||
|
||||
[codecs]
|
||||
== Codecs
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
This part of the reference documentation is concerned with data access and the
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
:doc-spring-gemfire: {doc-root}/spring-gemfire/docs/current/reference
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
This part of the reference documentation covers the Spring Framework's integration with
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
[[lanugages]]
|
||||
[[languages]]
|
||||
= Language Support
|
||||
:doc-root: https://docs.spring.io
|
||||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -58,18 +58,18 @@ for their APIs, thus giving a better Kotlin development experience overall.
|
|||
|
||||
To retrieve a list of `Foo` objects in Java, one would normally write:
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
Flux<User> users = client.get().retrieve().bodyToFlux(User.class)
|
||||
Flux<User> users = client.get().retrieve().bodyToFlux(User.class)
|
||||
----
|
||||
|
||||
Whilst with Kotlin and Spring Framework extensions, one is able to write:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val users = client.get().retrieve().bodyToFlux<User>()
|
||||
// or (both are equivalent)
|
||||
val users : Flux<User> = client.get().retrieve().bodyToFlux()
|
||||
val users = client.get().retrieve().bodyToFlux<User>()
|
||||
// or (both are equivalent)
|
||||
val users : Flux<User> = client.get().retrieve().bodyToFlux()
|
||||
----
|
||||
|
||||
As in Java, `users` in Kotlin is strongly typed, but Kotlin's clever type inference allows
|
||||
|
|
@ -172,24 +172,23 @@ This mechanism is very efficient as it does not require any reflection or CGLIB
|
|||
|
||||
In Java, one may for example write:
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBean(Foo.class);
|
||||
context.registerBean(Bar.class, () -> new
|
||||
Bar(context.getBean(Foo.class))
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBean(Foo.class);
|
||||
context.registerBean(Bar.class, () -> new Bar(context.getBean(Foo.class))
|
||||
);
|
||||
----
|
||||
|
||||
Whilst in Kotlin with reified type parameters and `GenericApplicationContext`
|
||||
Kotlin extensions one can instead simply write:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val context = GenericApplicationContext().apply {
|
||||
val context = GenericApplicationContext().apply {
|
||||
registerBean<Foo>()
|
||||
registerBean { Bar(it.getBean<Foo>()) }
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In order to allow a more declarative approach and cleaner syntax, Spring Framework provides
|
||||
|
|
@ -198,9 +197,9 @@ It declares an `ApplicationContextInitializer` via a clean declarative API
|
|||
which enables one to deal with profiles and `Environment` for customizing
|
||||
how beans are registered.
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
fun beans() = beans {
|
||||
fun beans() = beans {
|
||||
bean<UserHandler>()
|
||||
bean<Routes>()
|
||||
bean<WebHandler>("webHandler") {
|
||||
|
|
@ -227,7 +226,7 @@ fun beans() = beans {
|
|||
profile("foo") {
|
||||
bean<Foo>()
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In this example, `bean<Routes>()` is using autowiring by constructor and `ref<Routes>()`
|
||||
|
|
@ -235,12 +234,12 @@ is a shortcut for `applicationContext.getBean(Routes::class.java)`.
|
|||
|
||||
This `beans()` function can then be used to register beans on the application context.
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
val context = GenericApplicationContext().apply {
|
||||
val context = GenericApplicationContext().apply {
|
||||
beans().initialize(this)
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -276,9 +275,9 @@ Spring Framework now comes with a
|
|||
that allows one to leverage the <<web-reactive#webflux-fn,WebFlux functional
|
||||
API>> for writing clean and idiomatic Kotlin code:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
router {
|
||||
router {
|
||||
accept(TEXT_HTML).nest {
|
||||
GET("/") { ok().render("index") }
|
||||
GET("/sse") { ok().render("sse") }
|
||||
|
|
@ -293,7 +292,7 @@ router {
|
|||
}
|
||||
}
|
||||
resources("/**", ClassPathResource("static/"))
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -326,18 +325,18 @@ https://github.com/Kotlin/kotlinx.html[kotlinx.html] DSL or simply using Kotlin
|
|||
This can allow one to write Kotlin templates with full autocompletion and
|
||||
refactoring support in a supported IDE:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
import io.spring.demo.*
|
||||
import io.spring.demo.*
|
||||
|
||||
"""
|
||||
${include("header")}
|
||||
<h1>${i18n("title")}</h1>
|
||||
<ul>
|
||||
"""
|
||||
${include("header")}
|
||||
<h1>${i18n("title")}</h1>
|
||||
<ul>
|
||||
${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}
|
||||
</ul>
|
||||
${include("footer")}
|
||||
"""
|
||||
</ul>
|
||||
${include("footer")}
|
||||
"""
|
||||
----
|
||||
|
||||
See https://github.com/sdeleuze/kotlin-script-templating[kotlin-script-templating] example
|
||||
|
|
@ -394,9 +393,9 @@ you will be able to write your Kotlin beans without any additional `open` keywor
|
|||
In Kotlin, it is very convenient and considered best practice to declare read-only properties
|
||||
within the primary constructor, as in the following example:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
class Person(val name: String, val age: Int)
|
||||
class Person(val name: String, val age: Int)
|
||||
----
|
||||
|
||||
You can optionally add https://kotlinlang.org/docs/reference/data-classes.html[the `data` keyword]
|
||||
|
|
@ -410,12 +409,12 @@ in the primary constructor:
|
|||
|
||||
This allows for easy changes to individual properties even if `Person` properties are read-only:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
data class Person(val name: String, val age: Int)
|
||||
data class Person(val name: String, val age: Int)
|
||||
|
||||
val jack = Person(name = "Jack", age = 1)
|
||||
val olderJack = jack.copy(age = 2)
|
||||
val jack = Person(name = "Jack", age = 1)
|
||||
val olderJack = jack.copy(age = 2)
|
||||
----
|
||||
|
||||
Common persistence technologies such as JPA require a default constructor, preventing this
|
||||
|
|
@ -442,13 +441,13 @@ mappings (like with MongoDB, Redis, Cassandra, etc).
|
|||
Our recommendation is to try and favor constructor injection with `val` read-only (and non-nullable when possible)
|
||||
https://kotlinlang.org/docs/reference/properties.html[properties].
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Component
|
||||
class YourBean(
|
||||
@Component
|
||||
class YourBean(
|
||||
private val mongoTemplate: MongoTemplate,
|
||||
private val solrClient: SolrClient
|
||||
)
|
||||
)
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -461,17 +460,17 @@ explicit `@Autowired constructor` in the example shown above.
|
|||
If one really needs to use field injection, use the `lateinit var` construct,
|
||||
i.e.,
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Component
|
||||
class YourBean {
|
||||
@Component
|
||||
class YourBean {
|
||||
|
||||
@Autowired
|
||||
lateinit var mongoTemplate: MongoTemplate
|
||||
|
||||
@Autowired
|
||||
lateinit var solrClient: SolrClient
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
|
@ -487,28 +486,27 @@ character will need to be escaped by writing `@Value("\${property}")`.
|
|||
As an alternative, it is possible to customize the properties placeholder prefix by declaring
|
||||
the following configuration beans:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Bean
|
||||
fun propertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||||
@Bean
|
||||
fun propertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||||
setPlaceholderPrefix("%{")
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Existing code (like Spring Boot actuators or `@LocalServerPort`) that
|
||||
uses the `${...}` syntax, can be customised with configuration beans, like
|
||||
this:
|
||||
Existing code (like Spring Boot actuators or `@LocalServerPort`) that uses the `${...}` syntax,
|
||||
can be customised with configuration beans, like as follows:
|
||||
|
||||
[source,kotlin]
|
||||
[source,kotlin,indent=0]
|
||||
----
|
||||
@Bean
|
||||
fun kotlinPropertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||||
@Bean
|
||||
fun kotlinPropertyConfigurer() = PropertySourcesPlaceholderConfigurer().apply {
|
||||
setPlaceholderPrefix("%{")
|
||||
setIgnoreUnresolvablePlaceholders(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
|
||||
@Bean
|
||||
fun defaultPropertyConfigurer() = PropertySourcesPlaceholderConfigurer()
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -535,9 +533,9 @@ attribute it is specified as a `vararg` parameter.
|
|||
To understand what that means, let's take `@RequestMapping`, which is one
|
||||
of the most widely used Spring annotations as an example. This Java annotation is declared as:
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public @interface RequestMapping {
|
||||
public @interface RequestMapping {
|
||||
|
||||
@AliasFor("path")
|
||||
String[] value() default {};
|
||||
|
|
@ -548,7 +546,7 @@ public @interface RequestMapping {
|
|||
RequestMethod[] method() default {};
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The typical use case for `@RequestMapping` is to map a handler method to a specific path
|
||||
|
|
@ -568,8 +566,8 @@ use a shortcut annotation such as `@GetMapping` or `@PostMapping`, etc.
|
|||
|
||||
[NOTE]
|
||||
====
|
||||
Remininder: if the `@RequestMapping` `method` attribute is not specified, all HTTP methods will be matched,
|
||||
not only the `GET` methods.
|
||||
Reminder: If the `@RequestMapping` `method` attribute is not specified,
|
||||
all HTTP methods will be matched, not only the `GET` methods.
|
||||
====
|
||||
|
||||
Improving the syntax and consistency of Kotlin annotation array attributes is discussed in
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
[[overview]]
|
||||
= Spring Framework Overview
|
||||
:toc: left
|
||||
:toclevels: 1
|
||||
:docinfo1:
|
||||
|
||||
[[overview]]
|
||||
= Spring Framework Overview
|
||||
|
||||
Spring makes it easy to create Java enterprise applications. It provides everything you
|
||||
need to embrace the Java language in an enterprise environment, with support for Groovy
|
||||
and Kotlin as alternative languages on the JVM, and with the flexibility to create many
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ some subset of it:
|
|||
public void setUp() {
|
||||
client = WebTestClient.bindToApplicationContext(context).build(); // <3>
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
:doc-spring-boot: {doc-root}/spring-boot/docs/current/reference
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
The adoption of the test-driven-development (TDD) approach to software
|
||||
|
|
@ -1142,13 +1143,13 @@ example, a custom `@EnabledOnMac` annotation can be created as follows.
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnabledIf(
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnabledIf(
|
||||
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
||||
reason = "Enabled on Mac OS"
|
||||
)
|
||||
public @interface EnabledOnMac {}
|
||||
)
|
||||
public @interface EnabledOnMac {}
|
||||
----
|
||||
|
||||
===== @DisabledIf
|
||||
|
|
@ -1179,13 +1180,13 @@ example, a custom `@DisabledOnMac` annotation can be created as follows.
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@DisabledIf(
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@DisabledIf(
|
||||
expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
|
||||
reason = "Disabled on Mac OS"
|
||||
)
|
||||
public @interface DisabledOnMac {}
|
||||
)
|
||||
public @interface DisabledOnMac {}
|
||||
----
|
||||
|
||||
|
||||
|
|
@ -3901,15 +3902,15 @@ empty list in order to disable the default listeners, which otherwise would requ
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestExecutionListeners({})
|
||||
public class SimpleTest {
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestExecutionListeners({})
|
||||
public class SimpleTest {
|
||||
|
||||
@Test
|
||||
public void testMethod() {
|
||||
// execute test logic...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[testcontext-junit4-rules]]
|
||||
|
|
@ -3938,9 +3939,9 @@ demonstrates the proper way to declare these rules in an integration test.
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// Optionally specify a non-Spring Runner via @RunWith(...)
|
||||
@ContextConfiguration
|
||||
public class IntegrationTest {
|
||||
// Optionally specify a non-Spring Runner via @RunWith(...)
|
||||
@ContextConfiguration
|
||||
public class IntegrationTest {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule springClassRule = new SpringClassRule();
|
||||
|
|
@ -3952,7 +3953,7 @@ public class IntegrationTest {
|
|||
public void testMethod() {
|
||||
// execute test logic...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[testcontext-support-classes-junit4]]
|
||||
|
|
@ -4026,17 +4027,17 @@ The following code listing demonstrates how to configure a test class to use the
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// Instructs JUnit Jupiter to extend the test with Spring support.
|
||||
@ExtendWith(SpringExtension.class)
|
||||
// Instructs Spring to load an ApplicationContext from TestConfig.class
|
||||
@ContextConfiguration(classes = TestConfig.class)
|
||||
class SimpleTests {
|
||||
// Instructs JUnit Jupiter to extend the test with Spring support.
|
||||
@ExtendWith(SpringExtension.class)
|
||||
// Instructs Spring to load an ApplicationContext from TestConfig.class
|
||||
@ContextConfiguration(classes = TestConfig.class)
|
||||
class SimpleTests {
|
||||
|
||||
@Test
|
||||
void testMethod() {
|
||||
// execute test logic...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Since annotations in JUnit 5 can also be used as meta-annotations, Spring is able to
|
||||
|
|
@ -4049,16 +4050,16 @@ configuration used in the previous example.
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// Instructs Spring to register the SpringExtension with JUnit
|
||||
// Jupiter and load an ApplicationContext from TestConfig.class
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class SimpleTests {
|
||||
// Instructs Spring to register the SpringExtension with JUnit
|
||||
// Jupiter and load an ApplicationContext from TestConfig.class
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class SimpleTests {
|
||||
|
||||
@Test
|
||||
void testMethod() {
|
||||
// execute test logic...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Similarly, the following example uses `@SpringJUnitWebConfig` to create a
|
||||
|
|
@ -4067,16 +4068,16 @@ Similarly, the following example uses `@SpringJUnitWebConfig` to create a
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// Instructs Spring to register the SpringExtension with JUnit
|
||||
// Jupiter and load a WebApplicationContext from TestWebConfig.class
|
||||
@SpringJUnitWebConfig(TestWebConfig.class)
|
||||
class SimpleWebTests {
|
||||
// Instructs Spring to register the SpringExtension with JUnit
|
||||
// Jupiter and load a WebApplicationContext from TestWebConfig.class
|
||||
@SpringJUnitWebConfig(TestWebConfig.class)
|
||||
class SimpleWebTests {
|
||||
|
||||
@Test
|
||||
void testMethod() {
|
||||
// execute test logic...
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
See the documentation for `@SpringJUnitConfig` and `@SpringJUnitWebConfig` in
|
||||
|
|
@ -4121,8 +4122,8 @@ dependencies to be `final` and therefore _immutable_.
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
|
||||
private final OrderService orderService;
|
||||
|
||||
|
|
@ -4132,7 +4133,7 @@ class OrderServiceIntegrationTests {
|
|||
}
|
||||
|
||||
// tests that use the injected OrderService
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[testcontext-junit-jupiter-di-method]]
|
||||
|
|
@ -4149,14 +4150,14 @@ In the following example, Spring will inject the `OrderService` from the
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
|
||||
@Test
|
||||
void deleteOrder(@Autowired OrderService orderService) {
|
||||
// use orderService from the test's ApplicationContext
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Due to the robustness of the `ParameterResolver` support in JUnit Jupiter, it is also
|
||||
|
|
@ -4171,8 +4172,8 @@ use of `@RepeatedTest` from JUnit Jupiter allows the test method to gain access
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
@SpringJUnitConfig(TestConfig.class)
|
||||
class OrderServiceIntegrationTests {
|
||||
|
||||
@RepeatedTest(10)
|
||||
void placeOrderRepeatedly(RepetitionInfo repetitionInfo,
|
||||
|
|
@ -4181,7 +4182,7 @@ class OrderServiceIntegrationTests {
|
|||
// use orderService from the test's ApplicationContext
|
||||
// and repetitionInfo from JUnit Jupiter
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[testcontext-support-classes-testng]]
|
||||
|
|
@ -4276,11 +4277,11 @@ JUnit Jupiter based example of using Spring MVC Test:
|
|||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
|
||||
class ExampleTests {
|
||||
@SpringJUnitWebConfig(locations = "test-servlet-context.xml")
|
||||
class ExampleTests {
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
|
@ -4297,8 +4298,7 @@ class ExampleTests {
|
|||
.andExpect(content().contentType("application/json"))
|
||||
.andExpect(jsonPath("$.name").value("Lee"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The above test relies on the `WebApplicationContext` support of the __TestContext framework__
|
||||
|
|
@ -4461,13 +4461,13 @@ session across requests. It can be used as follows:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
// static import of SharedHttpSessionConfigurer.sharedHttpSession
|
||||
// static import of SharedHttpSessionConfigurer.sharedHttpSession
|
||||
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
|
||||
.apply(sharedHttpSession())
|
||||
.build();
|
||||
|
||||
// Use mockMvc to perform requests...
|
||||
// Use mockMvc to perform requests...
|
||||
----
|
||||
|
||||
See `ConfigurableMockMvcBuilder` for a list of all MockMvc builder features
|
||||
|
|
@ -4761,13 +4761,13 @@ paging through all messages. How would you go about testing it?
|
|||
|
||||
With Spring MVC Test, we can easily test if we are able to create a `Message`.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
.param("summary", "Spring Rocks")
|
||||
.param("text", "In case you didn't know, Spring Rocks!");
|
||||
|
||||
mockMvc.perform(createMessage)
|
||||
mockMvc.perform(createMessage)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/messages/123"));
|
||||
----
|
||||
|
|
@ -4775,9 +4775,9 @@ mockMvc.perform(createMessage)
|
|||
What if we want to test our form view that allows us to create the message? For example,
|
||||
assume our form looks like the following snippet:
|
||||
|
||||
[source,xml]
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
<form id="messageForm" action="/messages/" method="post">
|
||||
<form id="messageForm" action="/messages/" method="post">
|
||||
<div class="pull-right"><a href="/messages/">Messages</a></div>
|
||||
|
||||
<label for="summary">Summary</label>
|
||||
|
|
@ -4789,15 +4789,15 @@ assume our form looks like the following snippet:
|
|||
<div class="form-actions">
|
||||
<input type="submit" value="Create" />
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
----
|
||||
|
||||
How do we ensure that our form will produce the correct request to create a new message? A
|
||||
naive attempt would look like this:
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
.andExpect(xpath("//input[@name='summary']").exists())
|
||||
.andExpect(xpath("//textarea[@name='text']").exists());
|
||||
----
|
||||
|
|
@ -4807,19 +4807,19 @@ This test has some obvious drawbacks. If we update our controller to use the par
|
|||
form is out of synch with the controller. To resolve this we can combine our two tests.
|
||||
|
||||
[[spring-mvc-test-server-htmlunit-mock-mvc-test]]
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
String summaryParamName = "summary";
|
||||
String textParamName = "text";
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
String summaryParamName = "summary";
|
||||
String textParamName = "text";
|
||||
mockMvc.perform(get("/messages/form"))
|
||||
.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
|
||||
.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());
|
||||
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
MockHttpServletRequestBuilder createMessage = post("/messages/")
|
||||
.param(summaryParamName, "Spring Rocks")
|
||||
.param(textParamName, "In case you didn't know, Spring Rocks!");
|
||||
|
||||
mockMvc.perform(createMessage)
|
||||
mockMvc.perform(createMessage)
|
||||
.andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("/messages/123"));
|
||||
----
|
||||
|
|
@ -4908,19 +4908,19 @@ In order to use HtmlUnit with Apache HttpComponents 4.5+, you will need to use H
|
|||
We can easily create an HtmlUnit `WebClient` that integrates with `MockMvc` using the
|
||||
`MockMvcWebClientBuilder` as follows.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebClient webClient;
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.webAppContextSetup(context)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -4941,9 +4941,9 @@ Now we can use HtmlUnit as we normally would, but without the need to deploy our
|
|||
application to a Servlet container. For example, we can request the view to create
|
||||
a message with the following.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
|
||||
HtmlPage createMsgFormPage = webClient.getPage("http://localhost/messages/form");
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -4955,29 +4955,29 @@ illustrated in <<Advanced MockMvcWebClientBuilder>>.
|
|||
Once we have a reference to the `HtmlPage`, we can then fill out the form and submit
|
||||
it to create a message.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
|
||||
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute("Spring Rocks");
|
||||
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
|
||||
textInput.setText("In case you didn't know, Spring Rocks!");
|
||||
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
|
||||
HtmlPage newMessagePage = submit.click();
|
||||
HtmlForm form = createMsgFormPage.getHtmlElementById("messageForm");
|
||||
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute("Spring Rocks");
|
||||
HtmlTextArea textInput = createMsgFormPage.getHtmlElementById("text");
|
||||
textInput.setText("In case you didn't know, Spring Rocks!");
|
||||
HtmlSubmitInput submit = form.getOneHtmlElementByAttribute("input", "type", "submit");
|
||||
HtmlPage newMessagePage = submit.click();
|
||||
----
|
||||
|
||||
Finally, we can verify that a new message was created successfully. The following
|
||||
assertions use the http://joel-costigliola.github.io/assertj/[AssertJ] library.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
|
||||
String id = newMessagePage.getHtmlElementById("id").getTextContent();
|
||||
assertThat(id).isEqualTo("123");
|
||||
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
|
||||
assertThat(summary).isEqualTo("Spring Rocks");
|
||||
String text = newMessagePage.getHtmlElementById("text").getTextContent();
|
||||
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
|
||||
assertThat(newMessagePage.getUrl().toString()).endsWith("/messages/123");
|
||||
String id = newMessagePage.getHtmlElementById("id").getTextContent();
|
||||
assertThat(id).isEqualTo("123");
|
||||
String summary = newMessagePage.getHtmlElementById("summary").getTextContent();
|
||||
assertThat(summary).isEqualTo("Spring Rocks");
|
||||
String text = newMessagePage.getHtmlElementById("text").getTextContent();
|
||||
assertThat(text).isEqualTo("In case you didn't know, Spring Rocks!");
|
||||
----
|
||||
|
||||
This improves on our <<spring-mvc-test-server-htmlunit-mock-mvc-test,MockMvc test>> in a
|
||||
|
|
@ -4999,29 +4999,29 @@ In the examples so far, we have used `MockMvcWebClientBuilder` in the simplest w
|
|||
by building a `WebClient` based on the `WebApplicationContext` loaded for us by the Spring
|
||||
TestContext Framework. This approach is repeated here.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebClient webClient;
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.webAppContextSetup(context)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
We can also specify additional configuration options.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
WebClient webClient;
|
||||
WebClient webClient;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
webClient = MockMvcWebClientBuilder
|
||||
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
||||
.webAppContextSetup(context, springSecurity())
|
||||
|
|
@ -5031,20 +5031,20 @@ public void setup() {
|
|||
// the following will use MockMvc for example.com and example.org as well
|
||||
.useMockMvcForHosts("example.com","example.org")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
|
||||
instance separately and supplying it to the `MockMvcWebClientBuilder` as follows.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
|
||||
webClient = MockMvcWebClientBuilder
|
||||
webClient = MockMvcWebClientBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
|
|
@ -5094,27 +5094,27 @@ be displayed afterwards.
|
|||
If one of the fields were named "summary", then we might have something like the
|
||||
following repeated in multiple places within our tests.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute(summary);
|
||||
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute(summary);
|
||||
----
|
||||
|
||||
So what happens if we change the `id` to "smmry"? Doing so would force us to update all
|
||||
of our tests to incorporate this change! Of course, this violates the _DRY Principle_; so
|
||||
we should ideally extract this code into its own method as follows.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
|
||||
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
|
||||
setSummary(currentPage, summary);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
public void setSummary(HtmlPage currentPage, String summary) {
|
||||
public void setSummary(HtmlPage currentPage, String summary) {
|
||||
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
|
||||
summaryInput.setValueAttribute(summary);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
This ensures that we do not have to update all of our tests if we change the UI.
|
||||
|
|
@ -5122,9 +5122,9 @@ This ensures that we do not have to update all of our tests if we change the UI.
|
|||
We might even take this a step further and place this logic within an Object that
|
||||
represents the `HtmlPage` we are currently on.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public class CreateMessagePage {
|
||||
public class CreateMessagePage {
|
||||
|
||||
final HtmlPage currentPage;
|
||||
|
||||
|
|
@ -5154,7 +5154,7 @@ public class CreateMessagePage {
|
|||
public static boolean at(HtmlPage page) {
|
||||
return "Create Message".equals(page.getTitleText());
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Formerly, this pattern is known as the
|
||||
|
|
@ -5171,15 +5171,15 @@ includes a test dependency on `org.seleniumhq.selenium:selenium-htmlunit-driver`
|
|||
We can easily create a Selenium `WebDriver` that integrates with `MockMvc` using the
|
||||
`MockMvcHtmlUnitDriverBuilder` as follows.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebDriver driver;
|
||||
WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.webAppContextSetup(context)
|
||||
.build();
|
||||
|
|
@ -5204,16 +5204,16 @@ Now we can use WebDriver as we normally would, but without the need to deploy ou
|
|||
application to a Servlet container. For example, we can request the view to create
|
||||
a message with the following.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
CreateMessagePage page = CreateMessagePage.to(driver);
|
||||
CreateMessagePage page = CreateMessagePage.to(driver);
|
||||
----
|
||||
|
||||
We can then fill out the form and submit it to create a message.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
ViewMessagePage viewMessagePage =
|
||||
ViewMessagePage viewMessagePage =
|
||||
page.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
|
||||
----
|
||||
|
||||
|
|
@ -5223,9 +5223,9 @@ Pattern_. As we mentioned in <<spring-mvc-test-server-htmlunit-webdriver-why>>,
|
|||
use the Page Object Pattern with HtmlUnit, but it is much easier with WebDriver. Let's
|
||||
take a look at our new `CreateMessagePage` implementation.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public class CreateMessagePage
|
||||
public class CreateMessagePage
|
||||
extends AbstractPage { // <1>
|
||||
|
||||
// <2>
|
||||
|
|
@ -5251,7 +5251,7 @@ public class CreateMessagePage
|
|||
driver.get("http://localhost:9990/mail/messages/form");
|
||||
return PageFactory.initElements(driver, CreateMessagePage.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<1> The first thing you will notice is that `CreateMessagePage` extends the
|
||||
|
|
@ -5277,39 +5277,39 @@ annotation to look up our submit button using a css selector, *input[type=submit
|
|||
Finally, we can verify that a new message was created successfully. The following
|
||||
assertions use the https://code.google.com/p/fest/[FEST assertion library].
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
|
||||
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
|
||||
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
|
||||
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
|
||||
----
|
||||
|
||||
We can see that our `ViewMessagePage` allows us to interact with our custom domain
|
||||
model. For example, it exposes a method that returns a `Message` object.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
public Message getMessage() throws ParseException {
|
||||
public Message getMessage() throws ParseException {
|
||||
Message message = new Message();
|
||||
message.setId(getId());
|
||||
message.setCreated(getCreated());
|
||||
message.setSummary(getSummary());
|
||||
message.setText(getText());
|
||||
return message;
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
We can then leverage the rich domain objects in our assertions.
|
||||
|
||||
Lastly, don't forget to _close_ the `WebDriver` instance when the test is complete.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@After
|
||||
public void destroy() {
|
||||
@After
|
||||
public void destroy() {
|
||||
if (driver != null) {
|
||||
driver.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
For additional information on using WebDriver, refer to the Selenium
|
||||
|
|
@ -5322,29 +5322,29 @@ In the examples so far, we have used `MockMvcHtmlUnitDriverBuilder` in the simpl
|
|||
possible, by building a `WebDriver` based on the `WebApplicationContext` loaded for us by
|
||||
the Spring TestContext Framework. This approach is repeated here.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
@Autowired
|
||||
WebApplicationContext context;
|
||||
|
||||
WebDriver driver;
|
||||
WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.webAppContextSetup(context)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
We can also specify additional configuration options.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
WebDriver driver;
|
||||
WebDriver driver;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@Before
|
||||
public void setup() {
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
// demonstrates applying a MockMvcConfigurer (Spring Security)
|
||||
.webAppContextSetup(context, springSecurity())
|
||||
|
|
@ -5360,14 +5360,14 @@ public void setup() {
|
|||
As an alternative, we can perform the exact same setup by configuring the `MockMvc`
|
||||
instance separately and supplying it to the `MockMvcHtmlUnitDriverBuilder` as follows.
|
||||
|
||||
[source,java]
|
||||
[source,java,indent=0]
|
||||
----
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
MockMvc mockMvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
driver = MockMvcHtmlUnitDriverBuilder
|
||||
.mockMvcSetup(mockMvc)
|
||||
// for illustration only - defaults to ""
|
||||
.contextPath("")
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
This part of the documentation covers support for reactive stack, web applications built on a
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:api-spring-framework: {doc-root}/spring-framework/docs/{spring-version}/javadoc-api/org/springframework
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:docinfo1:
|
||||
|
||||
This part of the documentation covers support for Servlet stack, web applications built on the
|
||||
|
|
|
|||
|
|
@ -204,14 +204,14 @@ headers. `MultipartBodyBuilder` can be used to build the parts:
|
|||
[source,java,intent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||
builder.part("fieldPart", "fieldValue");
|
||||
builder.part("filePart", new FileSystemResource("...logo.png"));
|
||||
builder.part("jsonPart", new Person("Jason"));
|
||||
MultipartBodyBuilder builder = new MultipartBodyBuilder();
|
||||
builder.part("fieldPart", "fieldValue");
|
||||
builder.part("filePart", new FileSystemResource("...logo.png"));
|
||||
builder.part("jsonPart", new Person("Jason"));
|
||||
|
||||
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
|
||||
MultiValueMap<String, HttpEntity<?>> parts = builder.build();
|
||||
|
||||
Mono<Void> result = client.post()
|
||||
Mono<Void> result = client.post()
|
||||
.uri("/path", id)
|
||||
.syncBody(parts)
|
||||
.retrieve()
|
||||
|
|
@ -275,7 +275,6 @@ decoding HTTP messages:
|
|||
WebClient webClient = WebClient.builder()
|
||||
.exchangeStrategies(strategies)
|
||||
.build();
|
||||
|
||||
----
|
||||
|
||||
The builder can be used to insert <<webflux-client-filter>>.
|
||||
|
|
@ -307,11 +306,9 @@ build a new `WebClient`, based on, but without affecting the current instance:
|
|||
----
|
||||
WebClient client = WebClient.builder()
|
||||
.filter((request, next) -> {
|
||||
|
||||
ClientRequest filtered = ClientRequest.from(request)
|
||||
.header("foo", "bar")
|
||||
.build();
|
||||
|
||||
return next.exchange(filtered);
|
||||
})
|
||||
.build();
|
||||
|
|
@ -323,7 +320,7 @@ build a new `WebClient`, based on, but without affecting the current instance:
|
|||
[subs="verbatim,quotes"]
|
||||
----
|
||||
|
||||
// static import of ExchangeFilterFunctions.basicAuthentication
|
||||
// static import of ExchangeFilterFunctions.basicAuthentication
|
||||
|
||||
WebClient client = WebClient.builder()
|
||||
.filter(basicAuthentication("user", "pwd"))
|
||||
|
|
|
|||
|
|
@ -28,17 +28,16 @@ Creating a WebSocket server is as simple as implementing `WebSocketHandler`:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
import org.springframework.web.reactive.socket.WebSocketHandler;
|
||||
import org.springframework.web.reactive.socket.WebSocketSession;
|
||||
|
||||
public class MyWebSocketHandler implements WebSocketHandler {
|
||||
public class MyWebSocketHandler implements WebSocketHandler {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(WebSocketSession session) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Spring WebFlux provides a `WebSocketHandlerAdapter` that can adapt WebSocket
|
||||
|
|
@ -49,8 +48,8 @@ adapter is registered as a bean, you can map requests to your handler, for examp
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
static class WebConfig {
|
||||
@Configuration
|
||||
static class WebConfig {
|
||||
|
||||
@Bean
|
||||
public HandlerMapping handlerMapping() {
|
||||
|
|
@ -67,8 +66,7 @@ static class WebConfig {
|
|||
public WebSocketHandlerAdapter handlerAdapter() {
|
||||
return new WebSocketHandlerAdapter();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
|
@ -98,8 +96,8 @@ WebSocket options when running on Tomcat:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
static class WebConfig {
|
||||
@Configuration
|
||||
static class WebConfig {
|
||||
|
||||
@Bean
|
||||
public WebSocketHandlerAdapter handlerAdapter() {
|
||||
|
|
@ -112,8 +110,7 @@ static class WebConfig {
|
|||
strategy.setMaxSessionIdleTimeout(0L);
|
||||
return new HandshakeWebSocketService(strategy);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Check the upgrade strategy for your server to see what options are available. Currently
|
||||
|
|
|
|||
|
|
@ -47,26 +47,23 @@ the `DispatcherServlet`. This class is auto-detected by the Servlet container
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
public class MyWebApplicationInitializer implements WebApplicationInitializer {
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletCxt) {
|
||||
|
||||
// Load Spring web application configuration
|
||||
AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext();
|
||||
cxt.register(AppConfig.class);
|
||||
cxt.refresh();
|
||||
AnnotationConfigWebApplicationContext ac = new AnnotationConfigWebApplicationContext();
|
||||
ac.register(AppConfig.class);
|
||||
ac.refresh();
|
||||
|
||||
// Create DispatcherServlet
|
||||
DispatcherServlet servlet = new DispatcherServlet(cxt);
|
||||
|
||||
// Register and map the Servlet
|
||||
// Create and register the DispatcherServlet
|
||||
DispatcherServlet servlet = new DispatcherServlet(ac);
|
||||
ServletRegistration.Dynamic registration = servletCxt.addServlet("app", servlet);
|
||||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/app/*");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
|
|
@ -154,12 +151,12 @@ Below is example configuration with a `WebApplicationContext` hierarchy:
|
|||
|
||||
@Override
|
||||
protected Class<?>[] getRootConfigClasses() {
|
||||
return new Class[] { RootConfig.class };
|
||||
return new Class<?[] { RootConfig.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getServletConfigClasses() {
|
||||
return new Class[] { App1Config.class };
|
||||
return new Class<?[] { App1Config.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -319,7 +316,6 @@ example of registering a `DispatcherServlet`:
|
|||
registration.setLoadOnStartup(1);
|
||||
registration.addMapping("/");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -351,7 +347,6 @@ This is recommended for applications that use Java-based Spring configuration:
|
|||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -379,7 +374,6 @@ If using XML-based Spring configuration, you should extend directly from
|
|||
protected String[] getServletMappings() {
|
||||
return new String[] { "/" };
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -1933,13 +1927,10 @@ following the `@ModelAttribute` argument:
|
|||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) {
|
||||
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -1955,21 +1946,20 @@ controller or alternatively use the `binding` flag on the annotation:
|
|||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@ModelAttribute
|
||||
public AccountForm setUpForm() {
|
||||
public AccountForm setUpForm() {
|
||||
return new AccountForm();
|
||||
}
|
||||
}
|
||||
|
||||
@ModelAttribute
|
||||
public Account findAccount(@PathVariable String accountId) {
|
||||
@ModelAttribute
|
||||
public Account findAccount(@PathVariable String accountId) {
|
||||
return accountRepository.findOne(accountId);
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("update")
|
||||
public String update(@Valid AccountUpdateForm form, BindingResult result,
|
||||
@PostMapping("update")
|
||||
public String update(@Valid AccountUpdateForm form, BindingResult result,
|
||||
**@ModelAttribute(binding=false)** Account account) {
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
In addition to data binding you can also invoke validation using your own custom
|
||||
|
|
@ -1982,14 +1972,11 @@ subsequently reported back to the user:
|
|||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) {
|
||||
|
||||
new PetValidator().validate(pet, result);
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -2001,13 +1988,10 @@ annotation:
|
|||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) {
|
||||
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -2327,31 +2311,32 @@ object and converting from an object to the HTTP response body. The
|
|||
`RequestMappingHandlerAdapter` supports the `@RequestBody` annotation with the following
|
||||
default `HttpMessageConverters`:
|
||||
|
||||
* `ByteArrayHttpMessageConverter` converts byte arrays.
|
||||
* `StringHttpMessageConverter` converts strings.
|
||||
* `FormHttpMessageConverter` converts form data to/from a MultiValueMap<String, String>.
|
||||
* `SourceHttpMessageConverter` converts to/from a javax.xml.transform.Source.
|
||||
* `ByteArrayHttpMessageConverter` converts byte arrays
|
||||
* `StringHttpMessageConverter` converts strings
|
||||
* `FormHttpMessageConverter` converts form data to/from a `MultiValueMap<String, String>`
|
||||
* `SourceHttpMessageConverter` converts to/from a `javax.xml.transform.Source``
|
||||
|
||||
For more information on these converters, see <<integration.adoc#rest-message-conversion,
|
||||
Message Converters>>. Also note that if using the MVC namespace or the MVC Java config, a
|
||||
wider range of message converters are registered by default. See <<mvc-config-enable>> for
|
||||
more information.
|
||||
Message Converters>>. Also note that if using the MVC namespace or the MVC Java config,
|
||||
a wider range of message converters are registered by default, including default JSON
|
||||
and XML payload converters (if e.g. Jackson, Gson and/or JAXB2 are present at runtime).
|
||||
See <<mvc-config-enable>> for more information on MVC setup options.
|
||||
|
||||
If you intend to read and write XML, you will need to configure the
|
||||
`MarshallingHttpMessageConverter` with a specific `Marshaller` and an `Unmarshaller`
|
||||
implementation from the `org.springframework.oxm` package. The example below shows how
|
||||
to do that directly in your configuration but if your application is configured through
|
||||
the MVC namespace or the MVC Java config see <<mvc-config-enable>> instead.
|
||||
For a custom example, if you intend to read and write XML using the `spring-oxm` module,
|
||||
you need to configure the `MarshallingHttpMessageConverter` with a specific `Marshaller`
|
||||
implementation from the `org.springframework.oxm` package. The example below shows how to
|
||||
do that directly in your configuration but if your application is configured through the
|
||||
MVC namespace or the MVC Java config see <<mvc-config-enable>> instead.
|
||||
|
||||
[source,xml,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
|
||||
<property name="messageConverters">
|
||||
<util:list id="beanList">
|
||||
<list>
|
||||
<ref bean="stringHttpMessageConverter"/>
|
||||
<ref bean="marshallingHttpMessageConverter"/>
|
||||
</util:list>
|
||||
<list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
|
@ -2360,11 +2345,10 @@ the MVC namespace or the MVC Java config see <<mvc-config-enable>> instead.
|
|||
|
||||
<bean id="marshallingHttpMessageConverter"
|
||||
class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
|
||||
<property name="marshaller" ref="castorMarshaller"/>
|
||||
<property name="unmarshaller" ref="castorMarshaller"/>
|
||||
<constructor-arg ref="xstreamMarshaller"/>
|
||||
</bean>
|
||||
|
||||
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>
|
||||
<bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"/>
|
||||
----
|
||||
|
||||
An `@RequestBody` method parameter can be annotated with `@Valid`, in which case it will
|
||||
|
|
@ -2833,7 +2817,6 @@ Spring MVC also provides a mechanism for building links to controller methods. F
|
|||
|
||||
@GetMapping("/bookings/{booking}")
|
||||
public String getBooking(@PathVariable Long booking) {
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
|
@ -4029,7 +4012,7 @@ In XML use the `<mvc:annotation-driven>` element:
|
|||
|
||||
The above registers a number of Spring MVC
|
||||
<<mvc-servlet-special-bean-types,infrastructure beans>> also adapting to dependencies
|
||||
available on the classpath -- for JSON, XML, etc.
|
||||
available on the classpath: e.g. payload converters for JSON, XML, etc.
|
||||
|
||||
|
||||
|
||||
|
|
@ -4047,7 +4030,6 @@ In Java config implement `WebMvcConfigurer` interface:
|
|||
public class WebConfig implements WebMvcConfigurer {
|
||||
|
||||
// Implement configuration methods...
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4079,7 +4061,6 @@ In Java config, register custom formatters and converters:
|
|||
public void addFormatters(FormatterRegistry registry) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4153,7 +4134,6 @@ In Java config, you can customize the global `Validator` instance:
|
|||
public Validator getValidator(); {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4219,7 +4199,6 @@ In Java config, register interceptors to apply to incoming requests:
|
|||
registry.addInterceptor(new ThemeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
|
||||
registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4324,7 +4303,6 @@ Below is an example that adds Jackson JSON and XML converters with a customized
|
|||
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
|
||||
converters.add(new MappingJackson2XmlHttpMessageConverter(builder.xml().build()));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4406,7 +4384,6 @@ An example of forwarding a request for `"/"` to a view called `"home"` in Java:
|
|||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName("home");
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4442,7 +4419,6 @@ JSON rendering:
|
|||
registry.enableContentNegotiation(new MappingJackson2JsonView());
|
||||
registry.jsp();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4506,7 +4482,6 @@ In Java config simply add the respective "Configurer" bean:
|
|||
configurer.setTemplateLoaderPath("/WEB-INF/");
|
||||
return configurer;
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4541,7 +4516,6 @@ In Java config:
|
|||
.addResourceLocations("/public", "classpath:/static/")
|
||||
.setCachePeriod(31556926);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4584,7 +4558,6 @@ For example in Java config;
|
|||
.resourceChain(true)
|
||||
.addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4647,7 +4620,6 @@ To enable the feature using the default setup use:
|
|||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
|
||||
configurer.enable();
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -4797,7 +4769,6 @@ hook of the Spring `ApplicationContext`:
|
|||
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
|
|||
|
|
@ -828,8 +828,8 @@ Consider also customizing these server-side SockJS related properties (see Javad
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Configuration
|
||||
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
|
||||
@Configuration
|
||||
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
|
|
@ -840,8 +840,7 @@ public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
|
|||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
|
|
@ -1470,7 +1469,6 @@ In Java config:
|
|||
registry.setApplicationDestinationPrefixes("/app");
|
||||
registry.setPathMatcher(new AntPathMatcher("."));
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -1489,12 +1487,12 @@ In XML config:
|
|||
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
|
||||
|
||||
<websocket:message-broker application-destination-prefix="/app" path-matcher="pathMatcher">
|
||||
<websocket:stomp-endpoint path="/stomp" />
|
||||
<websocket:stomp-endpoint path="/stomp"/>
|
||||
<websocket:simple-broker prefix="/topic, /queue"/>
|
||||
</websocket:message-broker>
|
||||
|
||||
<bean id="pathMatcher" class="org.springframework.util.AntPathMatcher">
|
||||
<constructor-arg index="0" value="." />
|
||||
<constructor-arg index="0" value="."/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
@ -1511,8 +1509,8 @@ And below is a simple example to illustrate a controller with "." separator:
|
|||
|
||||
@MessageMapping("bar.{baz}")
|
||||
public void handleBaz(@DestinationVariable String baz) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
|
|
@ -1619,18 +1617,14 @@ user and associate it with subsequent STOMP messages on the same session:
|
|||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
registration.setInterceptors(new ChannelInterceptorAdapter() {
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
|
||||
StompHeaderAccessor accessor =
|
||||
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
|
||||
|
||||
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
|
||||
Authentication user = ... ; // access authentication header(s)
|
||||
accessor.setUser(user);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
});
|
||||
|
|
@ -1674,8 +1668,8 @@ the class-level to share a common destination):
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class PortfolioController {
|
||||
@Controller
|
||||
public class PortfolioController {
|
||||
|
||||
@MessageMapping("/trade")
|
||||
@SendToUser("/queue/position-updates")
|
||||
|
|
@ -1683,7 +1677,7 @@ public class PortfolioController {
|
|||
// ...
|
||||
return tradeResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
If the user has more than one session, by default all of the sessions subscribed
|
||||
|
|
@ -1694,8 +1688,8 @@ setting the `broadcast` attribute to false, for example:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class MyController {
|
||||
@Controller
|
||||
public class MyController {
|
||||
|
||||
@MessageMapping("/action")
|
||||
public void handleAction() throws Exception{
|
||||
|
|
@ -1708,10 +1702,9 @@ public class MyController {
|
|||
// ...
|
||||
return appError;
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
While user destinations generally imply an authenticated user, it isn't required
|
||||
|
|
@ -1859,10 +1852,10 @@ To begin create and configure `WebSocketStompClient`:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
WebSocketClient webSocketClient = new StandardWebSocketClient();
|
||||
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
|
||||
stompClient.setMessageConverter(new StringMessageConverter());
|
||||
stompClient.setTaskScheduler(taskScheduler); // for heartbeats
|
||||
WebSocketClient webSocketClient = new StandardWebSocketClient();
|
||||
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
|
||||
stompClient.setMessageConverter(new StringMessageConverter());
|
||||
stompClient.setTaskScheduler(taskScheduler); // for heartbeats
|
||||
----
|
||||
|
||||
In the above example `StandardWebSocketClient` could be replaced with `SockJsClient`
|
||||
|
|
@ -1875,9 +1868,9 @@ Next establish a connection and provide a handler for the STOMP session:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
String url = "ws://127.0.0.1:8080/endpoint";
|
||||
StompSessionHandler sessionHandler = new MyStompSessionHandler();
|
||||
stompClient.connect(url, sessionHandler);
|
||||
String url = "ws://127.0.0.1:8080/endpoint";
|
||||
StompSessionHandler sessionHandler = new MyStompSessionHandler();
|
||||
stompClient.connect(url, sessionHandler);
|
||||
----
|
||||
|
||||
When the session is ready for use the handler is notified:
|
||||
|
|
@ -1987,9 +1980,9 @@ scope proxy mode for WebSocket-scoped beans:
|
|||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
----
|
||||
@Component
|
||||
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
public class MyBean {
|
||||
@Component
|
||||
@Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
public class MyBean {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
|
@ -2002,10 +1995,10 @@ public class MyBean {
|
|||
public void destroy() {
|
||||
// Invoked when the WebSocket session ends
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
public class MyController {
|
||||
@Controller
|
||||
public class MyController {
|
||||
|
||||
private final MyBean myBean;
|
||||
|
||||
|
|
@ -2018,7 +2011,7 @@ public class MyController {
|
|||
public void handle() {
|
||||
// this.myBean from the current WebSocket session
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
As with any custom scope, Spring initializes a new `MyBean` instance the first
|
||||
|
|
|
|||
Loading…
Reference in New Issue