Spring core

The central element of the core module is the context. When a Spring application starts, the container needs a context in which the container can create different beans. This is very general and true for any dependency injection container. If we programmatically create two different contexts, they may live independent of each other in the same JVM. If there is a bean declared as a singleton so that there should be only one single instance of it, then the container will create a single instance of it for a context when we need one instance. The objects representing the context have a reference to the object that we have already created. If there are multiple contexts, however, they will not know that there is another context in the JVM that already has an instance, and the container will create a new instance of the singleton bean for the other context.

Usually, we do not use more than one context in a program, but there are numerous examples of there being many contexts in a single JVM. When different servlets run in the same servlet container, they run in the same JVM separated by the class loader and they may each use Spring. In this case, the context will belong to the servlet and each will have a new context.

In the previous chapter, we used Guice. The Spring context is similar to the Guice injector. In the previous chapter, I was cheating a bit because I was programming Guice to create a new injector for each request. This is far from optimal, and Guice provides an injector implementation that can handle servlet environments. The reason for cheating was that I wanted to focus more on the DI architecture essentials, and I did not want to complicate the code by introducing a complex (well, more complex) implementation of the injector.

In the Spring context behavior, what it does, is defined by the interface ApplicationContext. There are two extensions of this interface and many implementations. ConfigurableApplicationContext extends ApplicationContext, defining setters, and ConfigurableWebApplicationContext defines methods needed in the web environment. When we program web applications, we usually do not need to interfere directly with the context. The framework configures the servlet container programmatically, and it contains servlets that create the context and invoke our methods. This is all boilerplate code created for us.

The context keeps track of the beans created, but it does not create them. To create beans, we need bean factories (at least one). The topmost interface of bean factories in Spring is BeanFactory. The difference between an object and a bean is that a bean factory creates the bean, it is registered in the context, and it has a String name. This way, the program can refer to the bean by the name.

Different beans can be configured in several different ways in Spring. The oldest approach is to create an XML file that describes the different beans, specifying the names, the class that has to be instantiated to create the bean, and fields in case the bean needs other beans to be injected for its creation.

The motivation behind this approach is that this way, the bean wiring and configuration can be totally independent of the application code. It becomes a configuration file that can be maintained separately. If we have a large application that may work in several different environments, the access to inventory data may be available in multitude ways. In one environment, the inventory is available by calling SOAP services. In another environment, the data is accessible in an SQL database. In the third environment, it can be available in some NoSQL store. Each of these accesses is implemented as a separate class, implementing a common inventory access interface. The application code depends only on the interface, and it is the container that has to provide one or the other implementation.

When the configuration of the bean wiring is in XML, then only this XML file is to be edited, and the code can be started with the implementation of the interface that is suitable for that environment.

The next possibility is to configure the beans using annotations. Many times, we use beans and Spring not because there are many implementations for a bean functionality, but because we want to separate the creation of the object instance from the functionality. This is a good style: separation of the concerns even if the implementation is single without alternatives. However, in this case, creating the XML configuration is redundant. If there is an interface and a single implementation of it in our code, then why should I specify in an XML that by creating an object with a class that implements that interface, I should use the class that implements that interface? Quite obvious, isn't it? We do not like programming things that can be automated.

To signal that a class can be used as a bean, and to possibly provide a name, we can use the @Component annotation. We do not need to provide a name as an argument. In that case, the name will be an empty string, but why have a name if we do not refer to it? Spring scans all the classes that are on the classpath and recognizes the classes annotated, and it knows that they are the candidates to be used for bean creation. When a component needs another bean to be injected, the field can be annotated with @Autowired or @Inject. The @Autowired annotation is a Spring annotation and existed before the @Inject annotation was standardized. If you intend to use your code outside of the Spring container, it is recommended to use standard annotations. Functionally, they are equivalent.

