ProSpring5Chap4 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki
(??) = 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
(??)
- 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)
- 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.
- Spring allows you to specify, in your
- annotation-based (recommended)
- you can use JSR-250 annotations to specify the method that Spring should call after construction or before destruction.
- We do not need to care about initialization and destruction each time we use one of Spring’s components
- 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.
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
, callsetBeanName()
-> If bean type implementsBeanClassLoaderAware
, callsetBeanClassLoader()
-> If bean type implementsAppplicationContextAware
, callsetApplicationContext()
- -> If
@PostConstruct
annotation is present call method annotated with it -> If bean type implementsInitializaingBean
, callafterPropertiesSet()
-> If bean definition containsinit-method
or@Bean(initMethod="..")
, call the init method - -> If
@PreDestroy
annotation is present call method annotated with it -> If bean type implementsDisposableBean
, calldestroy()
-> If bean definition containsdestroy-method
or@Bean(destroyMethod="..")
, call the destroy method.
**4 ways for controlling life cycle events **link
- Custom
init()
anddestroy()
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
andDisposableBean
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.
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
throwsIllegalArgumentException
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." (??)
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>
- 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>
-
@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();
}
}
- 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 byCommonAnnotationBeanPostProcessor
and called
-
-
InitializingBean
’safterPropertiesSet
is executed (invoked by aBeanFactory
) - init-method attribute is executed
- (Assumed
ApplicationContext
is used) signal toBeanFactory
to destroy all singleton instances with a call toConfigurableBeanFactory. 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
- 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
- Custom
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()
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()
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>
@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()
- 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
-
- 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()
orclose()
any more.
- Not need to call
- 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(); // ■■■■■■■■■
}
// ...
(??)
BeanNameAware and BeanFactoryAware Interfaces in Spring
BeanNameAware
makes the object aware of the bean name defined in the container.BeanFactoryAware
is used to inject theBeanFactory
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.
- 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 toApplicationContext.getBean()
- Q. Does this mean that one cannot use name in
sing()
method? then we can add it to bug pattern... (??)
- Q. Does this mean that one cannot use name in
- 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();
}
}
- 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);
}
}
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)
- Solution:
- 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.
- 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 callMessageDigest.getInstance()
. - any beans that require a
MessageDigest
instance can simply declare a property,messageDigest
, and use theFactoryBean
to obtain the instance.
- we can do without a
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();
}
}
- 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
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
anddefaultDigestFactory
, were defined- SHA1 and default algorithm each
- To designate digest factory bean,
factory-bean
attribute is used inshaDigest
anddefaultDigest
<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
*/
https://docs.spring.io/spring-framework/docs/1.2.x/reference/validation.html
PropertyEditors
= converting typesSometimes 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 theClassEditor
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
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
- 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 theCustomEditorConfigurer
class by using the Map-typedcustomEditors
property - each entry in the Map represents a single
PropertyEditor
, with the key of the entry being the name of the class for which thePropertyEditor
is used.
- custom
<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
ApplicationContext supports the following features:
- Internationalization
- Event publication
- Resource management and access
- Additional life-cycle interfaces
- Improved automatic configuration of infrastructure components
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>
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>
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("");
}
}
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;
}
}
<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();
}
}