ProSpring5Chap4 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki

Ch4. Spring Configuration in Detail and Spring Boot

(??) = could not understand

In this chapter,

  • Managing the bean life cycle
  • Making your beans "Spring aware"
  • Using FactoryBeans
  • Working with JavaBeans PropertyEditors
  • Learning more about the Spring ApplicationContext
  • Using Java classes for configuration
  • Using Spring Boot
  • Using configuration enhancements
  • Using Groovy for configuration

1 Spring’s Impact on Application Portability

(??)

2 Bean Life-Cycle Management

Two bean life-cycle events

  • post-initialization
    • raised as soon as Spring finishes setting all the property values on the bean and finishes any dependency checks that you configured it to perform
  • pre-destruction
    • fired just before Spring destroys the bean instance (except for beans with prototype scope)

⭐ Three mechanisms that a bean can use to hook into each of these events

  • interface-based
    • your bean implements an interface specific to the type of notification it wants to receive, and Spring notifies the bean via a callback method defined in the interface.
  • method-based (recommended)
    • Spring allows you to specify, in your ApplicationContext configuration, the name of a method to call when the bean is initialized and the name of a method to call when the bean is destroyed.
  • annotation-based (recommended)
    • you can use JSR-250 annotations to specify the method that Spring should call after construction or before destruction.

Goal

  • We do not need to care about initialization and destruction each time we use one of Spring’s components

When to use? - skip

  • interface-based : If you are not too concerned about portability or you are defining many beans of the same type that need the life-cycle notifications, using the interface-based mechanism is the best way
  • method-based : If you are concerned about portability or you are just defining one or two beans of a particular type that need the callbacks, use the method-based mechanism.
  • annotation-based : If you use annotation-type configuration and are certain that you are using an IoC container that supports JSR-250, use the annotation mechanism.

Summary : the choice of which mechanism you use for receiving life-cycle notifications depends on your application requirements.

⭐Sequence of life cycle (Figure 4-1)

Bean instantiation and DI -> Check for Spring Awareness -> Bean Creation Life-Cycle Callback -> Bean Destruction Life-Cycle Callbacks

  • Scan for bean definition from XML files, annotated classes, java configuration classes -> create bean instance -> inject bean dependencies
  • -> If bean type implements BeanNameAware, call setBeanName() -> If bean type implements BeanClassLoaderAware, call setBeanClassLoader() -> If bean type implements AppplicationContextAware, call setApplicationContext()
  • -> If @PostConstruct annotation is present call method annotated with it -> If bean type implements InitializaingBean, call afterPropertiesSet() -> If bean definition contains init-method or @Bean(initMethod=".."), call the init method
  • -> If @PreDestroy annotation is present call method annotated with it -> If bean type implements DisposableBean, call destroy() -> If bean definition contains destroy-method or @Bean(destroyMethod=".."), call the destroy method.

3 Hooking into Bean Creation

**4 ways for controlling life cycle events **link

  • Custom init() and destroy() methods in bean configuration file (= an initialization method)
    • adv - keeping your application decoupled from Spring
    • disadv - have to remember to configure the initialization method for every bean that needs it.
  • InitializingBean and DisposableBean callback interfaces
    • adv - being able to specify the initialization callback once for all instances of your bean class
    • disadv - have to couple your application
  • @PostConstruct and @PreDestroy annotations
    • adv - need to apply the annotation to the method
    • disadv - make sure that the IoC container supports JSR-250
  • @Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy")
  • *Aware interfaces for specific behavior

**Which one author recommand? **- skip

If portability is an issue, use the initialization or annotation method; otherwise, use the InitializingBean interface to reduce the amount of configuration your application needs and the chance of errors creeping into your application because of misconfiguration.

3.1 Executing a Method When a Bean Is Created

public class Singer {
    private static final String DEFAULT_NAME = "Eric Clapton";
    private String name;
    private int age = Integer.MIN_VALUE;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void init() { // ■■■■■■■■■ 
        System.out.println("Initializing bean");
        if (name == null) {
            System.out.println("Using default name");
            name = DEFAULT_NAME;
        }
        if (age == Integer.MIN_VALUE) {
            throw new IllegalArgumentException(
                    "You must set the age property of any beans of type " + Singer.class);
        }
    }
    public String toString() {
        return "\tName: " + name + "\n\tAge: " + age;
    }
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        getBean("singerOne", ctx);
        getBean("singerTwo", ctx);
        getBean("singerThree", ctx);
        ctx.close();
    }
    public static Singer getBean(String beanName,
                                 ApplicationContext ctx) {
        try {
            Singer bean = (Singer) ctx.getBean(beanName);
            System.out.println(bean);
            return bean;
        } catch (BeanCreationException ex) {
            System.out.println("An error occured in bean configuration: "
                    + ex.getMessage());
            return null;
        }
    }
}

ApplicationContext configuration to define beans

  • default-lazy-init: to inform Spring to instantiate the bean only when it is first requested, rather than at startup.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-lazy-init="true"> // default-lazy-init
    <bean id="singerOne"
          class="com.apress.prospring5.ch4.Singer"
          init-method="init" p:name="John Mayer" p:age="39"/> 
    <bean id="singerTwo"
          class="com.apress.prospring5.ch4.Singer"
          init-method="init" p:age="72"/>
    <bean id="singerThree"
          class="com.apress.prospring5.ch4.Singer"
          init-method="init" p:name="John Butler"/>
</beans>

Result

  • singerOne 's name and ages passes through the init() method
  • singerTwo has no value for the name property, i.e., DEFAULT_NAME will be given
  • singerThree throws IllegalArgumentException
Initializing bean
    Name: John Mayer
    Age: 39
Initializing bean
Using default name
    Name: Eric Clapton
    Age: 72
Initializing bean
IllegalArgumentException Error occur

Can use default-init-method for common init method.

<beans ...
        default-lazy-init="true" default-init-method="init">
<bean id="singerOne"
      class="com.apress.prospring5.ch4.Singer"
      p:name="John Mayer" p:age="39"/>
<bean id="singerTwo"
      class="com.apress.prospring5.ch4.Singer"
      p:age="72"/>
<bean id="singerThree"
      class="com.apress.prospring5.ch4.Singer"
      p:name="John Butler"/>
        </beans>

Q. what does it means? "You can take full advantage of the benefits of IoC without losing any of the control you get from manually defining dependencies." (??)

3.2 Implementing the InitializingBean Interface

InitializingBean interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. link

  • afterPropertiesSet() = init() in the previous section
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.GenericXmlApplicationContext;

