DI

IoC/DI and beans

IoC is also known as _dependency injection_ (DI).

A bean just need to define its dependencies through its constructor arguments, its factory method arguments or its properties setter. And then the container will inject these dependencies when initializing this bean.

Traditionally, the bean itself control the initialization process or location of its dependencies by directly invoking its constructor of the class or some look-up mechanism such as Service Locator pattern(e.g. JNDI or something).

So DI is a converse of the above traditional process, and that’s the reason it’s called Inverse-of-Control (IoC).

A bean is a manged object, whose creation, assembly, even whole lifecycle is controlled by the container.

container-magic

Configuration metadata can be specified by XML-based configuration style or Annotation-based configuration style.

bean definition

bean name

<bean id="only_one_id" name="name1 name2 name3" ..>

<alias name="fromName" alias="toName"/>

you can name a bean by use id/name attribute. What’s more important is you can specify 0, 1, or multiple names/alias for a bean.

instantiating beans

  • Instantiation with a constructor
<bean id="exampleBean" class="examples.ExampleBean"/>  

<bean name="anotherExample" class="examples.ExampleBeanTwo$InnerClass"/>
  • Instantiation with a static factory method
<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
  • Instantiation using an instance factory method
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

dependencies

  • Constructor-based dependency injection
<bean id="foo" class="x.y.Foo">
    <constructor-arg ref="bar"/>
    <constructor-arg ref="baz"/>
</bean>

<bean id="bar" class="x.y.Bar"/>

<bean id="baz" class="x.y.Baz"/>

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

<!-- compiled with the debug flag -->
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

XML shortcut with the c-namespace

<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>

<!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
    <constructor-arg ref="bar"/>
    <constructor-arg ref="baz"/>
    <constructor-arg value="[email protected]"/>
</bean>

<!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="[email protected]"/>
  • Setter-based dependency injection
<bean id="exampleBean" class="examples.ExampleBean">
    <!-- setter injection using the nested ref element -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>

    <!-- setter injection using the neater ref attribute -->
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- results in a setDriverClassName(String) call -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
    <property name="username" value="root"/>
    <property name="password" value="masterkaoli"/>
</bean>

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

<bean id="moreComplexObject" class="example.ComplexObject">
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>
</bean>

** "” and null**

<bean class="ExampleBean">
    <property name="email" value=""/>
</bean>

<bean class="ExampleBean">
    <property name="email">
        <null/>
    </property>
</bean>

XML shortcut with the p-namespace

<bean name="john-classic" class="com.example.Person">
    <property name="name" value="John Doe"/>
    <property name="spouse" ref="jane"/>
</bean>

<bean name="john-modern"
      class="com.example.Person"
      p:name="John Doe"
      p:spouse-ref="jane"/>

bean scopes

annotations and bean scanning

Spring annotations vs. standard annotations

Context services

Bean Lifecycle Mangement

 

spring-bean-lifecycle

