Consistent use of tabs for sample code in the reference documentation

This commit is contained in:
Juergen Hoeller 2017-11-20 22:28:00 +01:00
parent 08c78554b9
commit 6f24c0de17
18 changed files with 1191 additions and 1213 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -83,7 +83,6 @@ some subset of it:
public void setUp() {
client = WebTestClient.bindToApplicationContext(context).build(); // <3>
}
}
----

View File

@ -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("")

View File

@ -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

View File

@ -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

View File

@ -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"))

View File

@ -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

View File

@ -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 {
// ...
}
}
----

View File

@ -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