public class SingerWithInterface implements InitializingBean {
    private static final String DEFAULT_NAME = "Eric Clapton";
    private String name;
    private int age = Integer.MIN_VALUE;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void afterPropertiesSet() throws Exception { // ■■■■■■■■■
        System.out.println("Initializing bean");
        if (name == null) {
            System.out.println("Using default name");
            name = DEFAULT_NAME;
        }
        if (age == Integer.MIN_VALUE) {
            throw new IllegalArgumentException(
                    "You must set the age property of any beans of type "
                            + SingerWithInterface.class);
        }
    }
    public String toString() {
        return "\tName: " + name + "\n\tAge: " + age;
    }
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        getBean("singerOne", ctx);
        getBean("singerTwo", ctx);
        getBean("singerThree", ctx);
        ctx.close();
    }
    private static SingerWithInterface getBean(String beanName,
                                               ApplicationContext ctx) {
        try {
            SingerWithInterface bean =
                    (SingerWithInterface) ctx.getBean(beanName);
            System.out.println(bean);
            return bean;
        } catch (BeanCreationException ex) {
            System.out.println("An error occured in bean configuration: "
                    + ex.getMessage());
            return null;
        }
    }
}
  • no init-method here
<beans ... default-lazy-init="true">
    <bean id="singerOne"
          class="com.apress.prospring5.ch4.SingerWithInterface"
          p:name="John Mayer" p:age="39"/>
    <bean id="singerTwo"
          class="com.apress.prospring5.ch4.SingerWithInterface"
          p:age="72"/>
    <bean id="singerThree"
          class="com.apress.prospring5.ch4.SingerWithInterface"
          p:name="John Butler"/>
</beans>

3.3 Using the JSR-250 @PostConstruct Annotation

  • package = javax.annotation
  • Supported from Spring 2.5
  • @PostConstruct = init-method in the previous section
  • can assign any name to the method, i.e., not only init()
  • need to add the <context:annotation-driven> tag into the configuration file
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class SingerWithJSR250 {
    private static final String DEFAULT_NAME = "Eric Clapton";
    private String name;
    private int age = Integer.MIN_VALUE;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @PostConstruct // ■■■■■■■■■
    public void init() throws Exception {
        System.out.println("Initializing bean");
        if (name == null) {
            System.out.println("Using default name");
            name = DEFAULT_NAME;
        }
        if (age == Integer.MIN_VALUE) {
            throw new IllegalArgumentException(
                    "You must set the age property of any beans of type " +
                            SingerWithJSR250.class);
        }
    }
    public String toString() {
        return "\tName: " + name + "\n\tAge: " + age;
    }
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-annotation.xml");
        ctx.refresh();
        getBean("singerOne", ctx);
        getBean("singerTwo", ctx);
        getBean("singerThree", ctx);
        ctx.close();
    }
    public static SingerWithJSR250 getBean(String beanName,
                                           ApplicationContext ctx) {
        try {
            SingerWithJSR250 bean =
                    (SingerWithJSR250) ctx.getBean(beanName);
            System.out.println(bean);
            return bean;
        } catch (BeanCreationException ex) {
            System.out.println("An error occured in bean configuration: "
                    + ex.getMessage());
            return null;
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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"
       xmlns:p="http://www.springframework.org/schema/p"
       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"
       default-lazy-init="true">
    <context:annotation-config/>
    <bean id="singerOne"
          class="com.apress.prospring5.ch4.SingerWithJSR250"
          p:name="John Mayer" p:age="39"/>
    <bean id="singerTwo"
          class="com.apress.prospring5.ch4.SingerWithJSR250"
          p:age="72"/>
    <bean id="singerThree"
          class="com.apress.prospring5.ch4.SingerWithJSR250"
          p:name="John Butler"/>
</beans>

4 Declaring an Initialization Method Using @Bean

  • @Bean(initMethod = "init") = Another way to declare the initialization method for a bean
  • Not use xml, Just add @Configuration in java code to set properties
  • @Lazy = default-lazy-init="true"
import com.apress.prospring5.ch4.Singer;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.GenericApplicationContext;
import static com.apress.prospring5.ch4.Singer.getBean;
public class SingerConfigDemo {
    @Configuration
    static class SingerConfig{
        @Lazy // ■■■■■■■■■
        @Bean(initMethod = "init") // ■■■■■■■■■
        Singer singerOne() {
            Singer singerOne = new Singer();
            singerOne.setName("John Mayer");
            singerOne.setAge(39);
            return singerOne;
        }
        @Lazy
        @Bean(initMethod = "init")
        Singer singerTwo() {
            Singer singerTwo = new Singer();
            singerTwo.setAge(72);
            return singerTwo;
        }
        @Lazy
        @Bean(initMethod = "init")
        Singer singerThree() {
            Singer singerThree = new Singer();
            singerThree.setName("John Butler");
            return singerThree;
        }
    }
    public static void main(String args) {
        GenericApplicationContext ctx =
                new AnnotationConfigApplicationContext(SingerConfig.class);
        getBean("singerOne", ctx);
        getBean("singerTwo", ctx);
        getBean("singerThree", ctx);
        ctx.close();
    }
}

4.1 Understanding Order of Resolution

  • can use all mechanisms on the same bean instance for bean creation.
  • Order: @PostConstruct -> InitializingBean’s afterPropertiesSet() -> initialization method (specified in the configuration file).
    • Understanding the order = useful
  • Bean creation process : (just see Order above)
    • constructor is called to create the bean.
    • dependencies are injected (setters are called).
    • pre-initialization BeanPostProcessor infrastructure beans are consulted to see whether they want to call anything from this bean
      • @PostConstruct annotation is registered by CommonAnnotationBeanPostProcessor and called
    • InitializingBean’s afterPropertiesSet is executed (invoked by a BeanFactory)
    • init-method attribute is executed

5 Hooking into Bean Destruction

  • (Assumed ApplicationContext is used) signal to BeanFactory to destroy all singleton instances with a call to ConfigurableBeanFactory. destroySingletons().
  • purpose : shut down gracefully (and avoid inconsistent state)
    • flush any data you are storing in memory to persistent storage
    • allow beans to end any long-running processes they may have started.
      • Q. Does it mean that the application shuts down once the process finish? (??)
    • allow beans to receive notification that destroySingletons() has been called
  • Usually, create and configure a resource in the initialization callback and then release the resource in the destruction callback

6 Executing a Method When a Bean Is Destroyed

  • 4 way to hook into bean destruction link
    • Custom destroy() methods in bean configuration file
    • DisposableBean callback interfaces
      • not much difference between two above.
    • @PreDestroy annotations
    • @Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy") for Java Configuration
import java.io.File;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.GenericXmlApplicationContext;
public class DestructiveBean implements InitializingBean {
    private File file;
    private String filePath;
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing Bean");
        if (filePath == null) {
            throw new IllegalArgumentException(
                    "You must specify the filePath property of"
                            + DestructiveBean.class);
        }
        this.file = new File(filePath);
        this.file.createNewFile();
        System.out.println("File exists: " + file.exists());
    }
    public void destroy() { // ■■■■■■■■■
        System.out.println("Destroying Bean");
        if(!file.delete()) {
            System.err.println("ERROR: failed to delete file.");
        }
        System.out.println("File exists: " + file.exists());
    }
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    public static void main(String... args) throws Exception {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        DestructiveBean bean = (DestructiveBean) ctx.getBean("destructiveBean");
        System.out.println("Calling destroy()");
        ctx.destroy(); // ■■■■■■■■■
        System.out.println("Called destroy()");
    }
}
  • destroy() = designate a method to be called when a bean is destroyed
  • Spring will not call this method for those beans with prototype scope
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="destructiveBean"
          class="com.apress.prospring5.ch4.DestructiveBean"
          destroy-method="destroy" // ■■■■■■■■■
          p:filePath=
                  "#{systemProperties'java.io.tmpdir'}# 
                   {systemProperties'file.separator'}test.txt" 
          			// SpEL ■■■■■■■■■ to ensure cross-platform compatibility
          /> 
</beans>

Result

Initializing Bean
File exists: true
Calling destroy()
Destroying Bean
File exists: false
Called destroy()

6.1 Implementing the DisposableBean Interface

import java.io.File;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.GenericXmlApplicationContext;
public class DestructiveBeanWithInterface implements InitializingBean, DisposableBean { // ■■■■■■■■■
    private File file;
    private String filePath;
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing Bean");
        if (filePath == null) {
            throw new IllegalArgumentException(
                    "You must specify the filePath property of " +
                            DestructiveBeanWithInterface.class);
        }
        this.file = new File(filePath);
        this.file.createNewFile();
        System.out.println("File exists: " + file.exists());
    }
    @Override // ■■■■■■■■■
    public void destroy() { 
        System.out.println("Destroying Bean");
        if(!file.delete()) {
            System.err.println("ERROR: failed to delete file.");
        }
        System.out.println("File exists: " + file.exists());
    }
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    public static void main(String... args) throws Exception {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        DestructiveBeanWithInterface bean =
                (DestructiveBeanWithInterface) ctx.getBean("destructiveBean");
        System.out.println("Calling destroy()");
        ctx.destroy(); // ■■■■■■■■■
        System.out.println("Called destroy()");
    }
}
<beans ...>
	<bean id="destructiveBean"
         class="com.apress.prospring5.ch4.DestructiveBeanWithInterface"
        p:filePath=
         "#{systemProperties'java.io.tmpdir'}#{systemProperties'file.separator'}test.txt"/>