package context.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class LifecycleExposureBean implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private String injectedProperty;

    public void setInjectedProperty(String injectedProperty) {
        System.out.println("Injection: injectedProperty = [" + injectedProperty + "]");
        this.injectedProperty = injectedProperty;
    }

    //----------------context aware-------------------------

    public void setBeanName(String beanName) {
        System.out.println("BeanNameAware: beanName = [" + beanName + "]");
    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BeanClassLoaderAware: classLoader = [" + classLoader + "]");
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("ApplicationAware: applicationContext = [" + applicationContext + "]");
    }

    //----------------initialization hook-------------------------

    @PostConstruct
    public void postContructCallBack() {
        System.out.println("@PostContruc: Post contruct invocation");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("IntializingBean: afterPropertiesSet() called");
    }

    public void initMethod() {
        System.out.println("init-method: called");
    }

    //----------------destroy hook-------------------------
    @PreDestroy
    public void preDestroy() {
        System.out.println("@PreDestory: called");
    }

    public void destroy() throws Exception {
        System.out.println("DisposableBean: destroy() called");
    }

    public void destroyMethod() {
        System.out.println("destroy-method: called");
    }

    public static void main(String[] args) {
        GenericXmlApplicationContext context = new GenericXmlApplicationContext("classpath:spring-lifecycle.xml");

        context.destroy();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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">

       <bean class="context.lifecycle.LifecycleExposureBean" init-method="initMethod" destroy-method="destroyMethod">
              <property name="injectedProperty" value="This is the value of injected property" />
       </bean>
</beans>

Injection: injectedProperty = [This is the value of injected property]

BeanNameAware: beanName = [context.lifecycle.LifecycleExposureBean#0]

BeanClassLoaderAware: classLoader = [sun.misc.Launcher$AppClassLoader@330bedb4]

ApplicationAware: applicationContext = [org.springframework.context.support.GenericXmlApplicationContext@18769467: startup date [Sat Apr 25 19:38:10 CST 2015]; root of context hierarchy]

IntializingBean: afterPropertiesSet() called

init-method: called

DisposableBean: destroy() called

destroy-method: called

FactoryBean

When a bean depends on some beans which cannot be simply intialized by using new

  • implement FactoryBean<T>
  • specify <span class="FontName1">factory-bean</span> and <span class="FontName1">factory-method</span>

PropertyEditor

In String, PropertyEditior is mainly used as a kind of converter from String to POJO JavaBean.

You implement your custom PropertyEditor for the specified JavaBean class, and then configure it using org.springframework.beans.factory.config.CustomEditorConfigurer

public class CustomBean {
    private String firstName;
    private String lastName;

    public CustomBean(String firstName, String lastName) {
    	this.firstName = firstName;
    	this.lastName = lastName;
    }

    public String toString() {
    	return "First name: " + firstName + ", last name: " + lastName;
    }

}

public class CustomBeanEditor extends PropertyEditorSupport {

    public void setAsText(String text) {
        String[] segs = text.split(" ");
        CustomBean customBean = new CustomBean(segs[0], segs[1]);

        setValue(customBean);
    }
}
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
		<map>
			<entry key="context.propertyeditor.CustomBean" value="context.propertyeditor.CustomBeanEditor" />
		</map>
	</property>
</bean>

ApplicationContext capabilities

Basically, AppliationContext has more extra capabilities than BeanFactory.

You can regard ApplicationContext as a MessageSource, an ApplicationEventPublisher, a ResourceLoader.

MessageSource

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>message</value>
        </list>
    </property>
</bean>
GenericXmlApplicationContext context = new GenericXmlApplicationContext("classpath:spring-messagesource.xml");

String name = "Richard";
String greeting = context.getMessage("greeting", new String[]{name}, Locale.US);
String greetingCN = context.getMessage("greeting", new String[]{name}, Locale.CHINA);

System.out.println("greeting = [" + greeting + "]");
System.out.println("greetingCN = [" + greetingCN + "]");

Then I have two message file: message_en_US.properties and message_zh_CN

greeting=你好, {0}
greeting=How is it going, {0}?

Output:

greeting = [How is it going, Richard?]

greetingCN = [你好, Richard!]

Application Events

ApplicationEvent  – derive from java.util.EventObject

ApplicationListener<T> interface

ApplicationEventPublisher.publishEvent()

ApplicationContext implements ApplicationEventPublisher interface

resource accessing

org.springframework.core.io.Resource

ResourceLoader

ApplicationContext implements ResourceLoader

Environment and PropertySource Abstraction

Environment –> PropertySource

ConfigurableEnvironment env =&amp;nbsp;ctx.getEnvironment();
MutablePropertySources propertySources =&amp;nbsp;env.getPropertySources();

Map appMap =&amp;nbsp;new HashMap();
appMap.put("application.home", "application_home");

propertySources.addLast(new MapPropertySource("application_properties", appMap));

For the PropertySource abstraction, Spring will access the properties in the following default order:

  • System properties for the running JVM (-Dxxxx)
  • Environment variables (like JAVA_HOME, Path)
  • Application-defined properties Most of time, we use PropertyPlaceHolder
<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.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:application.properties"/>

    <bean id="somebean" class="com.apress.prospring4.ch4.AppProperty">
        <property name="applicationHome" value="${application.home}"/>
        <property name="userHome" value="${user.home}"></property>
    </bean>
</beans>

You can see, once you get the properties, you can use them in configuration files using {....}

 

Profiles

Basically, a profile instructs Spring to configure only theApplicationContext that was defined when the specified profile was active.

  • How to associated with a profile?
<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"
        profile="devProfile">
...
</bean>

Those beans in the file should be instantiated only when the specified profile is active. If its profile is not active, actually these beans will not be initialized.

  • How to activate
  1. JVM argument -Dspring.profiles.active="devProfile"
  2. ctx.getEnvironment().setActiveProfiles(“devProfile”)

 

 

AOP

Crosscutting concerns refers to logic in an application that cannot be decomposed from the rest of the application and may result in code duplication and tight coupling.

Typical use cases:

  • logging
  • security
  • transaction

Concepts:

  • Pointcut -> where to do: class filtering and method matching
  • Advice -> when to do: before/after/returning/throws/…
  • Aspect -> what to do, do what the application specified things. This is the only code application needs to define, others concepts are supported and configured by framework.
  • Advisor -> associate Advice and Pointcut
  • JoinPoint -> the execution point: the occurrence context including which class, which methods, proceeding method execution..
  • weaving -> when to proxy target: build time(compile-time), load-time, run-time

Spring AOP style

Spring AOP depends on proxies. You must use ProxyFactoryBean to produce the proxy for target object.

spring-aop

Internally, Spring has two proxy implementations: JDK dynamic proxies and CGLIB proxies.

  • By default, when the target object to be advised implements an interface, Spring will use a JDK dynamic proxy to create proxy instances of the target.
  • when the advised target object doesn’t implement an interface (for example, it’s a concrete class), CGLIB will be used for proxy instance creation.

Joinpoints in Spring

Spring AOP supports only one joinpoint type: method invocation.

Aspects in Spring: Adviser (Advice+Pointcut)

An aspect in Spring is represented by an instance of a class that implements the Advisor interface.

  • PointcutAdvisor
  • IntroductionAdvisor The main difference between Adviceand Advisoris that Advisor carries Advice with the associated Pointcut.

spring-aop-concepts

ProxyFactory.addAdvice() delegates to addAdvisor() behind the scenes, creating an instance of DefaultPointcutAdvisor and configuring it with a pointcut that points to all methods.

Pointcut

Pointcut acutally does something filtering class and matching methods.

eight Pointcut interface implementation:

  • org.springframework.aop.support.NameMatchMethodPointcut
  • org.springframework.aop.support.JdkRegexpMethodPointcut
  • org.springframework.aop.support.annotation.AnnotationMatchingPointcut
  • org.springframework.aop.aspectj.AspectJExpressionPointcut
  • org.springframework.aop.support.DynamicMethodMatcherPointcut
  • org.springframework.aop.support .StaticMethodMatcherPointcut
  • org.springframework.aop.support.ComposablePointcut
  • org.springframework.aop.support.ControlFlowPointcut

Advice types

schema-based configuration

aop:aspect
<aop:config>
    <!-- shared top-level pointcut -->
    <aop:pointcut id="fooExecution" expression="execution(* com.apress.prospring4.ch5..foo*(int))"/>

    <aop:aspect ref="myAspect">
        <!-- <aop:pointcut ... /> -->
        <aop:before method="simpleBeforeAdvice" pointcut-ref="fooExecution" />
        <aop:around method="simpleAroundAdvice" pointcut-ref="fooExecution" />
    </aop:aspect>
</aop:config>

myAspect bean is a plain bean, which don’t necessarily implement any interface.

aop:advisor

This is called aspect instantiation model.

<aop:config>
    <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

    <aop:advisor pointcut-ref="businessService" advice-ref="tx-advice"/>
</aop:config>

<tx:advice id="tx-advice">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

advice-ref is a bean which must implement one of the Advice interfaces family.

@AspectJ style

Bascially, they use @Aspect, @Pointcut, @Around, @Before, …

To enable @AspectJ style:

<aop:aspectj-autoproxy/>

Load-time weaving

enabled by:

<context:load-time-weaver>

The default LoadTimeWeaver is the DefaultContextLoadTimeWeaver class, which attempts to decorate an automatically detectedLoadTimeWeaver: the exact type of LoadTimeWeaver that will be automatically detected is dependent upon your runtime environment (summarized in the following table).

You can also specific the exact load-time weaver rather than the automatic one in xml configuration file:

<context:load-time-weaver weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

WebLogic, WebSphere, Resin, GlassFish, JBoss

These environments have Load-time weaving support, no need for -javaagent:path/to/``org.springframework.instrument-{version}.jar

Tomcat

not support. You need do extra things to LTW

  • Copy org.springframework.instrument.tomcat.jar into _$CATALINA_HOME_/lib
  • Instruct Tomcat to use the custom class loader (instead of the default) by editing the web application context file:
<Context path="/myWebApp" docBase="/my/webApp/location">
    <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Generic J2SE applications

Spring provides InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general) VM agent, -javaagent:path/to/org.springframework.instrument-{version}.jar

Transaction management

In Java, there are multiple persistence APIs and all of them provides their own ways of transaction management.

  • JDBC transaction: this is a resource-specific transaction, which is implemented by connection.
  • JTA transaction: this is a transaction API for global transaction, it usually needs the support of application server.
  • JPA transaction: JPA has its own transaction API by EntityManager.
  • Hibernate transaction: Hibernate also provides its way to manage transaction by SessionFactory
  • JDO: … Now Spring transaction management abstracts and unifies all these API.

Infrastructure abstraction

  • TransactionStatus

  • PlatformTransactionManager (SPI)

  • TransactionDefinition

    • Isolation
    • Propagation
    • Timeout
    • Read-only Spring implements many built-in transaction manager implements:

AOP implementation

TransactionInterceptor

This is the secret for the declarative transaction.

transaction-interceptor

public interface FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);

}

