PROFESSIONAL JAVA FOR WEB APPLICATIONS (2014)
Part II Adding Spring Framework Into the Mix
· CHAPTER 12: Introducing Spring Framework
· CHAPTER 13: Replacing Your Servlets with Controllers
· CHAPTER 14: Using Services and Repositories to Support Your Controllers
· CHAPTER 15: Internationalizing Your Application with Spring Framework i18n
· CHAPTER 16: Using JSR 349, Spring Framework, and Hibernate Validator for Bean Validation
· CHAPTER 17: Creating RESTful and SOAP Web Services
· CHAPTER 18: Using Messaging and Clustering for Flexibility and Reliability
Chapter 12 Introducing Spring Framework
IN THIS CHAPTER
· Understanding Spring Framework
· The benefits of Spring Framework
· What are application contexts?
· How to bootstrap Spring Framework
· How to configure Spring Framework
· How to use Bean Definition Profiles
WROX.COM CODE DOWNLOADS FOR THIS CHAPTER
You can find the wrox.com code downloads for this chapter at http://www.wrox.com/go/projavaforwebapps on the Download Code tab. The code for this chapter is divided into the following major examples:
· Spring-One-Context-XML-Config Project
· Spring-XML-Config Project
· Spring-Hybrid-Config Project
· Spring-Java-Config Project
NEW MAVEN DEPENDENCIES FOR THIS CHAPTER
In addition to the Maven dependencies introduced in previous chapters, you also need the following Maven dependencies:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
<scope>compile</scope>
</dependency>
WHAT IS SPRING FRAMEWORK?
Spring Framework is an application container for Java that supplies many useful features, such as Inversion of Control, Dependency Injection, abstract data access, transaction management, and more. It was conceived in 2002 in response to industry complaints that the Java EE specification was sorely lacking and very difficult to use. Its first major release in 2004 was a game changer for Java EE applications and was followed by 2.0 in 2006, 2.5 in 2007, 3.0 in 2009, 3.1 in 2011, and 3.2 in 2012. Released in December 2013, the latest major version, Spring Framework 4.0, contains many enhancements, including support for Java SE 8, Java EE 7, WebSockets, and Groovy 2.
Though Spring Framework rose from the Enterprise Edition world, it is by no means strictly restricted to an EE environment. Often considered an alternative to or supplement for Enterprise JavaBeans (EJB, a Java EE server-side component for building enterprise applications in a modular fashion using encapsulated Beans), the Spring Framework container can run in any Java EE Application Server, Java EE Web Container, or Java SE standalone application (including server daemons and desktop applications).
EJB, on the other hand, is an Application Server-specific framework not found in desktop applications or even Web Containers such as Apache Tomcat and Jetty. Spring Framework is more than just a replacement for EJB, however; it provides many features that to this day Java EE still does not have replacements for. Over the years, Spring has evolved from a framework to a community, spawning many related projects such as Spring Security (which you learn about in Part IV of this book), Spring Data (which you learn about in Part III), Spring Integration, Spring Batch, Spring Mobile, Spring for Android, Spring Social, Spring Boot, and Spring.NET (the C# .NET port of Spring Framework), just to name a few.
To be sure, Spring Framework is not a requirement to develop rich Java web applications. You can create full-featured web applications with everything you have learned in this book up to this point. Spring Framework is a productivity framework: It makes developing applications of all types in a rapid way with modular concepts and testable code easier. It abstracts away the concepts of “requests” and “responses” and enables you to write application code that can be utilized from many different interfaces. When used properly, Spring Framework is the most powerful tool in your Java development toolbox.
Inversion of Control and Dependency Injection
One of Spring Framework’s core features is support for the closely related concepts of Inversion of Control (IoC) and Dependency Injection (DI). IoC is a software design pattern whereby an assembler, in this case Spring Framework, binds object coupling at run time instead of compile time. When some component of program logic, Service A for example, depends on another component of program logic, Service B, that dependency is fulfilled when the application runs instead of Service A instantiating Service B directly (which would be bound at compile time). This enables application developers to program against a set of interfaces, which can be swapped out in different environments without recompiling the code. One place this is handy is in a testing environment: “Mock” or “test” services can be provided while running unit tests; then the same code can be deployed to a production environment with more “real” services.
Though this can theoretically be achieved in any number of ways, DI is the most common technique. Using DI, a piece of program code (in Spring Framework, a class) declares that it depends on another piece of program code (an interface), and at run time the assembler injects an instance (usually but not always a singleton) of that dependency.
Aspect-Oriented Programming
Because Spring Framework handles the instantiation and injection of dependencies, it can wrap instances of the dependencies it injects to decorate method calls with other behavior. For example, using Spring Security you can annotate methods to indicate security restrictions placed on those methods, and Spring Framework wraps those method calls with the security checks necessary to fulfill those security restrictions. You can also define your own cross-cutting concerns using aspects, and Spring Framework decorates all the appropriate methods with these concerns. Cross-cutting concerns are concerns (such as security concerns) that affect multiple components of a program, often without regard to what those components are. Aspect-oriented programming is a complement to object-oriented programming that enables application of these concerns through the definition of aspects, which specify how and when to apply the concerns. Spring Framework provides extensive tools for aspect-oriented programming, which you will learn about throughout the rest of the book.
Data Access and Transaction Management
Spring Framework provides a set of data access tools that simplify the extraction and persistence of Java objects in relational databases. Though these features make unit testing data access drastically easier, some vendor-specify SQL is still required. Spring Framework also provides extensive support for the Java Persistence API (JPA) and object-relational mappers such as Hibernate ORM, which you explore in Part III of this book. Through the Spring Data project, persisting objects in relational databases, and NoSQL databases such as MongoDB and Redis, becomes a simple task. Finally, Spring Framework supports a declarative transactional model whereby execution of an annotated method is wrapped within a transaction and rolled back if the method throws an exception. This is achieved using Spring Framework’s AOP support.
Application Messaging
In any application, messaging is an important concern that needs addressing. For example, certain parts of a program may need to know when another part of the program performs a specified action. The part of the program performing this action could simply depend on the parts interested in the action and call methods on all of them to notify them, but that type of tight coupling is hard to maintain and can get very messy quickly.
Spring Framework provides a loosely coupled messaging system that uses the publish-subscribe pattern: Components in a system announce that they are interested in certain messages by subscribing to them, and the producers of these messages publish the messages without caring who is subscribed. This is essentially how Twitter works: People who have interesting (or perhaps noninteresting) things to say tweet those things, and other people who are interested in those types of tweets follow them. Using Spring Framework, a Spring-managed bean subscribes to a particular message type by implementing a generic interface, and other Spring-managed objects publish those messages to Spring Framework, which delivers them to the subscribed parties. This system can be extended and configured to deliver messages across a cluster of applications as well.
Model-View-Controller Pattern for Web Applications
Spring Framework features a model-view-controller (MVC) pattern framework that simplifies the process of creating interactive web applications. Instead of manually dealing with the complexities of Servlets, HttpServletRequests, HttpServletResponses, and forwarding to JSPs, Spring handles these tasks for you. Each method in a controller class is mapped to a different request URL, method, or other property of a request. The model is passed from the controller to the view in the form of a Map<String, Object>. The Viewor view name (String) returned from the controller method causes Spring to forward to the appropriate JSP view. Request and URL path parameters are automatically converted to primitive or complex controller method arguments.
In addition to typical HTML views, Spring can automatically generate plain text views and file download views and XML or JSON entity views. Through all these features, Spring Framework greatly simplifies working in a Servlet container.
WHY SPRING FRAMEWORK?
In previous sections, you have learned about many of the features Spring Framework offers, so you may already understand some of the compelling reasons for using Spring Framework in your applications. Its feature set and simplification of the Servlet API are not the only advantages of Spring Framework. The patterns it encourages, which you learn more about throughout this part of the book, provide several benefits that you can undoubtedly appreciate as you strive to create powerful web applications.
Logical Code Groupings
Consider some of the more complex Servlets you have created in previous chapters of this book. Each had a single doGet or doPost method, or perhaps both, containing many if statements or switch blocks responsible for routing the request to any number of methods within the Servlet. In a significantly more complicated enterprise application, you would quickly find this pattern becoming unmanageable and extremely difficult to test. A Servlet handling user profiles, for example, could have dozens of methods, each with a different route logic established in the doGet or doPost method.
A possible solution might be to create dozens of Servlets instead. Although this would result in a more testable and maintainable code base, the number of Servlets could quickly become as unmanageable as the number of logic branches were previously. If your application contains hundreds of features, each with dozens of pages, you would quickly realize a code base ballooning to thousands of tiny Servlets, which, to many developers, is equally unwanted. If your Servlets could be mapped down to the individual method level instead of only at the class, many problems would be solved.
Using Spring’s Web MVC framework, a controller class acts very much like a Servlet with method-level mappings. Each method can have a distinct mapping to a particular URL, request method, parameter existence, header value, content type, and/or expected response type. While your unit tests focus on testing the small units of code that are your controller methods, controller classes can contain many mapped methods that are logically grouped together. Returning to the user profile example, the controller could have dozens of mapped methods representing the different actions that can be taken on user profiles, but no doGet or doPost method is necessary to route requests to the proper methods. Spring Framework handles all of this analysis and routing for you.
Multiple User Interfaces Utilizing One Code Base
In a real-world scenario, you may be asked to create an advanced application with thousands of features that should be accessible from desktop applications, web browsers, mobile web browsers, RESTful web services, and SOAP web services. Using only Servlets, this task would quickly become daunting. You would eventually end up duplicating a lot of code or creating your own system in which business logic was abstracted into a set of other classes used from many user interfaces.
Fortunately, Spring Framework provides just such a system, tested and ready for you to use with very little effort. Using Spring, business logic becomes encapsulated in a set of business objects called services. These services perform operations common to all user interfaces, such as ensuring that certain entity properties are properly specified. Your application contains a different set of controllers and views for each user interface, and these use the common business objects to perform critical operations. Controllers, then, are necessary to perform only user interface-specific operations, such as translating form submissions or JSON request bodies into entities and displaying the appropriate view to the user. Unit testing becomes easier, code is reused, and very little effort is necessary to achieve both.
UNDERSTANDING APPLICATION CONTEXTS
The Spring Framework container comes in the form of one or more application contexts, represented by the org.springframework.context.ApplicationContext interface. An application context manages a set of beans, Java objects that perform business logic, execute tasks, persist and retrieve persisted data, respond to HTTP requests, and more. Spring-managed beans are automatically eligible for dependency injection, message notification, scheduled method execution, bean validation, and other crucial Spring services.
A Spring application always has at least one application context, and sometimes that’s all it needs. However, it can also have a hierarchy of multiple application contexts. In such a hierarchy, any Spring-managed beans have access to beans in the same application context, in the parent application context, in the parent’s parent application context, and so forth. They do not have access to beans in sibling or child application contexts. This is useful for defining a set of shared application components while isolating other application components from each other. For example, you may want the user and administrative sections of a web application to have no access to each other, but undoubtedly you’ll have some shared resources that both sections need.
NOTE You work with Spring Framework extensively throughout the rest of the book, and you should keep the API documentation handy at all times. Be sure to bookmark the documentation so that you can reference it whenever you want to know more about a Spring interface, class, annotation, enum, or exception.
There are a number of interfaces that extend and classes that implement ApplicationContext:
· The ConfigurableApplicationContext interface is, as the name implies, configurable, whereas the base ApplicationContext is only readable.
· The org.springframework.web.context.WebApplicationContext and ConfigurableWebApplicationContext interfaces are intended for Java EE web applications running in Servlet containers and provide access to the underlying ServletContext and, if applicable,ServletConfig.
· The concrete classes ClassPathXmlApplicationContext and FileSystemXmlApplicationContext are designed to load the Spring configuration from XML files in a standalone application, whereas the XmlWebApplicationContext achieves the same within Java EE web applications.
· For programmatically configuring Spring using Java instead of XML, the AnnotationConfigApplicationContext and AnnotationConfigWebApplicationContext classes work in standalone and Java EE web applications, respectively.
In Java EE web applications, Spring handles web requests using a dispatcher Servlet, which delegates incoming requests to the appropriate controllers and translates request and response entities as needed. Your web application can have as many instances of theorg.springframework.web.servlet.DispatcherServlet class as makes sense for your use case.
Each DispatcherServlet instance gets its own application context, which has references to the web application’s ServletContext and its own ServletConfig. You might create multiple DispatcherServlets, for example, to separate your web user interface from your web services. Because none of these DispatcherServlets can access the application context for any other DispatcherServlets, it is often desirable to share certain beans (such as business objects or data access objects) in a common root application context. This application context, global to the entire web application, is the parent of all the DispatcherServlets’ application contexts and is created using the org.springframework.web.context.ContextLoaderListener. It, too, has a reference to the web application’s ServletContext, but because it does not belong to any particular Servlet, it does not have a ServletConfig reference.
Although the idea of having multiple DispatcherServlets is commonplace in web applications and is used throughout this book, it is only one configuration of infinite possibilities. In any standalone or web application, you can create whatever application context hierarchy your needs justify. As a general rule, you should always have one root application context from which all other application contexts inherit in one way or another, as shown in Figure 12-1.
FIGURE 12-1
BOOTSTRAPPING SPRING FRAMEWORK
In the world of computer software, everything must get bootstrapped at some point or another. Consider your desktop, notebook, or mobile computing device: When it turns on, a bootstrap device of some type (typically a BIOS or similar) initializes all the hardware, loads startup instructions from some location (such as the boot sector on a hard drive), and starts the operating system. In this sense, the hardware is a container for the operating system. The operating system is another container — for the software that runs within it.
C programs have a standardized bootstrap mechanism: the main() method. All runnable C programs must contain this method to be bootstrapped. A Java Virtual Machine executable, which typically is written in C or C++, contains this main() method (or the equivalent for the language it is written in). Language virtual machines, such as the JVM or the .NET Runtime, are containers for other software. For Java applications, instructions in the application manifest file (or provided at the command line) bootstrap the Java application by providing the name of a class with a public static void main(String...) method. Tomcat, like any other Java application, has a public static void main(String...) method, but Tomcat is yet again another container — a Servlet container for running Java EE web applications. These web applications contain a form of bootstrap instructions: deployment descriptors or meta-information in the form of annotations that instruct Tomcat how to run them.
Like all these examples, Spring Framework is yet again another container. It can run within any Java SE or EE container and serves as the run-time environment for your application. Also like all these examples, Spring must be bootstrapped: It must be started and given instructions for how to run the application it contains.
Configuring and bootstrapping Spring Framework are two different tasks that, independently of each other, can be handled in different ways. While the configuration tells Spring how to run the application it contains, the bootstrap process starts Spring and passes off the configuration instructions to it. In a Java SE application, there is only one way to bootstrap Spring: programmatically, usually within the application’s public static void main(String...) method. In a Java EE application, you have two choices: You can use the deployment descriptor to bootstrap Spring using XML, or you can programmatically bootstrap Spring within a javax.servlet.ServletContainerInitializer.
Using the Deployment Descriptor to Bootstrap Spring
In traditional Spring Framework applications, you always bootstrapped Spring in the Java EE deployment descriptor. At the very least, this requires an instance of the DispatcherServlet given a configuration file in the form of a contextConfigLocation init parameter and instructed to load on startup.
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servletContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
This creates a single Spring application context within the setting of the DispatcherServlet and instructs the Servlet container to initialize the DispatcherServlet at startup. When initialized, the DispatcherServlet loads the context configuration from the /WEB-INF/servletContext.xml file and starts the application context. Of course, this creates only one application context for your application, which, as previously explained, is not very flexible. A more complete descriptor bootstrap would look more like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rootContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servletContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
The ContextLoaderListener is initialized when the web application first starts (because it implements ServletContextListener, it initializes before any Servlets), loads the root application context configuration from the /WEB-INF/rootContext.xml file specified in thecontextConfigLocation context init parameter, and starts the root application context.
Note that the contextConfigLocation context init parameter is different from the contextConfigLocation Servlet init parameter for the DispatcherServlet. They do not conflict; the former applies to the entire Servlet context and the latter applies only to the particular Servlet for which it is specified. The root application context created by the listener is automatically set as the parent context for any application contexts created by DispatcherServlets.
Although there can be only one root application context of this nature in a web application, there can be as many Servlet application contexts as you can find uses for. In Chapter 17, you can see an example of this when you create a second DispatcherServlet for RESTful web services. You can also arbitrarily create other application contexts if needed, though that is typically not applicable in a web application.
Of course, these bootstrap examples have assumed that you are configuring Spring using XML files, which you explore in the “Configuring Spring Framework” section of this chapter. You can also configure Spring with Java instead of XML (also covered in the later section), and bootstrapping a Java configuration from the deployment descriptor is very much the same:
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.
AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.wrox.config.RootContextConfiguration</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.
AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.wrox.config.ServletContextConfiguration
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Normally the ContextLoaderListener and DispatcherServlet create instances of org.springframework.web.context.support.XmlWebApplicationContext, which expect Spring’s configuration to come in the form of an XML file. The previous example overrides this behavior and uses the AnnotationConfigWebApplicationContext instead. This context type expects programmatic context configurations, specified as class names (instead of file names) in the contextConfigLocation parameters.
Programmatically Bootstrapping Spring in an Initializer
Recall that in previous chapters you used a ServletContextListener to programmatically configure the Servlets and filters in your application. The downside of using this interface is that the listener’s contextInitialized method may be called after other listeners. Java EE 6 added a new interface called ServletContainerInitializer. Classes implementing ServletContainerInitializer have their onStartup method called when the application first starts, before any listeners are notified. This is the earliest possible point in the life cycle of the application. You do not configure ServletContainerInitializers in the deployment descriptor; instead, you use Java’s service provider system to declare a class or classes that implement ServletContainerInitializer by listing them, one on each line, in a file named/META-INF/services/javax.servlet.ServletContainerInitializer. For example, the following file contents list two classes that implement ServletContainerInitializer:
com.wrox.config.ContainerInitializerOne
com.wrox.config.ContainerInitializerTwo
The downside is that this file cannot exist directly within your application’s WAR file or exploded directory — you cannot place the file in your web application’s /META-INF/services directory. It must be within the /META-INF/services directory of a JAR file included in your application’s /WEB-INF/lib directory.
Spring Framework provides a bridge interface that makes this much simpler to achieve. The org.springframework.web.SpringServletContainerInitializer class implements ServletContainerInitializer, and because the JAR containing this class includes a service provider file listing the class’s name, its onStartup method is called when your application starts up. This class then scans your application for implementations of the org.springframework.web.WebApplicationInitializer interface and calls the onStartup method of any matching classes it finds. Within a WebApplicationInitializer implementation class, you can programmatically configure listeners, Servlets, filters, and more, all without writing a single line of XML. More important, you can bootstrap Spring from within this class.
public class Bootstrap implements WebApplicationInitializer
{
@Override
public void onStartup(ServletContext container)
{
XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
rootContext.setConfigLocation("/WEB-INF/rootContext.xml");
container.addListener(new ContextLoaderListener(rootContext));
XmlWebApplicationContext servletContext = new XmlWebApplicationContext();
servletContext.setConfigLocation("/WEB-INF/servletContext.xml");
ServletRegistration.Dynamic dispatcher = container.addServlet(
"springDispatcher", new DispatcherServlet(servletContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
This bootstrap is the functional equivalent of the previous deployment descriptor bootstrap that used the Spring XML configuration. The following bootstrap instead uses the Spring Java configuration for a pure Java bootstrap and configuration process.
public class Bootstrap implements WebApplicationInitializer
{
@Override
public void onStartup(ServletContext container)
{
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(com.wrox.config.RootContextConfiguration.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext servletContext =
new AnnotationConfigWebApplicationContext();
servletContext.register(com.wrox.config.ServletContextConfiguration
.class);
ServletRegistration.Dynamic dispatcher = container.addServlet(
"springDispatcher", new DispatcherServlet(servletContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Of course, you do not have to configure all your application contexts the same way. You can mix and match configuration methods used during the bootstrap process. The following example demonstrates this and also demonstrates how to bootstrap Spring in a standalone application, such as a desktop application or server daemon.
public class Bootstrap
{
public static void main(String... arguments)
{
ClassPathXmlApplicationContext rootContext =
new ClassPathXmlApplicationContext("com/wrox/config/rootContext.xml");
FileSystemXmlApplicationContext daemonContext =
new FileSystemXmlApplicationContext(
new String[] {"file:/path/to/daemonContext.xml"}, rootContext
);
AnnotationConfigApplicationContext forkedProcessContext =
new AnnotationConfigApplicationContext(
com.wrox.config.ProcessContextConfiguration.class
);
forkedProcessContext.setParent(rootContext);
rootContext.start();
rootContext.registerShutdownHook();
daemonContext.start();
daemonContext.registerShutdownHook();
forkedProcessContext.start();
forkedProcessContext.registerShutdownHook();
}
}
Notice the extra effort required to assign the rootContext as the parent context for the daemonContext and forkedProcessContext, and to call the start and registerShutdownHook methods. In a web application, the ContextLoaderListener and DispatcherServlet automatically configure the parent application contexts and call their start methods on application startup and their stop methods on application shutdown. In a standalone application, you must call the start method yourself, and then you must call stop when your application is shutting down. As an alternative to manually calling stop, you can call registerShutdownHook to register a shutdown callback with the JVM that will stop the application context automatically when the JVM begins to exit.
THE DISPATCHER SERVLET MAPPING
The examples you have seen so far, and will continue to see throughout this book, show the DispatcherServlet mapped to the URL pattern /. Over the years, there has been much confusion about mapping the DispatcherServlet to the root of the application URL, and it’s easy to find incorrect or incomplete information about it on forums, blogs, and other technical sites.
You can map the DispatcherServlet to just about any URL pattern you want. Some common approaches are to map it to the URL patterns /do/*, *.do, or *.action, and some sites use a *.html mapping to make pages appear more static than they are. There are some important things to note, however. Most important, don’t map a DispatcherServlet to the URL pattern /*. In most cases the URL pattern must either start with or end with an asterisk, but when mapping to the application root, the lone forward slash without an asterisk is sufficient to get the Servlet to respond to all URLs in your application and while still enabling the Servlet container’s JSP mechanism to handle JSP requests. A trailing asterisk causes the Servlet container to send even internal JSP requests to that Servlet, which is not desirable. (This is because the container maps its JSP handler Servlet after your Servlets have been mapped, giving it lower precedence.)
If you plan to map the DispatcherServlet to the application root, make sure you account for static resources such as HTML pages, CSS and JavaScript files, and images. Some online tutorials demonstrate how to set up Spring Framework to serve static resources, but doing so is not necessary and does not perform well. When any Servlet is mapped to the application root (without an asterisk), more-specific URL patterns always override it. So permitting your Servlet container to serve static resources is as simple as adding mappings for those resources to the Servlet named default (which all containers provide automatically). This can be accomplished in the deployment descriptor like so:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/*</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.gif</url-pattern>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
Or programmatically in a ServletContainerInitializer (WebApplicationInitializer) or ServletContextListener like so:
servletContext.getServletRegistration("default").addMapping(
"/resources/*", "*.css", "*.js", "*.png", "*.gif", "*.jpg"
);
The default Servlet does not have to be declared. The Servlet container implicitly declares it on your behalf. The URL patterns shown here are examples—only you can decide where you want to put your static resources and, thus, which URL patterns are appropriate.
CONFIGURING SPRING FRAMEWORK
Now that you know how to bootstrap Spring Framework, you are ready to learn about configuring it. The remainder of this chapter continues to use both XML and programmatic bootstrapping for its examples, but all future chapters in this book use only programmatic bootstrapping. As you have already seen, you can configure a Spring application context using either XML or Java. Not only can you configure different application contexts in a single application differently, you also can configure a single application context with a mixture of Java and XML. This is an uncommon scenario in modern applications, but it is required in some cases. For example, the Java configuration added in Spring Security 3.2 does not cover every configuration scenario, so for some examples in Part IV of this book you must use Spring Security’s XML configuration instead. Also, the Spring Web Services tools you learn about in Chapter 17 have no support for Java configuration at all. When configuring an application context that uses these tools, you can configure the application context using Java but import an XML configuration for Spring Security or Spring Web Services. In this chapter, you learn about all these things.
This section utilizes the Spring-One-Context-XML-Config, Spring-XML-Config, Spring-Hybrid-Config, and Spring-Java-Config projects on the wrox.com code download site. You can refer to these projects throughout this section or create the code on your own. For starters, all four projects have the same basic deployment descriptor code you’re used to seeing from Part I. You’ll continue to use this template deployment descriptor throughout the rest of the book:
<display-name>Spring Application</display-name>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspf</url-pattern>
<page-encoding>UTF-8</page-encoding>
<scripting-invalid>true</scripting-invalid>
<include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
<default-content-type>text/html</default-content-type>
</jsp-property-group>
</jsp-config>
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
<distributable />
You should also update log4j2.xml to make sure that all appropriate messages end up in your logs. In the following example all messages get written to the console and to the log file, and although most messages are limited to warnings, classes in the com.wrox, org.apache, and org.springframework packages log at the INFO level.
<root level="warn">
<appender-ref ref="Console" />
<appender-ref ref="WroxFileAppender" />
</root>
<logger name="com.wrox" level="info" />
<logger name="org.apache" level="info" />
<logger name="org.springframework" level="info" />
All four projects also include the very basic GreetingService interface and its GreetingServiceImpl implementation.
public class GreetingServiceImpl implements GreetingService
{
@Override
public String getGreeting(String name)
{
return "Hello, " + name + "!";
}
}
The HelloController that follows responds to web requests in all four projects. You do not need to be concerned with the semantics of these classes, the annotations in the controller, or the controller-service pattern at this time. You’ll learn about these things soon, but right now you should remember that with four identical base projects, you can configure Spring Framework many different ways and achieve the same ends.
@Controller
public class HelloController
{
private GreetingService greetingService;
@ResponseBody
@RequestMapping("/")
public String helloWorld()
{
return "Hello, World!";
}
@ResponseBody
@RequestMapping("/custom")
public String helloName(@RequestParam("name") String name)
{
return this.greetingService.getGreeting(name);
}
public void setGreetingService(GreetingService greetingService)
{
this.greetingService = greetingService;
}
}
Creating an XML Configuration
You’ll recall that Spring Framework manages beans, and that is the primary thing you’ll configure whenever you configure Spring Framework. You’ll write some of these beans yourself, like GreetingServiceImpl and HelloController. Others beans are defaultframework beans that come with Spring Framework, such as implementations of Spring’s ApplicationContext, ResourceLoader, BeanFactory, MessageSource, and ApplicationEventPublisher classes, just for starters.
To tell Spring how to configure all these beans, you use the <beans> XML namespace, demonstrated in the Spring-One-Context-XML-Config project. Take a look at its /WEB-INF/servletContext.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven />
<bean name="greetingServiceImpl" class="com.wrox.GreetingServiceImpl" />
<bean name="helloController" class="com.wrox.HelloController">
<property name="greetingService" ref="greetingServiceImpl" />
</bean>
</beans>
This simple XML file tells Spring to instantiate GreetingServiceImpl and HelloController and to inject the greetingServiceImpl bean into the helloController bean’s greetingService property. The <beans> element is a parent element for containing the Spring configuration. Within it you can use nearly any other Spring configuration element — generally speaking, however, the elements you use within a <beans> element almost always result in the creation of beans. You use a <bean> element to explicitly construct a bean of a given class, and you can specify constructor arguments and properties for that bean within the <bean> element’s sub-elements. The <mvc:annotation-driven> element instructs Spring to use annotations like @RequestMapping, @RequestBody, @RequestParam, @PathParam, and@ResponseBody to map requests to controller methods. Using the <mvc:annotation-driven> element actually causes certain beans to be created behind the scenes, but you don’t have to worry about them just yet. They exist to facilitate mapping requests to controller methods. You learn more about these framework beans throughout the next several chapters.
You can do many things with the @RequestMapping annotation and its partners, and you will explore these more in Chapter 13. For now, understand that the URL pattern / gets mapped to the HelloController’s helloWorld method and the URL pattern /custom to thehelloName method. These URL patterns are relative to the DispatcherServlet’s URL pattern, not to the web application root URL. However, because the DispatcherServlet maps to the application root in this case, it results in the URL patterns also being relative to the application root. If you had mapped the DispatcherServlet to /do/*, the URL patterns in the @RequestMapping annotations wouldn’t have changed, but in the browser address bar they would have /do in front of them.
You have created the Spring configuration, but you still need to bootstrap it. For that use the deployment descriptor, but you could just as easily use Java bootstrapping if desired. Notice the mapping to the default Servlet for ensuring that Tomcat handles static resources.
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resource/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>springDispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servletContext.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Now compile the project, start Tomcat, and go to http://localhost:8080/xml/ in your favorite browser. You should see the text “Hello, World!” on the screen. Next, try the URL http://localhost:8080/xml/custom?name=Nick. You should now see “Hello, Nick!” in the browser window. Alter the name parameter, and the output should change to match. Your first Spring Framework application is working!
As discussed in an earlier section, often it’s desirable to have two different application contexts in a web application. You have created only one here. A typical pattern places all the business logic-related classes in the root application context and the controllers in the Servlet application context. To demonstrate this, switch to the Spring-XML-Config project or make the simple changes yourself.
Remove the greetingServiceImpl bean declaration from the servletContext.xml file (if you don’t remove it, it will be instantiated twice) and create a new /WEB-INF/rootContext.xml file containing only that bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean name="greetingServiceImpl" class="com.wrox.GreetingServiceImpl" />
</beans>
You then just need to add the ContextLoaderListener to the deployment descriptor.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/rootContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
You now have two application contexts: The root application context contains the greetingServiceImpl bean; and the DispatcherServlet’s application context inherits all the root context’s beans, contains the helloController bean, and initializes annotation-driven controller mapping. To make things a little more interesting, tweak the request mapping on the helloName method of the controller.
@RequestMapping(value = "/", params = {"name"})
public String helloName(@RequestParam("name") String name)
This method is now mapped to the same URL as the helloWorld method, but it requires the name parameter to be present in the request. The mere presence or absence of the name parameter determines which method Spring sends the request to. To prove that point, compile and start your application again and try the http://localhost:8080/xml/ and http://localhost:8080/xml/?name=Nick URLs.
NOTE You probably noticed that Spring Framework wrote log messages to the debug console and to your application.log file. Is Spring using Log4j? Not directly. Spring uses Apache Commons Logging as its logging API. Because your project has the org.apache.logging.log4j:log4j-jcl Commons Logging Bridge artifact on its class path, events that Spring Framework logs to Commons Logging get passed to Log4j and written to your log configuration. Messages INFO or higher appear because you set the logging level for org.springframework to INFO in your Log4j configuration file.
Creating a Hybrid Configuration
As you can imagine, configuring Spring Framework with XML can get tedious, and the previous example didn’t even demonstrate the MultiActionController and SimpleUrlHandlerMapping classes — a legacy mechanism (removed in Spring Framework 4.0) for mapping requests to controllers that involved extremely verbose XML and strict method signatures accepting HttpServletRequests and HttpServletResponses. In a large enterprise application, you could define hundreds of beans, each requiring three or more lines of XML. This configuration method quickly looks no better than configuring Servlets using XML in the deployment descriptor.
If you’re a fan of the explicitness of XML but don’t want configuration files that long — configuration files that can become extremely hard to maintain — you can create a hybrid configuration that combines some of the best of both worlds.
At the core of this hybrid configuration are the concepts of component scanning and annotation configuration. Using component scanning, Spring scans the package or packages you specify for classes with specific annotations. Any classes (in these packages) annotated with @org.springframework.stereotype.Component become Spring-managed beans, meaning Spring instantiates them and injects their dependencies.
Other annotations qualify for this component scanning: Any annotation that is annotated with @Component becomes a component annotation, and any annotation that is annotated with another component annotation becomes a component annotation. Therefore, classes annotated with @Controller, @Repository, and @Service (all in the same package as @Component) also become Spring-managed beans. You can create your own component annotations, too. In Chapter 17 you’ll create @WebController and @RestControllerannotations marked with @Controller to distinguish normal controllers from RESTful web service controllers.
Another key annotation that works in conjunction with annotation configuration is @org.springframework.beans.factory.annotation.Autowired. You can annotate any private, protected, or public field, or a public mutator method that accepts one or more arguments, with @Autowired. @Autowired declares a dependency or dependencies that Spring should inject after instantiation, and it can also mark a constructor. Usually, Spring-managed beans must have zero-argument constructors, but in the presence of a single @Autowiredconstructor on a class, Spring uses that constructor and injects all the constructor arguments.
In any of these cases, if Spring cannot find a matching bean for a dependency, it throws and logs an exception and fails to start. Likewise, if it finds more than one matching bean for a dependency, it also throws and logs an exception and fails to start. You can avoid this second problem using either the @org.springframework.beans.factory.annotation.Qualifier or the @org.springframework.context.annotation.Primary annotations. When used on an @Autowired field, method, method parameter, or constructor parameter, @Qualifierenables you to specify the name of the bean that should be used. Conversely, you can mark a component-annotated bean with @Primary to indicate it should be preferred when it is one of multiple candidate beans that fulfill a dependency.
So, should you use fields, mutators, or constructors with @Autowired? That debate has raged for years. Some teams prefer having @Autowired fields because it involves the least amount of code. Other teams prefer using @Autowired constructors because it makes it impossible to construct an object without satisfying all its dependencies. Still others prefer using @Autowired mutators because, though it involves the most code, it avoids having non-private fields while also making unit testing easier because you don’t always have to mock every dependency just to construct an instance. Which approach you use in the real world is completely up to you, but in this book you will annotate fields in most cases so that the code examples take up fewer pages.
NOTE Spring Framework also supports certain Java EE annotations in place of its proprietary annotations. @javax.inject.Inject is completely synonymous with @Autowired, and nothing special is required to make it work. You can annotate any field, constructor, or method using @Inject just like you would with @Autowired. @javax.annotation.Resource is also treated synonymously with @Autowired. Likewise, @javax.inject.Named is the equivalent of @Qualifier and has the same effect on @Autowiredor @Inject properties as @Qualifier does. (Spring also has support for custom qualifiers using the @javax.inject.Qualifier meta-annotation.) Which annotations you use is largely up to you; however, the argument can be made that using javax.*annotations somewhat unties your application from Spring and makes it easier to switch to another framework at a later date.
As mentioned earlier in the section, Spring provides many default framework beans automatically, and sometimes your beans need instances of those beans. Generally, using framework beans is discouraged because doing so further ties your application to Spring Framework and makes it more difficult to switch to something else. However, you must use these beans when you want to use certain powerful Spring features, such as publish-subscribe messaging. You can ask for any of these beans to be @Autowired or @Injected and Spring will oblige, but many people prefer to obtain these beans differently to make the connection to Spring more apparent. For this, you have the org.springframework.beans.factory.Aware interfaces. Aware is simply a marker interface for other interfaces to extend indicating awareness of certain framework beans. For example, the org.springframework.context.ApplicationContextAware interface specifies a setApplicationContext method for providing the ApplicationContext instance of the current (not parent) context to your beans. (If you need to, you can then obtain the parent context by calling ApplicationContext’s getParent method.)
There are many other interfaces that extend Aware, but some of the most popular follow:
· ApplicationEventPublisherAware for obtaining the bean used to publish application events
· BeanFactoryAware for obtaining the BeanFactory with which you can manually retrieve or create beans
· EnvironmentAware for obtaining an Environment object that you can use to get properties from property sources
· MessageSourceAware for obtaining the internationalization message source
· ServletContextAware for obtaining the ServletContext in Java EE web application environments
· ServletConfigAware for obtaining the ServletConfig in DispatcherServlet web application context-managed beans
It’s also often desirable to perform some sort of initialization on a class after all its dependencies have been injected but before it is injected as a dependency in any other beans. This is easily achieved merely by implementing theorg.springframework.beans.factory.InitializingBean interface. After all configuration of your bean is complete (dependencies are injected and Aware methods have been called), Spring calls its afterPropertiesSet method. If you do not want to tie your application to this proprietary Spring interface, you can instead create exactly one public, void, zero-argument method annotated with @javax.annotation.PostContstruct. It will be called at the same time as afterPropertiesSet normally would.
Whichever you use, it will be called even if your bean declared no dependencies that needed injection. On the flip side, if your bean needs to shut down after references to it have gone away and before Spring shuts down, you can implementorg.springframework.beans.factory.DisposableBean’s destroy method or create your own public, void, zero-argument method annotated with @javax.annotation.PreDestroy.
NOTE If you want more advanced control over the life cycle of your beans than InitializingBean, @PostConstruct, DisposableBean, or @PreDestroy have to offer, take a look at the API documentation for the org.springframework.context.Lifecycle andSmartLifecycle interfaces.
Initiating component scanning and annotation configuration is a simple task using the existing deployment descriptor configuration and the rootContext.xml and servletContext.xml files. You can see this change in the Spring-Hybrid-Config project. In therootContext.xml configuration, simply remove all bean definitions and replace them with the <context:annotation-config> and <context:component-scan> elements:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:annotation-config />
<context:component-scan base-package="com.wrox" />
</beans>
The base-package attribute instructs Spring Framework to scan all classes on the class path in the package com.wrox or in any subpackages for @Component, @Controller, @Repository, and @Service. The similar servletContext.xml configuration file still contains the<mvc:annotation-driven> element:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.wrox" />
</beans>
You still have two remaining minor things to change:
· You must tell Spring to instantiate the GreetingServiceImpl, so annotate the class with @Service.
·@Service
public class GreetingServiceImpl implements GreetingService
· You need to tell Spring to inject the GreetingService into the HelloController, so annotate the mutator method with @Autowired.
· @Autowired
public void setGreetingService(GreetingService greetingService)
You may already have noticed a problem with this configuration, which you can observe by placing a breakpoint in the setGreetingService mutator before starting Tomcat. This break point is hit twice for two different instances of the HelloController andGreetingServiceImpl. Indeed, you can also see this in the application log file. Spring instantiates the GreetingServiceImpl and HelloController twice: once in the root application context and once in the DispatcherServlet’s application context. Why is this happening?
Remember that component scanning, by default, scans for all @Components, and you have component scanning enabled in both application contexts. What you really want is to separate your beans. The root application context should hold services, repositories, and other pieces of business logic, whereas the DispatcherServlet’s application context should contain web controllers. Fortunately, you have a simple way to alter the default component-scanning algorithm. In the Spring-Hybrid-Config project this has already been done for you. First, the rootContext.xml configuration adds an exclusion to the scanning:
<context:annotation-config />
<context:component-scan base-package="com.wrox">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
This exclude-filter tells Spring to scan for all @Components except @Controllers. Other than this exclusion, the default scan pattern still applies. The servletContext.xml configuration takes a slightly different approach and uses a whitelist instead of a blacklist to tell Spring which components to scan for:
<mvc:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.wrox" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
</context:component-scan>
In this case, the use-default-filters attribute set to false tells Spring to ignore its standard scanning pattern. Instead of the exclude-filter specified in the root context, an include-filter tells Spring to scan only for @Controllers. Even if use-default-filters is true, you can still use include-filters to add to the default scanning filters.
Although you’ve removed all the <bean> definitions from the configuration files, you can still use <bean> and component scanning together. You may have cases in which you cannot use component scanning to register a bean. One example of this is registering a class provided by a third party as a bean: Because you’ll have only a compiled version of the class, you can’t add Spring annotations to it. Another example is registering framework beans that aren’t registered by default, such as Java Persistence API tools. Any time you need to, you can still specify <bean> elements in your configuration to supplement what Spring finds through component scanning.
Compile the application and start up Tomcat from your IDE. Go to http://localhost:8080/hybrid/ and http://localhost:8080/hybrid/?name=John, and you’ll notice that the application works exactly as it did using an XML-only configuration.
Configuring Spring with Java Using @Configuration
How you configure Spring is largely a matter of personal preference. Some development teams prefer the verbosity and plain text descriptiveness of XML files. However, there are some distinct disadvantages to configuring Spring using XML:
· It’s hard to debug an XML configuration. You must download and attach the Spring Framework sources to your project as well as know where to place breakpoints and how to step through it. The source code for Spring is massive, and debugging it is no trivial task.
· It’s impossible to unit test an XML configuration. Sure, you can programmatically bootstrap your Spring configuration within a unit test, but this isn’t a unit test. Because doing this starts your entire application context and wires all your beans, it’s actually an integration test. You can’t test isolated units of your Spring configuration if it’s XML-based.
Spring Framework’s pure Java configuration enables you to programmatically configure your Spring container so that you can easily debug and unit test pieces of your configuration. This is also useful for development teams that simply don’t like XML.
Earlier in the chapter, you explored bootstrapping programmatic Spring configuration using the AnnotationConfigWebApplicationContext. When using this class, you register configuration classes with it via the register method. These configuration classes (which must be annotated @org.springframework.context.annotation.Configuration and which must have a default constructor) register beans through no-argument methods annotated @Bean. You can do many powerful things with @Configuration classes, and more important, you can unit test and debug each method, as necessary.
The @Configuration annotation is meta-annotated with @Component, meaning your @Configuration classes are eligible for dependency injection using @Autowired or @Inject; may implement any of the Aware interfaces, InitializingBean, or DisposableBean; and may have@PostConstruct and @PreDestroy methods. This is useful if a @Configuration class needs to directly access a framework bean or a bean created in another @Configuration class. It’s also useful if it needs to initialize two or more dependent beans after its own dependencies are injected but before Spring calls its @Bean methods.
The fact that @Configuration classes are @Components also means that your @Configuration classes are picked up automatically if you have component scanning enabled on the package that contains them. This can have both intended and unintended consequences. If your @Configuration class enables component scanning on the same package it resides in, your beans could be instantiated twice, which isn’t good.
On the other hand, if you divide your application into many modules, each module can have its own @Configuration, and you can create a core @Configuration that component scans for all the modules’ @Configurations. The key point to take from this is that you should always be careful when combining @Configuration and component scanning.
Often it is desirable to have externalized values that drive your configuration. For example, your application may require the use of a JNDI data source, but you wouldn’t want to hard code the name of that data source. Instead, it is preferable to store the name of the data source in some settings file loaded at configuration time. Using the @org.springframework.context.annotation.PropertySource annotation, you can do just that.
@Configuration
@PropertySource({
"classpath:com/wrox/config/settings.properties",
"file:config.properties"
})
public class ExampleConfiguration
...
To access the properties in the property source, you can use an injected org.springframework.core.env.Environment instance to obtain the values manually, or you can use the @org.springframework.beans.factory.annotation.Value annotation to have the property values injected automatically.
...
public class ExampleConfiguration
{
@Inject Environment environment;
@Value("my.property.key") String myPropertyValue;
...
You might not be able to or want to contain your entire configuration in a single class. For that matter, you might not be able to configure everything with Java. As discussed earlier, some tools may still require XML configuration. The@org.springframework.context.annotation.Import and @ImportResource annotations provide support for these needs.
@Configuration
@Import({ DatabaseConfiguration.class, ClusterConfiguration.class })
@ImportResource("classpath:com/wrox/config/spring-security.xml")
public class ExampleConfiguration
...
In the previous code snippet, Spring Framework will load the configuration contained in ExampleConfiguration as well as the configuration contained in DatabaseConfiguration, ClusterConfiguration, and spring-security.xml. It does not initialize and execute these configurations in a specific order; instead, it determines if any of the configurations depend on beans provided by any of the other configurations, and initializes the configurations in the proper order to fulfill these dependencies.
Spring Framework has many XML configuration features you’ve already used that do not involve <bean> definitions. These result in very specific framework bean definitions behind the scenes but enable you to specify the configuration with minimal effort and include things such as <mvc:annotation-driven> and <context:component-scan>. There are replacements for all these XML namespace features using a set of configuration-related annotations. You can mark any @Configuration-annotated class with the following annotations to enable those features. You will learn more about these annotations gradually throughout the rest of the book.
· @ComponentScan replaces <context:component-scan> and enables component scanning on the specified package or packages. Just like its counterpart, you can enable or disable default filters and specify include and exclude filters to fine-tune the component scanning algorithm.
· @EnableAspectJAutoProxy is the replacement for <aop:aspectj-autoproxy> and enables support for handling classes marked with AspectJ’s @Aspect annotation and advising methods as appropriate using aspect-oriented programming.
· @EnableAsync replaces parts of Spring’s <task:*> namespace and enables Spring’s asynchronous @Async method execution. When used with the AsyncConfigurer interface, your @Configuration can fine-tune the configuration of asynchronous behavior.
· @EnableCaching enables Spring’s annotation-driven cache-management features and replaces the <cache:*> namespace.
· @EnableLoadTimeWeaving replaces <context:load-time-weaver>. It changes how several features such as @Transactional, @Configurable, @Aspect, and more, work. In most cases you won’t often need this feature, but it is covered in Part III when you work with the Java Persistence API. Importantly, it is mutually exclusive with @EnableAspectJAutoProxy.
· @EnableMBeanExport is the replacement for <context:mbean-export> and instructs Spring to expose certain framework beans and any @ManagedResource-annotated beans as JMX MBeans.
· @EnableScheduling replaces the rest of Spring’s <task:*> namespace and activates scheduled method execution with @Scheduled, similar to the @Async method execution enabled by @EnableAsync.
· @EnableSpringConfigured activates dependency injection in objects that are not Spring-managed beans and replaces <context:spring-configured>. It requires load time weaving to support this feature, as it must intercept object construction.
· @EnableTransactionManagement replaces <tx:annotation-driven> and enables database transaction management for methods advised with the @Transactional annotation.
· @EnableWebMvc activates annotation-driven controller request mapping and replaces <mvc:annotation-driven>. This activates a very complex configuration, which you often must customize. You can customize the entire Web MVC configuration by making your@Configuration class implement WebMvcConfigurer or, more easily, customize only the pieces that you need to by extending WebMvcConfigurerAdapter.
The Spring-Java-Config project demonstrates use of a pure Java configuration from WebApplicationInitializer bootstrapping to @Configuration classes for the root and DispatcherServlet application contexts. The GreetingService, GreetingServiceImpl, andHelloController classes have been moved to the com.wrox.site package to be separate from the configuration classes in the com.wrox.config package. This way, component scanning won’t inadvertently detect the @Configuration classes.
The first new class in this project, RootContextConfiguration, is very simple:
@Configuration
@ComponentScan(
basePackages = "com.wrox.site",
excludeFilters = @ComponentScan.Filter(Controller.class)
)
public class RootContextConfiguration
{
}
The @ComponentScan annotation tells Spring to scan classes in the com.wrox.site package and all subpackages and exclude @Controller classes, just like the hybrid configuration earlier in this section. The new ServletContextConfiguration class is very similar, turning off default filters to scan only for @Controllers, and enabling the annotation-driven Web MVC features.
@Configuration
@EnableWebMvc
@ComponentScan(
basePackages = "com.wrox.site",
useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(Controller.class)
)
public class ServletContextConfiguration
{
}
You probably noticed that neither of these classes have any @Bean methods. All the beans you need right now are configured automatically using component scanning and @EnableWebMvc, so you don’t have a need for any @Bean methods.
NOTE In the next section you see an example of manual bean configuration with @Bean, and throughout the rest of this book, you see more and more examples of creating custom beans with @Bean methods.
Of course, these configuration classes can’t bootstrap themselves, so the Bootstrap class implements WebApplicationInitializer.
public class Bootstrap implements WebApplicationInitializer
{
@Override
public void onStartup(ServletContext container) throws ServletException
{
container.getServletRegistration("default").addMapping("/resource/*");
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(RootContextConfiguration.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext servletContext =
new AnnotationConfigWebApplicationContext();
servletContext.register(ServletContextConfiguration.class);
ServletRegistration.Dynamic dispatcher = container.addServlet(
"springDispatcher", new DispatcherServlet(servletContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
The init parameters, listener, and DispatcherServlet configured in the deployment descriptor earlier in this section have been removed, and web.xml now contains just basic JSP and session configuration. The only other change made was for demonstration purposes and not out of necessity: The HelloController now uses @Inject instead of @Autowired to declare its dependencies.
@Inject
public void setGreetingService(GreetingService greetingService)
Now compile the application, start Tomcat from your IDE, and go to http://localhost:8080/java/ and http://localhost:8080/java/?name=Mars. The application functions identically to the Spring-XML-Config and Spring-Hybrid-Config applications.
UTILIZING BEAN DEFINITION PROFILES
Java is a flexible language that can run in many environments, so there’s no reason your Spring Framework application must be rigid. Spring’s bean definition profiles enable easily turning entire sections of the configuration on or off with a simple switch at the command line, in the deployment descriptor, or programmatically. This functionality is very handy in many use cases, and undoubtedly you’ll have your own ideas by the end of this section. Some examples follow:
· In an application environment with many tiers, you need some beans to run on one tier while other beans run on another. Using bean definition profiles, you can deploy a single application to every tier while the activated profile controls which beans are registered.
· You may write an application for resale to work against many different types of data stores. When your end users purchase and install the application they indicate which type of data store they want to use. Your application could have a Java Persistence API profile containing JPA repositories for persisting to relational databases and a Spring Data NoSQL profile containing NoSQL repositories for writing to schema-less data stores. Your users can install the same executable file while a simple configuration switch enables the correct profile.
· You might want to create different Development, Quality Assurance, and Production profiles. In the Development profile, you can probably hard-code certain settings, such as a connection to a local database that all developers must create. The Quality Assurance profile, likewise, might also have hard-coded settings that differ from development machines. Undoubtedly, your production environment team needs to change the application’s settings without waiting on you to change and recompile it, so the Production profile would load those settings from a properties file that your technicians can change.
Understanding How Profiles Work
Similar to other technologies that provide configuration profiles (such as Maven), using Spring bean definition profiles has two components: declaration and activation. You can declare profiles using <beans> elements in XML configuration files, the@org.springframework.context.annotation.Profile annotation on @Configuration classes or @Components, or both. Any <beans> element can contain a profile attribute to indicate that its beans belong to a particular profile. Fortunately, you do not have to create a new configuration file for every profile you want to create because you can nest <beans> elements. The following code snippet demonstrates this.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
...
<beans profile="development,qa">
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:com/wrox/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/wrox/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<context:property-placeholder location="file:/settings.properties" />
<jee:jndi-lookup id="dataSource"
jndi-name="java:/comp/env/${production.dsn}" />
</beans>
...
</beans>
In this example, the two differently configured DataSource beans are registered in different profiles. The in-memory embedded HyperSQL database is created if the development or qa profiles are active, whereas a data source with the configured name is looked up in the JNDI context only if the production profile is active. Because both beans implement DataSource and have the same bean ID, any <bean> can use the reference to the dataSource bean, and any @Component can have the appropriate DataSource injected automatically. You can also achieve this using Java @Configuration, as shown in the following code.
interface DataConfiguration
{
DataSource dataSource();
}
@Configuration
@Import({DevQaDataConfiguration.class, ProductionDataConfiguration.class})
@ComponentScan(
basePackages = "com.wrox.site",
excludeFilters = @ComponentScan.Filter(Controller.class)
)
public class RootContextConfiguration
{
}
@Configuration
@Profile({"development", "qa"})
public class DevQaDataConfiguration implements DataConfiguration
{
@Override
@Bean
public DataSource dataSource()
{
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/wrox/config/sql/schema.sql")
.addScript("classpath:com/wrox/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
@PropertySource("file:settings.properties")
public class ProductionDataConfiguration implements DataConfiguration
{
@Value("production.dsr")
String dataSourceName;
@Override
@Bean
public DataSource dataSource()
{
return new JndiDataSourceLookup()
.getDataSource("java:/comp/env/" + this.dataSourceName);
}
}
After you have declared your profiles, you can activate them in one or more of several different ways. First, you can use the spring.profiles.active context init parameter:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>development</param-value>
</context-param>
Or the Servlet init parameter:
<servlet>
...
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>development</param-value>
</init-param>
...
</servlet>
The context parameter affects all Spring application contexts running within the web application, whereas the Servlet init parameter affects only the DispatcherServlet application context for which it is set.
You can also use the -Dspring.profiles.active=development command-line parameter to activate a profile for all application contexts running throughout the Java Virtual Machine. Using any of these techniques, you can specify multiple profiles to activate, separated with commas (<param-value>profile1,profile2</param-value>, -Dspring.profiles.active=profile1,profile2). This, of course, means that profile names cannot contain commas.
You can also activate one or more profiles programmatically by calling the setActiveProfiles method on a ConfigurableEnvironment instance:
configurableEnvironment.setActiveProfiles("development");
configurableEnvironment.setActiveProfiles("profile1", "profile2");
Using this method affects the application context containing the environment and any children application contexts. Using context or servlet init parameters or command-line properties is typically more common. After all, if you know enough to programmatically set the active profiles, why do you need profiles at all? Programmatically setting profiles is useful for integration testing, however.
One more thing to understand is that the @Profile annotation, like @Component, can serve as a meta-annotation. This means you can create your own custom annotations that serve as @Profile annotations. As an example, consider the following annotation.
@Documented
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE, ElementType.METHOD})
@Profile("development")
public @interface Development
{
}
The custom @Development annotation has the same force as the @Profile annotation with the profile name “development.” Now, using @Development is equivalent to using @Profile("development"). This is especially useful to avoid littering your code with string-literal profile names subject to typos and hard to replace if you decide to change the profile name.
Considering Antipatterns and Security Concerns
You should keep some important considerations in mind whenever you decide to use bean definition profiles to solve a problem or need:
· Is there a simpler way to achieve the same result? If the same beans are created in multiple profiles but with different settings, chances are you can achieve this goal using a property source and one or more properties files. Using profiles for this would typically be an antipattern, adding more complexity than its worth.
· What are the types of beans present in your profiles? In most cases, two different profiles should largely have the same beans, albeit possibly different implementations or with more debugging, profiling, or reporting turned on. As a general rule, QA and production profiles should be nearly identical, with the major difference present between the development and QA profiles. Otherwise, you might not be testing everything you should be.
· What are the security implications of using bean definition profiles? Generally speaking, you should never use profiles to control security aspects of your application. Because end users can activate or deactivate profiles simply by enabling or disabling a JVM command-line property, they can easily circumvent security restrictions defined in such a way. One obvious antipattern in this case is to disable all product-licensing checks in a development profile but enable them in QA and production profiles. A clever user can then avoid purchasing your application merely by switching from the production to the development profile. There are ways to avoid this, such as stripping out classes during production builds or utilizing a Java SecurityManager, but it’s best just to avoid this situation altogether.
SUMMARY
In this chapter you were introduced to the Spring Framework, and you learned about beans, application contexts, and dispatcher Servlets. You then experimented with the many ways to bootstrap and configure Spring Framework. You explored the concepts of Dependency Injection (DI) and Inversion of Control (IoC), aspect-oriented programming, transaction management, publish-subscribe application messaging, and Spring’s MVC framework. You also saw the advantages of using Spring Framework over other technologies, and how Spring can make software development easier and more rapid for web applications, desktop applications, and even server daemons.
In the next chapter you take a deeper dive into controllers and the MVC framework to understand the true usefulness of the @RequestMapping annotation. Throughout the rest of the book, you’ll see many Spring applications. Keep in mind that only you can decide whether to use XML or Java bootstrapping and XML, hybrid, or Java configuration in your real-life Spring applications. However, for the sake of consistency, Java bootstrapping and configuration are used exclusively from here on out.