</beans>
Initializing Bean
File exists: true
Calling destroy()
Destroying Bean
File exists: false
Called destroy()

7 Using the JSR-250 @PreDestroy Annotation

import java.io.File;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.context.support.GenericXmlApplicationContext;
public class DestructiveBeanWithJSR250 {
    private File file;
    private String filePath;
    @PostConstruct // ■■■■■■■■■
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing Bean");
        if (filePath == null) {
            throw new IllegalArgumentException(
                    "You must specify the filePath property of " +
                            DestructiveBeanWithJSR250.class);
        }
        this.file = new File(filePath);
        this.file.createNewFile();
        System.out.println("File exists: " + file.exists());
    }
    @PreDestroy // ■■■■■■■■■
    public void destroy() {
        System.out.println("Destroying Bean");
        if(!file.delete()) {
            System.err.println("ERROR: failed to delete file.");
        }
        System.out.println("File exists: " + file.exists());
    }
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    public static void main(String... args) throws Exception {
        GenericXmlApplicationContext ctx =
                new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-annotation.xml");
        ctx.refresh();
        DestructiveBeanWithJSR250 bean =
                (DestructiveBeanWithJSR250) ctx.getBean("destructiveBean");
        System.out.println("Calling destroy()");
        ctx.destroy(); // ■■■■■■■■■
        System.out.println("Called destroy()");
    }
}
<beans ...>
    <context:annotation-config/>
    <bean id="destructiveBean"
          class="com.apress.prospring5.ch4.DestructiveBeanWithJSR250"
          p:filePath=
          "#{systemProperties'java.io.tmpdir'}#{systemProperties'file.separator'}test.txt"/>
</beans>

8 Declaring a Destroy Method Using @Bean

  • @Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy")
import com.apress.prospring5.ch4.DestructiveBeanWithJSR250;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.GenericApplicationContext;
/**
* Created by iuliana.cosmina on 2/27/17.
*/
public class DestructiveBeanConfigDemo {
    @Configuration
    static class DestructiveBeanConfig {
        @Lazy
        @Bean(initMethod = "afterPropertiesSet", destroyMethod = "destroy") // ■■■■■■■■■
        DestructiveBeanWithJSR250 destructiveBean() {
            DestructiveBeanWithJSR250 destructiveBean =
                new DestructiveBeanWithJSR250();
            destructiveBean.setFilePath(System.getProperty("java.io.tmpdir") +
                                        System.getProperty("file.separator") + "test.txt");
            return destructiveBean;
        }
    }
    public static void main(String... args) {
        GenericApplicationContext ctx =
            new AnnotationConfigApplicationContext(DestructiveBeanConfig.class);
        ctx.getBean(DestructiveBeanWithJSR250.class);
        System.out.println("Calling destroy()");
        ctx.destroy();
        System.out.println("Called destroy()");
    }
}
Initializing Bean
File exists: true
Calling destroy()
Destroying Bean
File exists: false
Called destroy()

9 Understanding Order of Resolution

  • We can use all mechanisms on the same bean instance for bean destruction
  • If you use all mechanism the order is like,
    • @PreDestroy -> DisposableBean.destroy() -> destroy method configured in your XML definition