public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        throw new UnsupportedOperationException();
    }

    public Foo getFoo(String fooName, String barName) {
        throw new UnsupportedOperationException();
    }

    public void insertFoo(Foo foo) {
        throw new UnsupportedOperationException();
    }

    public void updateFoo(Foo foo) {
        throw new UnsupportedOperationException();
    }

}

class Foo {

}
public static void main(String[] args) {
    GenericXmlApplicationContext context = new GenericXmlApplicationContext("classpath:spring-tx.xml");
    FooService fooService = (FooService)context.getBean("fooService");

    TransactionInterceptor txAdvice = (TransactionInterceptor)context.getBean("txAdvice");
    fooService.insertFoo(new Foo());
}

This is the log4j log which can show some implementation details:

&lt;!-- the DefaultFooService is actually proxied --&gt;
DEBUG {org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator}  - Creating implicit proxy for bean 'fooService' with 0 common interceptors and 2 specific interceptors
DEBUG {org.springframework.aop.framework.JdkDynamicAopProxy}  - Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [tx.DefaultFooService@14f9390f]
DEBUG {org.springframework.beans.factory.support.DefaultListableBeanFactory}  - Finished creating instance of bean 'fooService'

&lt;!--=========== Before: the insertFoo(..) method is now being invoked on the proxy ================--&gt;
&lt;!-- the transactional advice kicks in here... --&gt;

