Spring Basics

Disclaimer

You are not reading a complete reference, the posts are more about a simple understanding and getting something to work with.

Most examples use Java config rather than XML or annotation-based configuration.

What is Spring

Spring is

  • a Framework
  • a Container
  • open source

The framework consists of many subprojects to simplify working with lower-level technologies. Get an overview here: http://spring.io/projects

The projects to choose from may be overwhelming in the beginning, but simply remember this: Whenever you have a (Java-)programming problem of which you may think that somebody could have solved it already, search for it in the Spring-world and safe quite some time maybe.

Source code is available here: https://github.com/spring-projects/spring-framework

Binaries are available here: http://mvnrepository.com/artifact/org.springframework

As a container, it servers as a lifecycle manager by

  • instantiating your application objects (beans)
  • injecting their dependencies, so you do not have to care about them finding and connecting to each other

Configuration

Spring is configurable and focuses on programming against interfaces. Therefore, the complexity of the implementation can be concealed and implementations can be easily swapped out, e.g. for testing. Your objects managed by Spring are so called Spring-Beans.

The central part of your spring-application, the configuration, can be set up in different ways:

  • Using XML, the traditional way
  • Annotation based (required for Spring MVC)
  • by Java config, meaning the configuration is also a bean derived from a Java-class

Comparison between Java config and annotations

Java config pros:
  • Is centralized in one (or a few) places
  • Strong type checking enforced by compiler and IDE
  • Can be used for all classes
Java config cons:
  • More verbose than annotations
Annotation pros:
  • Frequently changes of beans made easy
  • the class as single place to edit
  • rapid development
Annotation cons:
  • Configuration spread across your classes (more maintenance/debugging)
  • Only applicable for your own code
  • Bad separation of concerns as configuration and code are merged

The configuration contains instructions on how Spring sets up its application-context which creates your fully configured application system.

Java config example

@Configuration
@ComponentScan("ch.pma.myapp")
public class MyConfiguration {
 
    @Bean(name="myService")
    public MyService getMyService() {        
        return new MyServiceImpl();
    }
 
    @Bean(name="myRepository")
    public MyRepository getMyRepository() {       
        return new MyRepositoryImpl();
    }
}

@ComponentScan allows you to use annotation-based configuration on classes, effectively combining the two methods of Java config and annotation-based configuration. For example, we can define a controller-bean using the @Controller-annotation, turning it into a bean available in the application context:

@Controller
public class MyController {
 
    private MyService myService;
 
    @Autowired
    public void setMyService(MyService myService) {       
        this.myService = myService;
    }
 
    public User getUserById(int id) {
        return myService.getUserById(id);
    }
}

@Controller, normally used in a web-environment for example in combination with @RequestMapping(s), is a sub-annotation of

@Component

You could also use one of the other stereotype sub-annotations

@Repository (e.g. for exception-translation)
or 
@Service

for the other beans, the effect in this example is the same. All beans, whether they are defined in an XML, based on annotated classes and picked up by the component-scan or created in the configuration, will be fully initialized available in the application-context.

You can set a bean-name with the annotation, if not set, the name will be the class-name in camelCase.

When you enable the component-scan, be sure to define base-package(s) as your whole classpath will be scanned otherwise.

Bean uniqueness

When you annotate two classes of the same type as beans, you need to provide an ID, which you can refer to via @Qualifier to wire the correct bean:

@Component("prodRepository")
public class JdbcRepository implements MyRepository {}
@Component("devRepository")
public class JpaRepository implements MyRepository {}
@Service
public class MyServiceImpl implements MyService {
    @Autowired   
    @Qualifier("devRepository")    
    MyRepository myRepository;
}

If no unique bean can be found by type and no @Qualifier is to be used, Spring tries to find a matching bean by name = bean-id.

These examples will look for a bean with id “sampleBean”:

@Autowired
private MyBean sampleBean;
@Autowired
public void setSampleBean(MyBean myBean) {
    ...
}
@Autowired
public void someValue(MyBean sampleBean) {
    ...
}

Multiple configuration files

You can split your configuration into several classes and combine them in your main-configuration file by using

@Import({Configfile1.class, Configfile2.class})

You could even combine XML-configuration with your configuration-class, using @ImportResource:

@Configuration
@ImportResource({"classpath:ch/pma/application-config.xml", "file:/home/pma/application-config.xml"})@Import(Configfile1.class)public class MyConfig { ... }

A bean reference from another configuration-file can be obtained by using

@Autowired

allowing you to separate your “application” beans from your “infrastructure” beans.

When using @Autowired, a unique dependency of the correct type must exist as long as “(required=false)” is not added to the annotation. With Java 8 and Lambdas, optional autowiring can be defined and used like follows:

@Autowired
Optional<YourService> yourService;
 
public void useService() {
    yourService.ifPresent( s -> {
        //s is the instance of YourService
    });
}

Example

@Configuration
@Import(InfrastructureConfiguration.class)
public class ApplicationConfiguration {
 
    @Autowired
    DataSource dataSource;
 
    @Bean
    public MyRepository myRepository() {
        MyRepositoryImpl myRepository = new MyRepositoryImpl();
        myRepository.setDataSource(dataSource);
        return myRepository;
    }
}
@Configuration
public class InfrastructureConfiguration {
 
    @Bean
    public DataSource getDataSource() {
        ...
    }
}

In general you can use

@Autowired

on a constructor, method or (even on a private) field. Some say, using it on a field is bad practice though (see http://olivergierke.de/2013/11/why-field-injection-is-evil/).

It is not illegal to define the same bean more than once (hence in different configurations), you will get the last bean Spring sees defined.

ApplicationContext

Spring application contexts can be bootstrapped in any environment, you can even use more than one context, e.g. one for the repository-domain of your application and separate ones for different web-endpoints.

Use your configuration when setting up the application context and retrieve beans from it to work with.

Example

public class MyApp{
 
    private ApplicationContext applicationContext;
 
    public static void main(String[] args) {
 
        new MyApp();
    }
 
    public MyApp() {
 
        initApplicationContext();
        MyController myController = applicationContext.getBean(MyController.class);
        myController.getUserById(1);
        closeApplicationContext();
    }
 
    private void closeApplicationContext() {
        ((ConfigurableApplicationContext) applicationContext).close();
    }
 
    private void initApplicationContext() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
        this.applicationContext = applicationContext;
    }
}

Bean scope

Possible scopes are

  • singleton
  • prototype (instantiates a new bean every time the bean is referenced)
  • request (foreseen for usage in web-environments)
  • session (foreseen for usage in web-environments)
  • custom (define your own name and rules for this scope)

Singleton is the default scope, meaning that multiple calls to

applicationContext.getBean("...")

always returns the same instance. For using another scope, the bean has to be annotated with

@Scope("[desired_scope]")

Bean lifecycle in an application context

Creation:

In general:

  • Bean definitions loaded
  • Post process bean definitions (BeanFactoryPostProcessor; e.g. replacing property placeholders with their actual value by a PropertySourcesPlaceholderConfigurer. These beans must be defined as static methods, to ensure their early presence.)

For each bean:

  • Instantiate bean (eagerly by default, using lazy is not recommended)
  • Populate properties (dependency injection)
  • setters for bean-factory and application-context
  • BeanPostProcessor postProcessBeforeInitialization
  • afterPropertiesSet()
  • custom init-method
  • post-construct or BeanPostProcessor postProcessAfterInitialization, depending on the order-property

ApplicationContexts autodetect BeanPostProcessor beans in their bean definitions and applies them to any beans subsequently created.

BeanPostProcessors are used e.g. for proxying the bean, for example to handle transaction management.

Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory. For standard usage you will not need a BeanFactory though, as the application context extends the BeanFactory.

Destruction:

Is completed when an application context is closed

If available:

  • @PreDestroy or @Bean(destroyMethod=”…”) / destroy-method=”…” (XML)
  • destroy() (if DisposableBean)

Actual destruction of objects will follow when the garbage collection runs for the next time.

External Properties

Getting properties from the Environment

The environment automatically provides access to System properties and Environment variables.

Use Environment explicitly:

@Autowired
public Environment env;
...
env.getProperty("db.url");

or implicitly by using “${…}” placeholders:

@Value("${db.url}") String dbUrl

For undefined values, an alternative value can be provided using a colon:

@Value("${db.connectionLimit:10}")private int connectionLimit;

Property Sources

@PropertySources("[classpath:|file:|http:]/my_props.properties")

is used for referencing other sources but requires a bean PropertySourcesPlaceholderConfigurer-bean:

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

Your property-source path can also contain ${…}-placeholders, which are resolved against existing system properties and environments variables.
This way, you could provide different property-files for different environments like dev, test or prod.

SpEL

Spring expression language can be used to resolve more complex expressions than just property-names.

Examples:

@Value("#{systemProperties['db.url']}") 
String dbUrl;
 
@Bean 
public DataSource dataSource(@Value("#{systemProperties['db.url']}") String url, …) {
    DataSource dataSource = new DataSource();
    dataSource.setUrl(url);
    return dataSource;
}

systemProperties and systemEnvironment are available by default.

It is also possible to access public fields and/or getters of beans:

@Value("#{dataSource.url}") 
String dataSourceUrl;

SpEL also supports the Elvis-operator, to shorten the usage of a ternary operator with a null-check:

ExpressionParser parser = new SpelExpressionParser();
String testName = null;
String actualName = parser.parseExpression("testName ?: 'Unknown'").getValue(String.class);

In general, SpEL is way more powerful than what’s shown in these few examples. Read this: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html for more insight on the topic.

Profiles

Beans can be grouped into profiles by using

 @Profile("[desired_profile_id]")

on the configuration or on the bean.

Beans not grouped by a profile are always active.

Profiles need to be activated to be usable. Ways to do so are:

  • For testing: @ActiveProfiles
  • Web-Application: Setting context-param name spring.profile.active and value to the desired profile(s) in the web.xml
  • System property: -Dspring.profiles.active=profile1,profile2
  • Setting the spring.profiles.active system property programmatically before accessing the application context

Proxying

When using Java config, Spring proxies @Configuration using cglib. It creates a subclass of the

@Configuration
public class MyConfig {
...
}

named

public class MyConfig$$EnhancerByCGLIB$ extends MyConfig {
...
}

When referencing a singleton bean, this class first checks if it already existis in the application-context and returns it if so. Otherwise it calls the super-class to get the bean, stores in the application-context and returns it.

A BeanPostProcessor may wrap any bean with a proxy adding behaviour transparently. For example, your calls to your @Transactional RepositoryService are routed through a TransactionInterceptor who begins and commits (or rolls back) the transaction.

Spring uses JDK and CGLib to set up proxies. By default, all interfaces are proxied using dynamic (JDK) proxies. CGLib, which is included in the Spring .jars, is used when no interface is available as long as the class or method is not declared final.

Therefore, a JDK proxy will implement the interface, while the CGLib proxy will extend the class to be proxied.

Comparison between Java config and XML

Java config XML
Profile
@Profile("dev")
public class DevClass {}
<beans profile="dev">
</beans>

You can nest -tags within the parent-beans-tag to separate beans
into several profiles

Bean ID
@Bean
public MyService myService() {}
//id = methodname

or

@Bean(name="myService") //id = name
public MyService someService(){}
<bean class="ch.pma.myapp.services.MyService" id="myService"/>
Dependency injection
MyService myService = new MyServiceImpl();
myService.setDependency(myDependency());
<bean id="myService">
  <property name="dependency" ref="myDependency"/>
  <!-- implicit reference to setter setDependency(...) -->
</bean>

For some types, properties can be set using

<property name="someProperty" value="..." />
or
<property name="someProperty>
  <value=>...</value>
</property>

instead of ref=””. These types are:

  • Numeric types
  • BigDecimal,
  • boolean: “true”, “false”
  • Date
  • Locale
  • Resource
Constructor injection (unlike for setter-injection, constructor-arguments can not be treated as optional)
MyService myService = new MyServiceImpl(myDependency());
<bean id="myService">
  <constructor-arg ref="myDependency"/>
</bean>

constructor-arg elements can be in any order, to indicate your intended order when passing
ambigous values, use index (starting from 0)

<constructor-arg index="0" value="123">
<constructor-arg index="1" value="some string">

or set the name property, e.g.

name="intValue"

matching the name of the constructor-argument to omit the index.

Import
@Configuration
@Import(Config1.class)
public class MyConfig { ... }
<beans>
  <import resource="config1.xml" />
<beans>

Relative path is used by default. Alternatives are file, classpath, http.

Bean behaviour
Annotations require annotation-driven or the component-scanner to be activated

@PostConstruct
<bean ... init-method="init">
  ...
<beans>
@PreDestroy
<bean ... destroy-method="destroy">
  ...
<beans>
@Scope(...)
<bean ... scope="...">
  ...
<beans>
@Lazy("true")
<bean ... lazy-init="true">
  ...
<beans>
Component scan
@ComponentScan({"ch.pma.myapp.package1",
"ch.pma.myapp.package2"})
<context:component-scan 
base-package="ch.pma.myapp.package1, ch.pma.myapp.package2"/>

XML Namespaces

The default namespace in a Spring configuration file is typically the “beans” namespace:

<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.xsd>

Various framework functionality (aop, tx, context, etc.) can be accessed by adding the corresponding predefined namespace. This makes advanced features of Spring easy declarable.

For example,

<code><span class="atn"><beans ...
  xmlns:tx</span><span class="pun">=</span><span class="atv">"http://www.springframework.org/schema/tx"
  ...
  </span><span class="atn">xsi:schemaLocation</span><span class="pun">=</span><span class="atv">"http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd 
  ...">
  ...
  <tx:annotation-driven />
  ...
</beans></span></code>

hides a couple of bean-definitions for enabling the configuration of transactional behavior based on annotations.

Bing Bong

Guest Author - Knows Java and Spring very well.

Latest posts by Bing Bong (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *

*