9.1 Using a Shutdown Hook

  • To do destruction callback, (drawback) we should remember to call AbstractApplicationContext.destroy() before application is closed. - ctx.destroy();
    • Not simple in stand-alone application (e.g., multiple exit) (??)
    • solution: registerShutdownHook() method
      • Not need to call ctx.destroy() or close() any more.
    • Q. anyway we should call registerShutdownHook(), but why is it benfit?
      • One problem is that these destroy callback methods are not executed automatically you do have to call AbstractApplicationContext.destroy or AbstractApplicationContext.close before shutting down the IOC container. There is another option to ensure graceful shutdown, you can register a shutdown hook with the JVM using registerShutdownHook() method in Spring. link
// ...
    public class DestructiveBeanWithHook {
        public static void main(String... args) {
            GenericApplicationContext ctx =
                new AnnotationConfigApplicationContext(
                DestructiveBeanConfig.class);
            ctx.getBean(DestructiveBeanWithJSR250.class);
            // ctx.destroy(); -> removed
            ctx.registerShutdownHook(); // ■■■■■■■■■
        }
// ...

10 Making Your Beans “Spring Aware”

(??)

BeanNameAware and BeanFactoryAware Interfaces in Spring

  • BeanNameAware makes the object aware of the bean name defined in the container.
  • BeanFactoryAware is used to inject the BeanFactory object
  • The typical use case for BeanNameAware could be acquiring the bean name for logging or wiring purposes. For the BeanFactoryAware it could be the ability to use a spring bean from legacy code. In most cases, we should avoid using any of the Aware interfaces, unless we need them. Implementing these interfaces will couple the code to the Spring framework.

10.1 Using the BeanNameAware Interface

  • can be implemented by a bean that wants to obtain its own name
  • Spring calls the setBeanName() method after it has finished configuring your bean but before any life-cycle callbacks (initialization or destroy) are called (see sequence of life cycle above)
  • Call sequence: BeanNameAware.setBeanName() is called before the first instance of the bean is returned to your application via a call to ApplicationContext.getBean()
    • Q. Does this mean that one cannot use name in sing() method? then we can add it to bug pattern... (??)
  • Useful for improving quality of log messages
  • Q. What does it mean? - "Avoid being tempted to give your bean names business meaning just because you can access them" (??)
import org.springframework.beans.factory.BeanNameAware;
public class NamedSinger implements BeanNameAware {
    private String name;
    /** @Implements {@link BeanNameAware#setBeanName(String)} */
    public void setBeanName(String beanName) {
        this.name = beanName;
    }
    public void sing() {
        System.out.println("Singer " + name + " - sing()");
    }
}
<beans ...>
    <bean id="johnMayer"
          class="com.apress.prospring5.ch4.NamedSinger"/>
</beans>
import org.springframework.context.support.GenericXmlApplicationContext;
public class NamedSingerDemo {
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        NamedSinger bean = (NamedSinger) ctx.getBean("johnMayer"); 
        bean.sing();
        ctx.close();
    }
}

10.2 Using the ApplicationContextAware Interface

  • Why introduced?
    • Spring can be used to deal with beans that require other beans to function that are not injected using constructors or setters in the configuration
    • Using the ApplicationContextAware interface, it is possible for your beans to get a reference to the ApplicationContext instance that configured them
    • (main reason) to allow a bean to access Spring’s ApplicationContext in your application, e.g, to acquire other Spring beans programmatically
  • however, avoid this practice and use dependency injection to provide your beans with their collaborators
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
public class ShutdownHookBean implements ApplicationContextAware {
    private ApplicationContext ctx;
    /** @Implements {@link ApplicationContextAware#s
 etApplicationContext(ApplicationContext)} }*/
    public void setApplicationContext(ApplicationContext ctx)
        throws BeansException {
        if (ctx instanceof GenericApplicationContext) {
            ((GenericApplicationContext) ctx).registerShutdownHook();
        }
    }
}
<beans ...>
    <context:annotation-config/>
    <bean id="destructiveBean"
          class="com.apress.prospring5.ch4.DestructiveBeanWithInterface"
          p:filePath=
          "#{systemProperties'java.io.tmpdir'}#{systemProperties'file.separator'}test.txt"/>
    <bean id="shutdownHook"
          class="com.apress.prospring5.ch4.ShutdownHookBean"/>
</beans>
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
import org.springframework.context.support.GenericXmlApplicationContext;
public class DestructiveBeanWithInterface {
    private File file;
    private String filePath;
    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        System.out.println("Initializing Bean");
        if (filePath == null) {
            throw new IllegalArgumentException(
                "You must specify the filePath property of " +
                DestructiveBeanWithInterface.class);
        }
        this.file = new File(filePath);
        this.file.createNewFile();
        System.out.println("File exists: " + file.exists());
    }
    @PreDestroy
    public void destroy() {
        System.out.println("Destroying Bean");
        if(!file.delete()) {
            System.err.println("ERROR: failed to delete file.");
        }
        System.out.println("File exists: " + file.exists());
    }
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }
    public static void main(String... args) throws Exception {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-annotation.xml");
        ctx.registerShutdownHook();
        ctx.refresh();
        ctx.getBean("destructiveBean",
                    DestructiveBeanWithInterface.class);
    }
}

11 Use of FactoryBeans

How to use the Spring FactoryBean?

  • There are two kinds of beans in the Spring bean container: ordinary beans and factory beans. Spring uses the former directly, whereas latter can produce objects themselves, which are managed by the framework. Simply put, we can build a factory bean by implementing org.springframework.beans.factory.FactoryBean interface.
  • Spring container uses the object produced by the FactoryBean instead of itself for dependency injection.
  • Using a FactoryBean can be a good practice to encapsulate complex construction logic or make configuring highly configurable objects easier in Spring.
  • Facing problems while using Spring = how to create and then inject dependencies that cannot be created simply by using the new operator.
    • Solution: FactoryBean interface (acts as an adapter)
  • FactoryBeans are used to great effect in Spring; the most noticeable uses are the creation of transactional proxies, which we cover in Chapter 9, and the automatic retrieval of resources from a JNDI context.

11.1 FactoryBean Example: The MessageDigestFactoryBean

  • cryptographic processing
    • generating a message digest
    • hash of a user’s password to be stored in a database
  • In Java, MessageDigest
    • MessageDigest md5 = MessageDigest.getInstance("MD5");
  • In Spring,
    • we can do without a FactoryBean is have a property, algorithmName, on your bean and then use an initialization callback to call MessageDigest.getInstance().
    • any beans that require a MessageDigest instance can simply declare a property, messageDigest, and use the FactoryBean to obtain the instance.
import org.springframework.context.support.GenericXmlApplicationContext;

