Optimizing Spring Framework for App Engine Applications

Matt Stephenson / Wally YauNov 2012

Introduction

Central to the Spring Framework is its Inversion of Control (IoC)
container, alternatively known as Dependency Injection. It allows developers
to compose disparate components into a fully working application ready for
use. When an application is first loaded, Spring parses the configuration
metadata and combine the application classes to create an application
context. Depending on the complexity of the application, the initial time
required to instantiate the framework can take a long time. This does not
pose any problem for services that run in a load once and run forever
environment but may become a problem for App Engine applications. App
Engine frontend instances are dynamically scaled. That means App Engine
automatically creates new instances when load increases and turns off
instances when they are not being used. Reloading instances may result in
additional latency for users. Frontend instances also have a 60 seconds
deadline to complete a given request. Applications that take a long time to
load will result in DeadlineExceededException. This article discusses ways
you can optimize the initial load time for an application that uses the
Spring framework.

Best Practices for App Engine Applications

The following are best practices that can optimize the loading time for
App Engine applications that use the Spring Framework.

Reducing or Avoiding the Use of Component Scanning

Spring defines a set of stereotype annotations, which are markers for any
class that fulfills a role within an application. For example, the
@Repository annotation, which was introduced in Spring 2.0, is used for
classes that fulfill the role of Data Access Object of a repository. Another
example is the @Controller annotation, which indicates that a particular
class serves the role of a controller in the Spring Web MVC framework.
Spring provides an option to automatically detect stereotyped classes and
register corresponding BeanDefinitions with the ApplicationContext,
eliminating the need to specify the stereotyped classes in the XML
configuration metadata file. This is achieved by implicitly detecting the
candidate components by scanning the classpath and matching against filters.

Real-time component scanning is enabled by using the
context:component-scan configuration element in the Spring XML configuration
file.

When component scanning is enabled, during the application initialization
phase Spring will have to read a large amount of data from the file system in
real-time to scan for the stereotyped classes on the classpath. This can
cause the initial request to an App Engine application to take an
indeterministic amount of time to complete, despite the fact that the App
Engine’s virtual file system is a very high performance system.

Disable component scanning by not using the following configuration
element in the Spring XML configuration file:

If you must use component scanning, pay extra attention to the
base-package. If the base-package references classes in a large JAR file, or
if it references a significant number of JAR files, or if the base-package
refers to a large amount of .class files, the application’s performance will
be impacted negatively.

Here are some tips to help mitigate the impact of component scanning on
your application’s performance:

Breaking up larger JAR files can improve performance of classpath
scanning. This is because the application does not have as many classes to
scan if Spring can determine which JARS are relevant.

It is not advisable to attempt to perform classpath scanning with
UberJars or any other form of JAR files generated from “JAR composing” build
tools.

The use of filters on annotations (e.g. the context:exclude-filter tag)
will not help performance in App Engine. There are facilities in place that
make this an insignificant performance gain for the application.

Try to avoid uploading a large amount of .class files; attempt to “JAR
up” these .class files into smaller JAR files that will be compressed and
therefore faster to load.

Reducing or Avoiding the Use of Relationship Autowiring

The Spring container can automatically wire relationships between
collaboration beans using introspection of the bean classes. This eliminates
the need to explicitly specify the relationships as properties or
constructor arguments within the application configuration metadata.

Let’s see how the “automatic wiring” of the relationships is enabled. For
example, consider the following Spring MVC Controller class with a DAO
member,

While automatic wiring can significantly reduce the complexity of
specifying properties or constructor arguments through the use of the
XML-based configuration, it significantly increases the time required to
resolve the beans during application initialization time.
The time penalty is worse when the wiring mode is byType. This is because,
in this mode, Spring has to iterate over every bean defined in the
application context and must perform Java introspection to find out its
type.

If you need to use autowire, make sure that the container is
reasonably sized and it uses the byName mode.

Disabling XML Validation in Production

To further reduce the loading time of an application, you can disable XML
validation in production by following the steps below:

Create a class that extends
XmlWebApplicationContext.

Override the initBeanDefinitionReader
method.

Set the XmlBeanDefinitionReader validation mode to
VALIDATION_NONE. XML validation is enabled if an App Engine application runs within the App
Engine SDK and disabled when it runs in production mode.

Using Lazy-Initialized Beans

Spring’s ApplicationContext creates and configures all singleton beans as
part of the initialization process. This is desirable because if there are
configuration errors they are discovered immediately. But this has a
negative side effect of increasing the application loading time.

This may not be an issue for long-lived services which only have to start
once, but is not desirable for the dynamic environment of App Engine
applications.

For beans that are used less frequently, and are not
required when the container initializes for the first request, this problem
can be mitigated by setting the lazy-init attribute to true.

Lazy initialization is implemented using Aspect Oriented Programming
(AOP), which may introduce a certain amount of control flow that may or may
not be necessary. It uses proxies that construct and delegate to the
dependent bean after the bean is requested. This can become a performance
problem in situations where it would be just as quick to instantiate the
dependency bean versus instantiating a delegating proxy. Lazy initialization
can also introduce performance problems if it’s used extensively, and can
also complicate the process of debugging.

In the Spring Framework the beans are singleton by default. Singletons
are scoped within the IoC container. The use of the term singleton in Spring
therefore refers to a single instance of the bean within the IoC container
and has no strong relation to the lifecycle of the object in the virtual
machine. Therefore, multiple instances of a Spring singleton can exist for
any given classloader.

Significant application performance can be achieved by
carefully choosing which beans must be initialized during the application
loading time.

Avoiding Constructor Injection by Name

Spring 3.0 added support for using the constructor parameter name for
value disambiguation. Let’s consider the following sample class as an
example:

To make this work “out of the box”, Spring requires that the code
must be compiled with the debug flag enabled (-g for
all debugging info, or -g:vars to be precise, for the local variable
debug information). This allows Spring to look up the parameter name
in the debug information. However, since this information is not cached
in the JVM, it must be loaded from disk which causes significant I/O
time penalty.

To solve this problem you can use one of the following guidelines:

Use the @ConstructorProperties annotation to explicitly name your
constructor arguments as shown next.

However, constructor injection by name is a bad programming practice. It
is an anti-pattern of dependency injection which relies on the consistent
naming of constructor arguments. Here are the potential problems that may
lead to problems that are difficult to debug.

When utilizing software that you do not control, the effective
definition of the interface itself is extended to include information that
is not part of the standard method signature in Java. Therefore, it is
entirely possible that someone could change the name of a constructor
argument without revising the major or minor versions of the built
artifact, causing your application to fail.

Constructor injection can become problematic with any framework or
system that rewrites the class bytecode using something similar to ASM.
In Java, it is entirely possible to obtain the bytecode of a specific
class and modify it to be reloaded by a classloader. Doing so can break
linkage of the subsequently defined class with the source it was
originally defined with. This means the lookup of the names of the
constructor arguments will fail and this again may break the application
unpredictably.