In our code, when Spring creates an instance of the ProductInformationController component, it sees that it needs an instance of ProductLookup. This is an interface, and thus, Spring starts to look for some class that implements this interface, creates an instance of it, possibly first creating other beans, and then injects it, setting the field. You can decide to annotate the setter of the field instead of the field itself. In such a case, Spring will invoke the setter even if the setter is private. You can inject dependencies through constructor arguments. The major difference between the setter, field injection, and constructor injection is that you cannot create a bean without dependency in case you use constructor injection. When the bean is instantiated, it should and will have all other beans injected so that it depends on using the constructor injection. At the same time, the dependencies that need to be injected through the setter injection, or directly into a field, could be instantiated later by the container sometime between instantiating the class and readying the bean.

This slight difference may not seem interesting or important until your constructor code may become more complex than the simple dependency settings or until the dependencies become complex. In the case of a complex constructor, the code should pay attention to the fact that the object is not fully created. This is generally true for any constructor code, but in the case of beans created by a dependency injection container, it is even more important. Thus, it may be advisable to use constructor injection. In that case, the dependencies are there; if a programmer makes a mistake, forgetting that the object is not fully initialized, and uses it in the constructor or a method that is called from a constructor, the dependency is there. Also, it is clean and well-structured to use the constructor to initialize the dependencies and have those fields declared final.

On the other hand, constructor injection has its downsides.

If different objects depend on each other and there is a ring in the dependency graph, then Spring will face a hard time if you use constructor dependencies. When class A needs class B and the other way round, as the simplest circle, then neither A nor B can be created without the other if the dependency injection is constructor dependency. In situations like this, the constructor injection cannot be used, and the circle should be broken at least as a single dependency. In situations like this, setter injection is inevitable.

Setter injection may also be better when there are optional dependencies. Many times, some class may not need all its dependencies at the same time. Some class may use a database connection or a NoSQL database handle but not both at the same time. Although it may also be a code smell and probably a sign of poor OO design, it may happen. It may be a deliberate decision to do that because the pure OO design would result in too deep object hierarchies and too many classes, beyond the maintainable limit. If such is the situation, the optional dependencies may be better handled using setter injection. Some are configured and set; some are left with default values, usually null.