public class MessageDigestDemo {
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        MessageDigester digester = ctx.getBean("digester",
                                               MessageDigester.class);
        digester.digest("Hello World!");
        ctx.close();
    }
}
<beans ...>
    <bean id="shaDigest"
          class="com.apress.prospring5.ch4.MessageDigestFactoryBean"
          p:algorithmName="SHA1"/>
    <bean id="defaultDigest"
          class="com.apress.prospring5.ch4.MessageDigestFactoryBean"/>
    
    <bean id="digester"
          class="com.apress.prospring5.ch4.MessageDigester"
          p:digest1-ref="shaDigest"
          p:digest2-ref="defaultDigest"/>
</beans>
import java.security.MessageDigest;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

public class MessageDigestFactoryBean implements
    FactoryBean<MessageDigest>, InitializingBean {
    private String algorithmName = "MD5"; // default message-digest algorithm 
    private MessageDigest messageDigest = null;
    public MessageDigest getObject() throws Exception {
        return messageDigest;
    }
    public Class<MessageDigest> getObjectType() {
        return MessageDigest.class;
    }
    public boolean isSingleton() {
        return true;
    }
    public void afterPropertiesSet() throws Exception {
        messageDigest = MessageDigest.getInstance(algorithmName);
    }
    public void setAlgorithmName(String algorithmName) {
        this.algorithmName = algorithmName;
    }
}
import java.security.MessageDigest;

