Просмотр исходного кода

Revise instructions and add TODOs

Chris Beams 13 лет назад
Родитель
Сommit
b9ad6b227d
1 измененных файлов: 128 добавлений и 9 удалений
  1. 128
    9
      README.md

+ 128
- 9
README.md Просмотреть файл

@@ -23,6 +23,9 @@ Add the [Spring MVC](TODO) and [Jackson](http://jackson.codehaus.org) JSON libra
23 23
 
24 24
 ### Maven \[[copy complete `pom.xml` to clipboard](start/pom.xml)\]
25 25
 
26
+Add the following within the `<project>` section of your pom.xml file:
27
+
28
+`pom.xml`
26 29
 ```xml
27 30
 <dependencies>
28 31
     <dependency>
@@ -55,6 +58,9 @@ Add the [Spring MVC](TODO) and [Jackson](http://jackson.codehaus.org) JSON libra
55 58
 
56 59
 ### Gradle \[[copy complete `build.gradle` to clipboard](start/build.gradle)\]
57 60
 
61
+Add the following within the `dependencies { }` section of your build.gradle file:
62
+
63
+`build.gradle`
58 64
 ```groovy
59 65
 compile 'org.springframework:spring-webmvc:3.2.2.RELEASE'
60 66
 compile 'com.fasterxml.jackson.core:jackson-core:2.1.4'
@@ -114,11 +120,11 @@ public class HelloWorldWebAppInitializer extends AbstractAnnotationConfigDispatc
114 120
 }
115 121
 ```
116 122
 
117
-By extending [`AbstractAnnotationConfigDispatcherServletInitializer`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/servlet/support/AbstractAnnotationConfigDispatcherServletInitializer.html), our web application initializer will get a `DispatcherServlet` that is configured with [`@Configuration`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/context/annotation/Configuration.html)-annotated classes. All we must do is tell it where those configuration classes are and what path(s) to map `DispatcherServlet` to. 
123
+By extending [`AbstractAnnotationConfigDispatcherServletInitializer`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/servlet/support/AbstractAnnotationConfigDispatcherServletInitializer.html), our web application initializer will get a `DispatcherServlet` that is configured with [`@Configuration`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/context/annotation/Configuration.html)-annotated classes. All we must do is tell it where those configuration classes are and what path(s) to map `DispatcherServlet` to.
118 124
 
119 125
 With regard to the servlet path mappings, `getServletMappings()` returns a single-entry array of `String` specifying that `DispatcherServlet` should be mapped to "/".
120 126
 
121
-The `getRootConfigClasses()` and `getServletConfigClasses()` methods specify the configuration classes. The `Class` array returned from `getRootConfigClasses()` specifies the classes for the root context provided to [`ContextLoaderListener`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/context/ContextLoaderListener.html). Similarly, the `Class` array returned from `getServletConfigClasses()` specifies the classes for the servlet application context provided to `DispatcherServlet`. 
127
+The `getRootConfigClasses()` and `getServletConfigClasses()` methods specify the configuration classes. The `Class` array returned from `getRootConfigClasses()` specifies the classes for the root context provided to [`ContextLoaderListener`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/context/ContextLoaderListener.html). Similarly, the `Class` array returned from `getServletConfigClasses()` specifies the classes for the servlet application context provided to `DispatcherServlet`.
122 128
 
123 129
 For our purposes there will only be a servlet application context, so `getRootConfigClasses()` returns `null`. `getServletConfigClasses()`, however, specifies `HelloWorldConfiguration` as the only configuration class.
124 130
 
@@ -127,7 +133,7 @@ Creating a Representation Class
127 133
 -------------------------------
128 134
 With the essential Spring MVC configuration out of the way, it's time to get to the nuts and bolts of our REST service by creating a resource representation class and an endpoint controller.
129 135
 
130
-Before we get too carried away with building the endpoint controller, we need to give some thought to what our API will look like. 
136
+Before we get too carried away with building the endpoint controller, we need to give some thought to what our API will look like.
131 137
 
132 138
 What we want is to handle GET requests for /hello-world, optionally with a name query parameter. In response to such a request, we'd like to send back JSON, representing a greeting, that looks something like this:
133 139
 
@@ -198,17 +204,130 @@ public class HelloWorldController {
198 204
 }
199 205
 ```
200 206
 
201
-The key difference between a human-facing controller and a REST endpoint controller is in how the response is created. Rather than rely on a view (such as JSP) to render model data in HTML, an endpoint controller simply returns the data to be written directly to the body of the response. 
207
+The key difference between a human-facing controller and a REST endpoint controller is in how the response is created. Rather than rely on a view (such as JSP) to render model data in HTML, an endpoint controller simply returns the data to be written directly to the body of the response.
202 208
 
203 209
 The magic is in the [`@ResponseBody`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html) annotation. `@ResponseBody` tells Spring MVC to not render a model into a view, but rather to write the returned object into the response body. It does this by using one of Spring's message converters. Because Jackson 2 is in the classpath, this means that [`MappingJackson2HttpMessageConverter`](http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.html) will handle the conversion of Greeting to JSON if the request's `Accept` header specifies that JSON should be returned.
204 210
 
