Spin up the Spring container

In Spring, the org.springframework.context.ApplicationContext interface represents the Spring IoC container. In a standalone application, the typical way of setting up the container is to use ClassPathXmlApplicationContext or AnnotationConfigApplicationContext. Both are implementations of the ApplicationContext interface. And, as the name suggests, ClassPathXmlApplicationContext is used for XML-based configuration, while AnnotationConfigApplicationContext is used for Java-based configuration, and this is the one we will use.

Let's start building the backend of the Messages App. We will build it into a workable Spring Boot web application piece by piece as we introduce the features of Spring.

All of the code in this book is written with Visual Studio Code (https://code.visualstudio.com/), and the following extensions are installed for the frontend code writing:
  • ESLint (dbaeumer.vscode-eslint)
  • Vetur (octref.vetur)

The following extensions are installed for the backend code writing:

  • Language support for Java ™ for Visual Studio Code (redhat.java)
  • Java Extension Pack (vscjava.vscode-java-pack)
  • Debugger for Java (vscjava.vscode-java-debug)
  • Spring Boot Support (Pivotal.vscode-spring-boot)
  • Maven Project Explorer (vscjava.vscode-maven)

We will create this application using Maven (https://maven.apache.org). For readers who are not familiar with Maven, you can think of it as npm in the Java world. A typical Maven project directory structure looks like this:

/src
/src/main/java
/src/main/resources
/src/main/webapp
/src/test/java
/src/test/resources
/pom.xml

The /src/main folder is the root of the application's source code. The /src/main/webapp is for web application to put views—static assets. However, because we are building a Spring Boot web application, we don't need this folder. The /src/test folder is the root of the application's test code. The pom.xml file is Maven's configuration file; you can think of it as package.json in Maven.

Project Object Model (POM) is the unit of work in Maven. For more details, refer to https://maven.apache.org/guides/introduction/introduction-to-the-pom.html.

To start a Maven project, the first step is to create the pom.xml file. And our pom.xml file looks like this:

1. <?xml version="1.0" encoding="UTF-8"?>
2. <project xmlns="http://maven.apache.org/POM/4.0.0"
3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
5. <modelVersion>4.0.0</modelVersion>
6.
7. <groupId>app.sample</groupId>
8. <artifactId>messages</artifactId>
9. <version>1.0-SNAPSHOT</version>
10.
11. <properties>
12. <spring.version>5.0.3.RELEASE</spring.version>
13. <log4j.version>2.10.0</log4j.version>
14. </properties>
15.
16. <dependencies>
17. <dependency>
18. <groupId>org.springframework</groupId>
19. <artifactId>spring-context</artifactId>
20. <version>${spring.version}</version>
21. </dependency>
22. <dependency>
23. <groupId>org.apache.logging.log4j</groupId>
24. <artifactId>log4j-api</artifactId>
25. <version>${log4j.version}</version>
26. </dependency>
27. <dependency>
28. <groupId>org.apache.logging.log4j</groupId>
29. <artifactId>log4j-core</artifactId>
30. <version>${log4j.version}</version>
31. </dependency>
32. </dependencies>
33.
34. </project>

The root of pom.xml is the <project> element. In line 5, modelVersion is the version of the POM itself; usually, you don't need to change it. In lines 7 to 9, we define the details of the artifact of the application. If your application/library is published, the artifact is identified by a combination of groupId, artifactId, and version. In lines 11 to 14, we add properties of this POM to specify the versions of the dependencies that we use. And in lines 16 to 32, we add Spring Framework and Apache Log4j as the dependencies of this application.

Now, let's write source code that lives in the app.messages package. We will create the following four Java classes:

/src/main/java/app/messages/AppConfig.java
/src/main/java/app/messages/Application.java
/src/main/java/app/messages/Message.java
/src/main/java/app/messages/MessageRepository.java
/src/main/java/app/messages/MessageService.java

AppConfig.java is the configuration metadata Spring will use to instantiate the container. Application.java is the entry point of the application. It is where the main() method lives. Message.java defines the Message model, which has a very simple structure. MessageRepository.java is a demo of the repository that is responsible for saving messages. And MessageService.java is the application service that provides API's for its clients.

In our Messages App, we will define the repository and the service by using concrete classes instead of interfaces for reasons of simplicity. And, at this point, we don't have API available for HTTP-based access. Also, the code structure we're using for the Messages App is flat. Everything lives in the same package, which is not practical. You will see how the package grows by the end of this chapter. 

Let's create Message.java. Here is what it looks like:

1. package app.messages;
2.
3. public class Message {
4. private String text;
5. public Message(String text){this.text = text;}
6. public String getText(){return text;}
7. }

Line 1 defines the package in which the Message class lives. In line 4, we define a private field text of the String type. And in line 5, we define a Message class constructor. It accepts one parameter text, which is assigned to the private field text. In Java, a parameter's name can be the same as a class field. You can use the this keyword to distinguish them. In line 6, we define a getter method for others to access the private text field.

The code here has been reformatted so that it takes fewer lines. In practice, you should follow the convention of a Java-style guide, for example, https://github.com/google/styleguide.

MessageRepository.java looks like this:

1. package app.messages;
2.
3. import org.apache.commons.logging.Log;
4. import org.apache.commons.logging.LogFactory;
5.
6. public class MessageRepository {
7.
8. private final static Log log =
LogFactory.getLog(MessageRepository.class);
9.
10. public void saveMessage(Message message) {
11. // Save message to a database
12. log.info("Saved message: " + message.getText());
13. }
14.}

As you can see, in line 8, we create an instance of Log. And in lines 10 to 13, we create the saveMessage() method, which takes a Message object as its parameter. We leave the implementation of saving messages to databases for a later section. Here, we will simply print out a message to the log.

MessageService.java looks like this:

1. package app.messages;
2.
3. public class MessageService {
4. private MessageRepository repository;
5.
6. public MessageService(MessageRepository repository) {
7. this.repository = repository;
8. }
9. public void save(String text) {
10. this.repository.saveMessage(new Message(text));
11. }
12.}

As you can see, in line 4, we define MessageRepository as the dependency of MessageService. And in lines 6 to 8, we create a constructor that takes an instance of MessageRepository as its parameter. And Spring will wire up the dependency for us through this constructor. In lines 9 to 11, we define a simple API save(String) method, which allows its client to pass a text of the String type and save it to the repository.

Now that the MessageService class and the MessageRepository class are ready, let's create the configuration AppConfig.java, which looks like the following:

1. package app.messages;
2.
3. import org.springframework.context.annotation.Bean;
4. import org.springframework.context.annotation.ComponentScan;
5. import org.springframework.context.annotation.Configuration;
6.
7. @Configuration
8. @ComponentScan("app.messages")
9. public class AppConfig {
10.
11. @Bean
12. public MessageRepository messageRepository() {
13. return new MessageRepository();
14. }
15.
16. @Bean
17. MessageService messageService() {
18. return new MessageService(messageRepository());
19. }
20.}

As you can see, in line 7, we apply the @Configuration annotation to tell Spring that this AppConfig.java is for defining beans. The @ComponentScan annotation in line 8 is used with @Configuration to tell Spring the base package for scanning for annotated components. And we use the @Bean annotation to annotate the messageRepository() method and the messageService() method. These methods are responsible for generating beans. And the method name will be the bean's name; in other words, the MessageRepository bean's name will be messageRepository. It is the same for MessageService. And in line 18, we pass the MessageRepository instance to the MessageService constructor. 

Now, it is time to spin up the container in Application.javaIt looks like the following:

1. package app.messages;
2.
3. import org.springframework.context.ApplicationContext;
4. import org.springframework.context.annotation.
AnnotationConfigApplicationContext;
5.
6. public class Application {
7. public static void main(String[] args) {
8. ApplicationContext context = new
AnnotationConfigApplicationContext(AppConfig.class);
9. MessageService messageService =
context.getBean(MessageService.class);
10. messageService.save("Hello, Spring!");
11. }
12.}

In line 7, we define the entry point of our application using the static-void-main method, which takes an array of String objects as its parameter. In line 8, we create Spring container by passing the AppConfig class to the constructor of the AnnotationConfigApplicationContext class. In line 9, we retrieve the instance of the MessageService bean by calling the getBean(Class<T>) method that was defined in the org.springframework.beans.factory.BeanFactory interface, which is extended by the ApplicationContext interface. At this point, the messageService bean we get from the Spring container has been fully instantiated, notably, an instance of MessageRepository has been injected to it. In line 10, we ask the service to save a message.

Now, let's run the code. We will use Maven to generate a .jar file and run it from the command line. In order to do that, we will need to update the pom.xml file and add the following <build> section underneath the <dependencies> section.

Here is what it looks like:

</dependencies>
<build>
<finalName>messages</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals><goal>single</goal></goals>
<configuration>
<archiveBaseDirectory>${project.basedir}
</archiveBaseDirectory>
<archive>
<manifest>
<mainClass>app.messages.Application</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Basically, this adds the Maven plugin, maven-assembly-plugin, to the build process. And this plugin requires the information of the main class for the .jar file. We provide it in the mainClass tag. And in order to execute the .jar file from the command line, we will also need to include the application's dependencies, in our case, spring-context-5.0.3.RELEASE.jar, into the .jar file. That is the purpose of <descriptorRef>

Before we build the application, we will need to add the Log4j configuration at src/main/resources/log4j2.xml, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level
%logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="app.messages" level="INFO">
<AppenderRef ref="Console"/>
</Logger>
<Root level="ERROR"/>
</Loggers>
</Configuration>

As you can see, in this configuration, we define a console appender and a logger for our application. And the root level of the log is set to ERROR. If you're interested in the details of Log4j configuration, you can check its manual here: https://logging.apache.org/log4j/2.x/manual/configuration.html.

Now, open a Terminal in your macOS or a command line in Windows and switch to the root folder of the application where the pom.xml file lives and execute mvn install, which will download all of the required dependencies from the public Maven repository and then build the .jar file. The output will be placed under the target folder under the root folder of the application. Once the build finishes, execute the following command:

java -jar target/messages-jar-with-dependencies.jar

And you should see output similar to the following:

13:07:55.381 [main] INFO  app.messages.MessageRepository - Saved message: Hello, Spring!

By now, you have successfully created a very simple Spring application. 

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

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