Last but not least, we can configure the container using Java classes in case the annotations are not enough. For example, there are multiple implementations of the ProductLookup interface, as it is, in our code base. (Don't worry if you did not recognize that; I have not told you about that yet.) There is a ResourceBasedProductLookup class that reads properties files from the package and is mainly to test the application, and there is RestClientProductLookup, which is a production-like implementation of the interface. If I have no other configuration than annotating the lookup field with @Autowired, Spring will not know which implementation to use and will reward the use during startup with the following error message:

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled. 
2016-11-03 07:25:01.217 ERROR 51907 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in packt.java9.by.example.mybusiness.productinformation.ProductInformationController required a single bean, but 2 were found:
- resourceBasedProductLookup: defined in file [/.../sources/ch07/productinformation/build/classes/main/packt/java9/by/example/mybusiness/productinformation/lookup/ResourceBasedProductLookup.class]
- restClientProductLookup: defined in file [/.../sources/ch07/productinformation/build/classes/main/packt/java9/by/example/mybusiness/productinformation/lookup/RestClientProductLookup.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

This is a fairly self-explanatory error message; it tells a lot. Now is the time when we can configure the bean in XML, but at the same time, we can also configure it using Java.

Many developers do not get the point the first time. I did not get it either. The whole XML configuration was to separate the configuration from the code. It was to create the possibility that a system administrator changes the configuration and is free to select one or the other implementation of some interface, wiring the application together. Now Spring tells me that it is better to return to the programmatic way?

At the same time, I could hear concerns for many years that XML is not really any better than Java code. XML writing is essentially programming, except that the tooling and IDE support is not as good for XML as it is for Java code (the latter developed a lot in recent years, although for Spring XML configuration).

To understand the concept of returning to Java code from XML, we have to get back to the pure reason and aim of the XML way of configuration. The main advantage of XML Spring configuration is not that the format is not programmatic but rather that the configuration code is separated from application code. If we write the configuration in Java and keep those configuration classes to the bare minimum, and they stay as they should, then the separation of application versus configuration code still stands. It is only the format of the configuration that we change from XML to Java. The advantages are numerous. One is that the names of the classes are recognized by the IDE as we edit and we can have autocomplete in Java (note that this also works using XML in some of the IDEs utilizing some of the extensions of plugins). In the case of Java, IDE support is ubiquitous. Java is more readable than XML. Well, this is a matter of taste, but most of us like Java more than XML.

System administrators can also edit Java code. When they edit the XML configuration, they usually have to extract it from a JAR or WAR file, edit it, and then package the archive again. In the case of Java editing, they also have to issue a gradle war command or something similar. This should not be a showstopper for a system manager who runs Java applications on a server. And again, it is not Java programming. It is only editing some Java code files and replacing some class name literals and string constants.

We follow this approach in our sample application code. We have two configuration files in the application: one for local deployment and testing and another for production. The @Profile annotation specifies which profile the configuration should use. The profile, when the code is executed, can be specified on the command line as a system property, as follows:

    $ gradle -Dspring.profiles.active=local bootRun

The configuration class is annotated with @Configuration. The methods that are bean factories are annotated with @Bean:

package packt.java9.by.example.mybusiness.productinformation; 
import ...
@Configuration
@Profile("local")
public class SpringConfigurationLocal {
@Bean
@Primary
public ProductLookup productLookup() {
return new ResourceBasedProductLookup();
}
@Bean
public ProductInformationServiceUrlBuilder urlBuilder(){
return null;
}
}

The bean factory simply returns a new instance of the ResourceBasedProductLookup class that implements the ProductLookup interface. This implementation can be used to run the application for local testing when there are no external services to rely on. This implementation reads the product data from local resource files packaged into the JAR application.

The production version of the configuration is not much different, but as it may be expected, there are a few more things to configure:

@Configuration 
@Profile("production")
public class SpringConfiguration {

@Bean
@Primary
public ProductLookup productLookup() {
return new RestClientProductLookup(urlBuilder());
}

@Bean
public ProductInformationServiceUrlBuilder urlBuilder(){
return new ProductInformationServiceUrlBuilder(
"http://localhost");
}
}

This version of the ProductLookup service class uses an external REST service to retrieve the data that it will present to the clients. To do so, it needs the URLs of these services. Such URLs should usually be configured. In our example, we implement a solution where these URLs can be computed on the fly. I tried to make up a situation where it may be needed in real life, but all reasoning was contorted and I gave up. The real reason is that, this way, we can see code that contains a bean that needs another bean to be injected. For now, note that the ProductInformationServiceUrlBuilder instance bean is defined in the same way as the ProductLookup bean, and when it has to be injected into the constructor of the ProductLookup bean, its defining bean method is used and not the following expression directly:

new ProductInformationServiceUrlBuilder("http://localhost");

The latter may work, but not in all situations and we should not use it. For the reasons, we will return when we discuss AOP with Spring in a subsequent section.

Also note that there is no need to define an interface to define a bean. The type that the bean method returns can also be a class. The context will use the method that fits the needed type, and if there are more than one suitable types and the configuration is not precise enough, as we saw, the container will log an error and will not work.

In the configuration that serves the local profile, we create a null value for ProductInformationServiceBuilder. This is because we do not need it when we use local testing. Also, if any method from this class is invoked, it will be an error. Errors should be detected as soon as possible; thus, a null value is a good choice.

The ProductInformationServiceUrlBuilder class is very simple:

package packt.java9.by.example.mybusiness.productinformation; 

public class ProductInformationServiceUrlBuilder {
private final String baseUrl;

public ProductInformationServiceUrlBuilder(String baseUrl) {
this.baseUrl = baseUrl;
}

public String url(String service, String parameter) {
final String serviceUrl;
switch (service) {
case "pi":
serviceUrl =
baseUrl + ":8081/product/{id}";
break;
case "query":
serviceUrl =
baseUrl + ":8081/query/{query}";
break;
case "inventory":
serviceUrl =
baseUrl + ":8083/inventory/{id}";
break;
default:
serviceUrl = null;
break;
}
return serviceUrl;
}
}

This bean also needs a constructor parameter, and we used a string constant in the configuration. This clearly shows that it is possible to use a simple object to initialize some of the dependencies (what would stop us, it is pure Java after all), but it may hinder the working of some Spring features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.29.71