205
-Building and Running the REST Service
211
+
212
+Creating an executable main class
213
+---------------------------------
214
+_**TODO**: explain._
215
+
216
+`src/main/java/hello/Main.java`
217
+```java
218
+package hello;
219
+
220
+import java.util.HashSet;
221
+
222
+import org.apache.catalina.Context;
223
+import org.apache.catalina.core.AprLifecycleListener;
224
+import org.apache.catalina.core.StandardServer;
225
+import org.apache.catalina.startup.Tomcat;
226
+import org.springframework.web.SpringServletContainerInitializer;
227
+
228
+public class Main {
229
+
230
+    public static void main(String[] args) throws Exception {
231
+        Tomcat tomcat = new Tomcat();
232
+        tomcat.setPort(8080);
233
+        tomcat.setBaseDir(".");
234
+        tomcat.getHost().setAppBase("/");
235
+
236
+        // Add AprLifecycleListener
237
+        StandardServer server = (StandardServer)tomcat.getServer();
238
+        AprLifecycleListener listener = new AprLifecycleListener();
239
+        server.addLifecycleListener(listener);
240
+
241
+        Context context = tomcat.addWebapp("/", "/");
242
+        HashSet<Class<?>> classes = new HashSet<Class<?>>();
243
+        classes.add(HelloWorldWebAppInitializer.class);
244
+        context.addServletContainerInitializer(new SpringServletContainerInitializer(), classes);
245
+        tomcat.start();
246
+        tomcat.getServer().await();
247
+    }
248
+}
249
+```
250
+
251
+
252
+
253
+Building an executable JAR
254
+--------------------------
255
+
256
+Add the following to the <build><plugins> section of your pom.xml file:
257
+
258
+`pom.xml`
259
+```xml
260
+<plugin>
261
+    <groupId>org.apache.maven.plugins</groupId>
262
+    <artifactId>maven-shade-plugin</artifactId>
263
+    <version>1.6</version>
264
+    <configuration>
265
+        <createDependencyReducedPom>true</createDependencyReducedPom>
266
+        <filters>
267
+            <filter>
268
+                <artifact>*:*</artifact>
269
+                <excludes>
270
+                    <exclude>META-INF/*.SF</exclude>
271
+                    <exclude>META-INF/*.DSA</exclude>
272
+                    <exclude>META-INF/*.RSA</exclude>
273
+                </excludes>
274
+            </filter>
275
+        </filters>
276
+    </configuration>
277
+    <executions>
278
+        <execution>
279
+            <phase>package</phase>
280
+            <goals>
281
+                <goal>shade</goal>
282
+            </goals>
283
+            <configuration>
284
+                <transformers>
285
+                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
286
+                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
287
+                        <mainClass>hello.Main</mainClass>
288
+                    </transformer>
289
+                </transformers>
290
+            </configuration>
291
+        </execution>
292
+    </executions>
293
+</plugin>
294
+```
295
+
296
+The following will produce a single executable JAR file containing all necessary dependency classes:
297
+
298
+```
299
+$ mvn package
300
+```
301
+
302
+_**TODO:** this produces a bunch of the following. Fix?_
303
+```
304
+[WARNING] We have a duplicate javax/el/ExpressionFactory$4.class in /Users/cbeams/.m2/repository/org/apache/tomcat/tomcat-el-api/7.0.39/tomcat-el-api-7.0.39.jar
305
+```
306
+
307
+
308
+Running the Service
206 309
 -------------------------------------
207
->**NOTE**: This section is a very important section, because it shows the user how all of the work done up to this point comes together and runs. The challenge here, however, is that there's no *easy* way to run this application. Gradle's Jetty plugin seems easy and natural, but it uses an older, non-Servlet 3 version of Jetty, so the application initializer will not work. There is a Gradle Tomcat plugin, but it's quite involved setup-wise. And loading this into any IDE and running it is far more involved than either of the Gradle-based options. It would be really nice to leverage Spring Bootstrap/Catalyst for running the sample. That's likely what will happen, but at this point it's too risky of an option until bootstrap/catalyst stabilizes.
208 310
 
209
-Next Steps
210
-----------
211
-Congratulations! You have just developed a simple REST service using Spring. This is a basic foundation for building a complete REST API in Spring. 
311
+```
312
+$ java -jar target/gs-rest-service-0.0.1-SNAPSHOT.jar
313
+
314
+... service comes up ...
315
+```
316
+
317
+_**TODO:** this fails with the following. Fix._
318
+```
319
+Caused by: java.lang.IllegalArgumentException: Failed to register servlet with name 'dispatcher'.Check if there is another servlet registered under the same name.
320
+        at org.springframework.util.Assert.notNull(Assert.java:112)
321
+        at org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.registerDispatcherServlet(AbstractDispatcherServletInitializer.java:98)
322
+```
323
+
324
+_**TODO:** exercise the service in some meaningful way, e.g. with `curl`._
325
+
326
+Congratulations! You have just developed a simple REST service using Spring. This is a basic foundation for building a complete REST API in Spring.
327
+
328
+
329
+Related Resources
330
+-----------------
212 331
 
213 332
 There's more to building REST services than is covered here. You may want to continue your exploration of Spring and REST with the following Getting Started guides:
214 333