&lt;!-- acquire connection --&gt;
DEBUG {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Acquired Connection [jdbc:h2:file:D:/Projects/spring-sample/activejdbc/src/main/resources/test, UserName=, H2 JDBC Driver] for JDBC transaction

&lt;!-- disable autocommit --&gt;
DEBUG {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Switching JDBC Connection [jdbc:h2:file:D:/Projects/spring-sample/activejdbc/src/main/resources/test, UserName=, H2 JDBC Driver] to manual commit

&lt;!-- !!! this is the way: bound resource to the thread --&gt;
TRACE {org.springframework.transaction.support.TransactionSynchronizationManager}  - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@2101b44a] for key [org.apache.commons.dbcp.BasicDataSource@52d645b1] to thread [main]

&lt;!-- initialize transaction synchronization --&gt;
TRACE {org.springframework.transaction.support.TransactionSynchronizationManager}  - Initializing transaction synchronization
TRACE {org.springframework.transaction.interceptor.TransactionInterceptor}  - Getting transaction for [tx.DefaultFooService.insertFoo]

=================foo() invoked=============

&lt;!--============ Exception: the insertFoo(..) method from DefaultFooService throws an exception...========== --&gt;
TRACE {org.springframework.transaction.interceptor.TransactionInterceptor}  - Completing transaction for [tx.DefaultFooService.insertFoo] after exception: java.lang.UnsupportedOperationException
TRACE {org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}  - Applying rules to determine whether transaction should rollback on java.lang.UnsupportedOperationException
TRACE {org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}  - Winning rollback rule is: null
TRACE {org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}  - No relevant rollback rule found: applying default rules
TRACE {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Triggering beforeCompletion synchronization

&lt;!-- rollback depending on the rollback rules --&gt;
DEBUG {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Initiating transaction rollback
DEBUG {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Rolling back JDBC transaction on Connection [jdbc:h2:file:D:/Projects/spring-sample/activejdbc/src/main/resources/test, UserName=, H2 JDBC Driver]
TRACE {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Triggering afterCompletion synchronization

&lt;!-- clear transaction synchronization --&gt;
TRACE {org.springframework.transaction.support.TransactionSynchronizationManager}  - Clearing transaction synchronization
TRACE {org.springframework.transaction.support.TransactionSynchronizationManager}  - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@2101b44a] for key [org.apache.commons.dbcp.BasicDataSource@52d645b1] from thread [main]

&lt;!-- release connection --&gt;
DEBUG {org.springframework.jdbc.datasource.DataSourceTransactionManager}  - Releasing JDBC Connection [jdbc:h2:file:D:/Projects/spring-sample/activejdbc/src/main/resources/test, UserName=, H2 JDBC Driver] after transaction
DEBUG {org.springframework.jdbc.datasource.DataSourceUtils}  - Returning JDBC Connection to DataSource