public class MessageDigester {
    private MessageDigest digest1;
    private MessageDigest digest2;
    public void setDigest1(MessageDigest digest1) {  // SHA1
        this.digest1 = digest1;
    }
    public void setDigest2(MessageDigest digest2) { // MD5 
        this.digest2 = digest2;
    }
    public void digest(String msg) {
        System.out.println("Using digest1");
        digest(msg, digest1);
        System.out.println("Using digest2");
        digest(msg, digest2);
    }
    private void digest(String msg, MessageDigest digest) {
        System.out.println("Using alogrithm: " + digest.getAlgorithm());
        digest.reset();
        byte[] bytes = msg.getBytes();
        byte[] out = digest.digest(bytes);
        System.out.println(out);
    }
}
Using digest1
Using alogrithm: SHA1
[B@679b62af // hello world 
Using digest2
Using alogrithm: MD5
[B@5cdd8682 // hello world 

Another way - using Java configuration

import com.apress.prospring5.ch4.MessageDigestFactoryBean;
import com.apress.prospring5.ch4.MessageDigester;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
public class MessageDigesterConfigDemo {
    @Configuration
    static class MessageDigesterConfig {
        @Bean
        public MessageDigestFactoryBean shaDigest() {
            MessageDigestFactoryBean factoryOne =
                new MessageDigestFactoryBean();
            factoryOne.setAlgorithmName("SHA1");
            return factoryOne;
        }
        @Bean
        public MessageDigestFactoryBean defaultDigest() {
            return new MessageDigestFactoryBean();
        }
        @Bean
        MessageDigester digester() throws Exception {
            MessageDigester messageDigester = new MessageDigester();
            messageDigester.setDigest1(shaDigest().getObject());
            messageDigester.setDigest2(defaultDigest().getObject());
            return messageDigester;
        }
    }
    public static void main(String... args) {
        GenericApplicationContext ctx =
            new AnnotationConfigApplicationContext(MessageDigesterConfig.class);
        MessageDigester digester = (MessageDigester) ctx.getBean("digester");
        digester.digest("Hello World!");
        ctx.close();
    }
}

12 Accessing a FactoryBean Directly

  • How access?
    • Prefix the bean name with ampersand (&)
  • But Avoid accessing FactoryBean directly
    • extra work and unnecessarily coupling
import org.springframework.context.support.GenericXmlApplicationContext;
import java.security.MessageDigest;
public class AccessingFactoryBeans {
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        ctx.getBean("shaDigest", MessageDigest.class);
        MessageDigestFactoryBean factoryBean =
            (MessageDigestFactoryBean) ctx.getBean("&shaDigest"); // ■■■■■■■
        try {
            MessageDigest shaDigest = factoryBean.getObject();
            System.out.println(shaDigest.digest("Hello world".getBytes()));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        ctx.close();
    }
}

// Result: B@130f889

13 Using the factory-bean and factory-method Attributes

not know how the codes works

import java.security.MessageDigest;
public class MessageDigestFactory {
    private String algorithmName = "MD5";
    public MessageDigest createInstance() throws Exception {
        return MessageDigest.getInstance(algorithmName);
    }
    public void setAlgorithmName(String algorithmName) {
        this.algorithmName = algorithmName;
    }
}
import java.security.MessageDigest;

public class MessageDigester {
    private MessageDigest digest1;
    private MessageDigest digest2;
    public void setDigest1(MessageDigest digest1) {
        this.digest1 = digest1;
    }
    public void setDigest2(MessageDigest digest2) {
        this.digest2 = digest2;
    }
    public void digest(String msg) {
        System.out.println("Using digest1");
        digest(msg, digest1);
        System.out.println("Using digest2");
        digest(msg, digest2);
    }
    private void digest(String msg, MessageDigest digest) {
        System.out.println("Using alogrithm: " + digest.getAlgorithm());
        digest.reset();
        byte[] bytes = msg.getBytes();
        byte[] out = digest.digest(bytes);
        System.out.println(out);
    }
}
  • two digest factory beans , shaDigestFactory and defaultDigestFactory, were defined
    • SHA1 and default algorithm each
  • To designate digest factory bean, factory-bean attribute is used in shaDigest and defaultDigest
<beans ...>
    <bean id="shaDigestFactory"
          class="com.apress.prospring5.ch4.MessageDigestFactory"
          p:algorithmName="SHA1"/>
    <bean id="defaultDigestFactory"
          class="com.apress.prospring5.ch4.MessageDigestFactory"/>
    <bean id="shaDigest"
          factory-bean="shaDigestFactory"
          factory-method="createInstance">
    </bean>
    <bean id="defaultDigest"
          factory-bean="defaultDigestFactory"
          factory-method="createInstance"/>
    <bean id="digester"
          class="com.apress.prospring5.ch4.MessageDigester"
          p:digest1-ref="shaDigest"
          p:digest2-ref="defaultDigest"/>
</beans>
import org.springframework.context.support.GenericXmlApplicationContext;
public class MessageDigestFactoryDemo {
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-xml.xml");
        ctx.refresh();
        MessageDigester digester = ctx.getBean("digester",
                                               MessageDigester.class);
        digester.digest("Hello World!");
        ctx.close();
    }
}

/*
Result
 Using digest1
 Using alogrithm: SHA1 
 [B@77a57272 
 Using digest2
 Using alogrithm: MD5 
 [B@7181ae3f
*/

14 JavaBeans PropertyEditors

https://docs.spring.io/spring-framework/docs/1.2.x/reference/validation.html

PropertyEditors = converting types

Sometimes it might be handy to be able to represent properties in a different way than the object itself. For example, a date can be represented in a human readable way, while we're still able to convert the human readable form back to the original date. This behavior can be achieved by registering custom editors, of type java.beans.PropertyEditor.

Examples where property editing is used in Spring

  • setting properties on beans is done using PropertyEditors. When mentioning java.lang.String as the value of a property of some bean you're declaring in XML file, Spring will (if the setter of the corresponding property has a Class-parameter) use the ClassEditor to try to resolve the parameter to a Class object
  • parsing HTTP request parameters in Spring's MVC framework is done using all kinds of PropertyEditors that you can manually bind in all subclasses of the CommandController
  • PropertyEditor converts a property’s value to and from its native type representation into a String.
    • e.g. date object -> human readable text (or vice versa)
  • PropertyEditors = lightweight classes
  • In spring,
    • used to manage the conversion of String-based property values into the correct types

Summary - Used for data conversion

14.1 Using the Built-in PropertyEditors

import java.io.File;
import java.io.InputStream;
import java.net.URL; import java.util.Date; import java.util.List; import java.util.Locale;
import java.util.Properties; import java.util.regex.Pattern; import java.text.
    SimpleDateFormat;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.context.support.GenericXmlApplicationContext;

public class PropertyEditorBean {
    private byte[] bytes; // ByteArrayPropertyEditor
    private Character character; //CharacterEditor
    private Class cls; // ClassEditor
    private Boolean trueOrFalse; // CustomBooleanEditor
    private List<String> stringList; // CustomCollectionEditor
    private Date date; // CustomDateEditor
    private Float floatValue; // CustomNumberEditor
    private File file; // FileEditor
    private InputStream stream; // InputStreamEditor
    private Locale locale; // LocaleEditor
    private Pattern pattern; // PatternEditor
    private Properties properties; // PropertiesEditor
    private String trimString; // StringTrimmerEditor
    private URL url; // URLEditor
    
    public void setCharacter(Character character) {
        System.out.println("Setting character: " + character);
        this.character = character;
    }
    public void setCls(Class cls) {
        System.out.println("Setting class: " + cls.getName());
        this.cls = cls;
    }
    public void setFile(File file) {
        System.out.println("Setting file: " + file.getName());
        this.file = file;
    }
    public void setLocale(Locale locale) {
        System.out.println("Setting locale: " + locale.getDisplayName());
        this.locale = locale;
    }
    public void setProperties(Properties properties) {
        System.out.println("Loaded " + properties.size() + " properties");
        this.properties = properties;
    }

    public void setUrl(URL url) {
        System.out.println("Setting URL: " + url.toExternalForm());
        this.url = url;
    }
    public void setBytes(byte... bytes) {
        System.out.println("Setting bytes: " + Arrays.toString(bytes));
        this.bytes = bytes;
    }
    public void setTrueOrFalse(Boolean trueOrFalse) {
        System.out.println("Setting Boolean: " + trueOrFalse);
        this.trueOrFalse = trueOrFalse;
    }
    public void setStringList(List<String> stringList) {
        System.out.println("Setting string list with size: "
                           + stringList.size());
        this.stringList = stringList;
        for (String string: stringList) {
            System.out.println("String member: " + string);
        }
    }
    public void setDate(Date date) {
        System.out.println("Setting date: " + date);
        this.date = date;
    }
    public void setFloatValue(Float floatValue) {
        System.out.println("Setting float value: " + floatValue);
        this.floatValue = floatValue;
    }
    public void setStream(InputStream stream) {
        System.out.println("Setting stream: " + stream);
        this.stream = stream;
    }
    public void setPattern(Pattern pattern) {
        System.out.println("Setting pattern: " + pattern);
        this.pattern = pattern;
    }
    public void setTrimString(String trimString) {
        System.out.println("Setting trim string: " + trimString);
        this.trimString = trimString;
    }
    public static class CustomPropertyEditorRegistrar
        implements PropertyEditorRegistrar {
        @Override
        public void registerCustomEditors(PropertyEditorRegistry registry) {
            SimpleDateFormat dateFormatter = new SimpleDateFormat("MM/dd/yyyy");
            registry.registerCustomEditor(Date.class,
                                          new CustomDateEditor(dateFormatter, true));
            registry.registerCustomEditor(String.class, new StringTrimmerEditor(true));
        }
    }
    public static void main(String... args) throws Exception {
        File file = File.createTempFile("test", "txt");
        file.deleteOnExit();
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-01.xml");
        ctx.refresh();
        PropertyEditorBean bean =
            (PropertyEditorBean) ctx.getBean("builtInSample");
        ctx.close();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util
                           http://www.springframework.org/schema/util/spring-util.xsd">
    <bean id="customEditorConfigurer"
          class="org.springframework.beans.factory.config.CustomEditorConfigurer"
          p:propertyEditorRegistrars-ref="propertyEditorRegistrarsList"/> 
    <util:list id="propertyEditorRegistrarsList">
        <bean class="com.apress.prospring5.ch4.PropertyEditorBean$
                     CustomPropertyEditorRegistrar"/>
    </util:list>
    <bean id="builtInSample"
          class="com.apress.prospring5.ch4.PropertyEditorBean"
          p:character="A"
          p:bytes="John Mayer"
          p:cls="java.lang.String"
          p:trueOrFalse="true"
          p:stringList-ref="stringList"
          p:stream="test.txt"
          p:floatValue="123.45678"
          p:date="05/03/13"
          p:file="#{systemProperties'java.io.tmpdir'}
                  #{systemProperties'file.separator'}test.txt"
          p:locale="en_US"
          p:pattern="a*b"
          p:properties="name=Chris age=32"
          p:trimString=" String need trimming "
          p:url="https://spring.io/"
          />
    <util:list id="stringList">
        <value>String member 1</value>
        <value>String member 2</value>
    </util:list>
</beans>

Result

Setting bytes: [74, 111, 104, 110, 32, 77, 97, 121, 101, 114] Setting character: A
Setting class: java.lang.String Setting date: Wed May 03  00:00:00 EET 13 Setting file: test.txt Setting float value: 123.45678 Setting locale: English (United States) Setting pattern: a*b Loaded 1 properties
Setting stream: java.io.BufferedInputStream@42e25b0b Setting string list with size: 2 String member: String member 1 String member: String member 2 Setting trim string: String need trimming Setting Boolean: true
Setting URL: https://spring.io/

Spring has converted the String representations of the various properties to the correct types

14.2 Creating a Custom PropertyEditor

  • should extend PropertyEditorSupport
  • only a single method: setAsText()
  • From Spring 3
    • Spring introduced the Type Conversion API and the Field Formatting Service Provider Interface (SPI), which provide a simpler and well-structured API to perform type conversion and field formatting. It’s especially useful for web application development. (see details in chapter 10)
public class FullName {
    private String firstName;
    private String lastName;
    public FullName(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String toString() {
        return "First name: " + firstName + " - Last name: " + lastName;
    }
}
import java.beans.PropertyEditorSupport;
public class NamePropertyEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        String[] name = text.split("\\s"); // space
        setValue(new FullName(name[0], name[1])); // PropertyEditorSupport method
    }
}

ApplicationContext configuration of CustomEditorConfigurer and NamePropertyEditor

  • should notice two things 어렵다
    • custom PropertyEditors get injected into the CustomEditorConfigurer class by using the Map-typed customEditors property
    • each entry in the Map represents a single PropertyEditor, with the key of the entry being the name of the class for which the PropertyEditor is used.
<beans ...>
    <bean name="customEditorConfigurer" 
          class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="com.apress.prospring5.ch4.custom.FullName"
                       value="com.apress.prospring5.ch4.custom.NamePropertyEditor"/>
                // register NamePropertyEditor in Spring’s ApplicationContext
            </map>
        </property>
    </bean>
    <bean id="exampleBean" 
          class="com.apress.prospring5.ch4.custom.CustomEditorExample"
          p:name="John Mayer"/>
</beans>
import org.springframework.context.support.GenericXmlApplicationContext;
public class CustomEditorExample {
    private FullName name; 
    public FullName getName() {
        return name;
    }
    public void setName(FullName name) { 
        this.name = name;
    }
    public static void main(String... args) {
        GenericXmlApplicationContext ctx =
            new GenericXmlApplicationContext();
        ctx.load("classpath:spring/app-context-02.xml"); // 1
        ctx.refresh();
        CustomEditorExample bean =
            (CustomEditorExample) ctx.getBean("exampleBean"); 
        System.out.println(bean.getName());
        ctx.close();
    }
}
Result: First name: John - Last name: Mayer

the first name and last name of the FullName object were correctly populated by Spring by using the configured NamePropertyEditor

15 More Spring ApplicationContext Configuration

ApplicationContext supports the following features:

  • Internationalization
  • Event publication
  • Resource management and access
  • Additional life-cycle interfaces
  • Improved automatic configuration of infrastructure components

15.1 Internationalization with the MessageSource

import java.util.Locale;
import org.springframework.context.support.GenericXmlApplicationContext;
public class MessageSourceDemo {
 public static void main(String... args) {
 GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
 ctx.load("classpath:spring/app-context-xml.xml");
 ctx.refresh();
 Locale english = Locale.ENGLISH;
 Locale german = new Locale("de", "DE");
 System.out.println(ctx.getMessage("msg", null, english));
 System.out.println(ctx.getMessage("msg", null, german));
 System.out.println(ctx.getMessage("nameMsg", new Object[]
 { "John", "Mayer" }, english));
 System.out.println(ctx.getMessage("nameMsg", new Object[]
 { "John", "Mayer" }, german));
 ctx.close();
 }
}
<beans ...>
 <bean id="messageSource"
 class="org.springframework.context.support.ResourceBundleMessageSource"
 p:basenames-ref="basenames"/>
 <util:list id="basenames">
 <value>buttons</value>
 <value>labels</value>
 </util:list>
</beans>

15.2 Using MessageSource in Stand-Alone Applications

15.3 Application Events

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Publisher implements ApplicationContextAware {
 private ApplicationContext ctx;
 public void setApplicationContext(ApplicationContext applicationContext)
 throws BeansException {
 this.ctx = applicationContext;
 }
 public void publish(String message) {
 ctx.publishEvent(new MessageEvent(this, message));
 }
 public static void main(String... args) {
 ApplicationContext ctx = new ClassPathXmlApplicationContext(
 "classpath:spring/app-context-xml.xml");
 Publisher pub = (Publisher) ctx.getBean("publisher");
 pub.publish("I send an SOS to the world... ");
 pub.publish("... I hope that someone gets my...");
 pub.publish("... Message in a bottle");
 }
}
<beans ...>
 <bean id="publisher"
 class="com.apress.prospring5.ch4.Publisher"/>
 <bean id="messageEventListener"
 class="com.apress.prospring5.ch4.MessageEventListener"/>
</beans>

16 Accessing Resources

import java.io.File;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
public class ResourceDemo {
 public static void main(String... args) throws Exception{
 ApplicationContext ctx = new ClassPathXmlApplicationContext();
 File file = File.createTempFile("test", "txt");
 file.deleteOnExit();
 Resource res1 = ctx.getResource("file://" + file.getPath());
 displayInfo(res1);
 Resource res2 = ctx.getResource("classpath:test.txt");
 displayInfo(res2);
 Resource res3 = ctx.getResource("http://www.google.com");
 displayInfo(res3);
 }
 private static void displayInfo(Resource res) throws Exception{
 System.out.println(res.getClass());
 System.out.println(res.getURL().getContent());
 System.out.println("");
 }
}

17 Configuration Using Java Classes

17.1 ApplicationContext Configuration in Java

public interface MessageProvider {
 String getMessage();
}
//chapter03/constructor-injection/src/main/java/com/apress/prospring5/
// ch3/xml/ConfigurableMessageProvider.java
package com.apress.prospring5.ch3.xml;
import com.apress.prospring5.ch2.decoupled.MessageProvider;
public class ConfigurableMessageProvider implements MessageProvider {
 private String message = "Default message";
 public ConfigurableMessageProvider() {
 }
 public ConfigurableMessageProvider(String message) {
 this.message = message;
 }
 public void setMessage(String message) {
 this.message = message;
 }
 public String getMessage() {
 return message;
 }
}
public interface MessageRenderer {
 void render();
 void setMessageProvider(MessageProvider provider);
 MessageProvider getMessageProvider();
}
//chapter02/hello-world/src/main/java/com/apress/prospring5/
// ch2/decoupled/StandardOutMessageRenderer.java
package com.apress.prospring5.ch2.decoupled;
public class StandardOutMessageRenderer
 implements MessageRenderer {
 private MessageProvider messageProvider;
 public StandardOutMessageRenderer(){
 System.out.println(" -->
 StandardOutMessageRenderer: constructor called");
 }
 @Override
 public void render() {
 if (messageProvider == null) {
 throw new RuntimeException(
 "You must set the property messageProvider of class:"
 + StandardOutMessageRenderer.class.getName());
 } System.out.println(messageProvider.getMessage());
 }
 @Override
 public void setMessageProvider(MessageProvider provider) {
 System.out.println(" -->
 StandardOutMessageRenderer: setting the provider");
 this.messageProvider = provider;
 }
 @Override
 public MessageProvider getMessageProvider() {
 return this.messageProvider;
 }
}
<beans ...>
 <bean id="messageRenderer"
 class="com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer"
 p:messageProvider-ref="messageProvider"/>
 <bean id="messageProvider"
 class="com.apress.prospring5.ch3.xml.ConfigurableMessageProvider"
 c:message="This is a configurable message"/>
</beans>
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JavaConfigXMLExample {
public static void main(String... args) {
 ApplicationContext ctx = new ClassPathXmlApplicationContext("
 classpath:spring/app-context-xml.xml");
 MessageRenderer renderer =
 ctx.getBean("messageRenderer", MessageRenderer.class);
 renderer.render();
 }
}
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import com.apress.prospring5.ch3.xml.ConfigurableMessageProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
 @Bean
 public MessageProvider messageProvider() {
 return new ConfigurableMessageProvider();
 }
 @Bean
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(messageProvider());
 return renderer;
 }
}
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigExampleOne {
 public static void main(String... args) {
 ApplicationContext ctx = new
 AnnotationConfigApplicationContext(AppConfig.class);
 MessageRenderer renderer =
 ctx.getBean("messageRenderer", MessageRenderer.class);
 renderer.render();
 }
}

import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import com.apress.prospring5.ch3.xml.ConfigurableMessageProvider;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class JavaConfigSimpleExample {
 @Configuration
 static class AppConfigOne {
 @Bean
 public MessageProvider messageProvider() {
 return new ConfigurableMessageProvider();
 }
 @Bean
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(messageProvider());
 return renderer;
 }
 }
 public static void main(String... args) {
 ApplicationContext ctx = new
 AnnotationConfigApplicationContext(AppConfig.class);
 MessageRenderer renderer =
 ctx.getBean("messageRenderer", MessageRenderer.class);
 renderer.render();
 }
}
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
@PropertySource(value = "classpath:message.properties")
public class AppConfigOne {
 @Autowired
 Environment env;
 @Bean
 public MessageProvider messageProvider() {
 return new ConfigurableMessageProvider(env.getProperty("message"));
 }
 @Bean(name = "messageRenderer")
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(messageProvider());
 return renderer;
 }
}
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
@Configuration
@PropertySource(value = "classpath:message.properties")
public class AppConfig {
 @Autowired
 Environment env;
 @Bean
 @Lazy
 public MessageProvider messageProvider() {
 return new ConfigurableMessageProvider(env.getProperty("message"));
 }
 @Bean(name = "messageRenderer")
 @Scope(value="prototype")
 @DependsOn(value="messageProvider")
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(messageProvider());
 return renderer;
 }
}
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service("provider")
public class ConfigurableMessageProvider implements MessageProvider {
 private String message;
 public ConfigurableMessageProvider(
 @Value("Love on the weekend")String message) {
 this.message = message;
 }
 @Override
 public String getMessage() {
 return this.message;
 }
}
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan(basePackages={"com.apress.prospring5.ch4.annotated"})
public class AppConfigTwo {
 @Autowired
 MessageProvider provider;
 @Bean(name = "messageRenderer")
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer =
 new StandardOutMessageRenderer();
 renderer.setMessageProvider(provider);
 return renderer;
 }
}
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class JavaConfigExampleTwo {
 public static void main(String... args) {
 ApplicationContext ctx = new
 AnnotationConfigApplicationContext(AppConfigTwo.class);
 MessageRenderer renderer =
 ctx.getBean("messageRenderer", MessageRenderer.class);
 renderer.render();
 }
}
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages={"com.apress.prospring5.ch4.annotated"})
public class AppConfigFour { }
package com.apress.prospring5.ch4.multiple;
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(AppConfigFour.class)
public class AppConfigThree {
 @Autowired
 MessageProvider provider;
 @Bean(name = "messageRenderer")
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(provider);
 return renderer;
 }
}

17.2 Spring Mixed Configuration

<beans ...>
 <bean id="provider"
 class="com.apress.prospring5.ch4.ConfigurableMessageProvider"
 p:message="Love on the weekend" />
</beans>
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
@Configuration
@ImportResource(value="classpath:spring/app-context-xml-01.xml")
public class AppConfigFive {
 @Autowired
 MessageProvider provider;
 @Bean(name = "messageRenderer")
 public MessageRenderer messageRenderer() {
 MessageRenderer renderer = new StandardOutMessageRenderer();
 renderer.setMessageProvider(provider);
 return renderer;
 }
}
<beans ...>
 <context:annotation-config/>
 <bean class="com.apress.prospring5.ch4.mixed.AppConfigSix"/>
 <bean id="messageRenderer"
 class="com.apress.prospring5.ch2.decoupled.StandardOutMessageRenderer"
 p:messageProvider-ref="provider"/>
</beans>
import com.apress.prospring5.ch2.decoupled.MessageProvider;
import com.apress.prospring5.ch4.annotated.ConfigurableMessageProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfigSix {
 @Bean
 public MessageProvider provider() {
 return new ConfigurableMessageProvider("Love on the weekend");
 }
}
import com.apress.prospring5.ch2.decoupled.MessageRenderer;
import com.apress.prospring5.ch4.mixed.AppConfigFive;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JavaConfigExampleThree {
 public static void main(String... args) {
 ApplicationContext ctx =
 new ClassPathXmlApplicationContext
 ("classpath:spring/app-context-xml-02.xml");
 MessageRenderer renderer =
 ctx.getBean("messageRenderer", MessageRenderer.class);
 renderer.render();
 }
}

17.3 Java or XML Configuration?

18 Profiles

18.1 An Example of Using the Spring Profiles Feature

19 Spring Profiles Using Java Configuration

19.1 Considerations for Using Profiles

20 Environment and PropertySource Abstraction

21 Configuration Using JSR-330 Annotations

22 Configuration Using Groovy xxx

23 Spring Boot

⚠️ **GitHub.com Fallback** ⚠️