Spring 中 bean 的生命周期详解
一、bean 的生命周期图解
二、bean 创建时使用 Spring 的资源
- 实现 aware 类型接口的 bean,可以在 bean 实例化的时候获取到一些相对应的资源,如实现 BeanNameAware 的 bean,就可以获取到 beanName。
- Spring 自带的 aware 类型的接口有很多,下面只列举了常用的三个(ApplicationContextAware 是用的最多的一个)
1、BeanNameAware
- 使用 BeanNameAware 接口来获取 beanName
示例
package com.luo.spring.guides.beanlifecycle.aware.name;
import org.springframework.beans.factory.BeanNameAware;
/**
* @author : archer
* @date : Created in 2022/12/6 15:37
* @description :
*/
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()");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="namedSinger" class="com.luo.spring.guides.beanlifecycle.aware.name.NamedSinger"/>
</beans>
测试
package com.luo.spring.guides.beanlifecycle.aware.name;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/6 15:38
* @description :
*/
public class Main {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("beanlifecycle/aware/name/app-context-name.xml");
ctx.refresh();
NamedSinger bean = (NamedSinger) ctx.getBean("namedSinger");
bean.sing();
ctx.close();
}
}
输出
Singer [namedSinger] - sing()
2、BeanClassLoaderAware
package com.luo.spring.guides.beanlifecycle.aware.classloader;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.stereotype.Component;
/**
* @author : archer
* @date : Created in 2022/12/6 15:51
* @description :
*/
@Component
public class SingerClassLoaderBean implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void print(){
System.out.println(classLoader);
}
}
测试
package com.luo.spring.guides.beanlifecycle.aware.classloader;
import com.luo.spring.guides.iocdi.annotation.field.Singer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author : archer
* @date : Created in 2022/12/6 15:52
* @description :
*/
public class Main {
@Configuration
@ComponentScan(basePackages = {"com.luo.spring.guides.beanlifecycle.aware.classloader"})
public static class ClassloaderConfiguration{}
public static void main(String... args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext
(ClassloaderConfiguration.class);
SingerClassLoaderBean singerClassLoaderBean = ctx.getBean("singerClassLoaderBean", SingerClassLoaderBean.class);
singerClassLoaderBean.print();
}
}
输出
jdk.internal.loader.ClassLoaders$AppClassLoader@4e25154f
3、ApplicationContextAware
package com.luo.spring.guides.beanlifecycle.aware.applicationcontext;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Created by iuliana.cosmina on 2/22/17.
*/
@Component
public class Singer implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void sing() {
System.out.println(applicationContext.getId());
}
}
测试
package com.luo.spring.guides.beanlifecycle.aware.applicationcontext;
import com.luo.spring.guides.beanlifecycle.aware.classloader.SingerClassLoaderBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author : archer
* @date : Created in 2022/12/8 14:20
* @description :
*/
public class Main {
@Configuration
@ComponentScan(basePackages = {"com.luo.spring.guides.beanlifecycle.aware.applicationcontext"})
public static class MConfiguration {
}
public static void main(String... args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MConfiguration.class);
Singer singer = (Singer) ctx.getBean("singer");
singer.sing();
}
}
输出
org.springframework.context.annotation.AnnotationConfigApplicationContext@45820e51
三、bean 创建时调用特定方法
1、@PostConstruct
package com.luo.spring.guides.beanlifecycle.init.postconstruct;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import javax.annotation.PostConstruct;
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
private 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;
}
}
<?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.luo.spring.guides.beanlifecycle.init.postconstruct.SingerWithJSR250"
p:name="John Mayer" p:age="39"/>
<bean id="singerTwo"
class="com.luo.spring.guides.beanlifecycle.init.postconstruct.SingerWithJSR250"
p:age="72"/>
<bean id="singerThree"
class="com.luo.spring.guides.beanlifecycle.init.postconstruct.SingerWithJSR250"
p:name="John Butler"/>
</beans>
测试
package com.luo.spring.guides.beanlifecycle.init.postconstruct;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 15:26
* @description :
*/
public class Main {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:beanlifecycle/init/app-context-postconstruct.xml");
ctx.refresh();
getBean("singerOne", ctx);
getBean("singerTwo", ctx);
getBean("singerThree", ctx);
ctx.close();
}
private 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;
}
}
}
输出
Initializing bean
Name: John Mayer
Age: 39
Initializing bean
Using default name
Name: Eric Clapton
Age: 72
Initializing bean
An error occured in bean configuration: Error creating bean with name ‘singerThree’: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: You must set the age property of any beans of type class com.luo.spring.guides.beanlifecycle.init.postconstruct.SingerWithJSR250
2、InitializingBean
package com.luo.spring.guides.beanlifecycle.init.initialzingbean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
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;
}
}
<?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">
<bean id="singerOne"
class="com.luo.spring.guides.beanlifecycle.init.initialzingbean.SingerWithInterface"
p:name="John Mayer" p:age="39"/>
<bean id="singerTwo"
class="com.luo.spring.guides.beanlifecycle.init.initialzingbean.SingerWithInterface"
p:age="72"/>
<bean id="singerThree"
class="com.luo.spring.guides.beanlifecycle.init.initialzingbean.SingerWithInterface"
p:name="John Butler"/>
</beans>
测试
package com.luo.spring.guides.beanlifecycle.init.initialzingbean;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 15:33
* @description :
*/
public class Main {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("beanlifecycle/init/app-context-initializingbean.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 occurred in bean configuration: "
+ ex.getMessage());
return null;
}
}
}
输出
Initializing bean
Name: John Mayer
Age: 39
Initializing bean
Using default name
Name: Eric Clapton
Age: 72
Initializing bean
An error occurred in bean configuration: Error creating bean with name ‘singerThree’ defined in class path resource [beanlifecycle/init/app-context-initializingbean.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: You must set the age property of any beans of type class com.luo.spring.guides.beanlifecycle.init.initialzingbean.SingerWithInterface
3、init-method
package com.luo.spring.guides.beanlifecycle.init.initmethod.domain;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
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;
}
private 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;
}
}
1)、xml 形式
<?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-init-method="init">
<bean id="singerOne"
class="com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer"
p:name="John Mayer" p:age="39"/>
<bean id="singerTwo"
class="com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer"
p:age="72"/>
<bean id="singerThree"
class="com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer"
p:name="John Butler"/>
</beans>
测试
package com.luo.spring.guides.beanlifecycle.init.initmethod.xml;
import com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 15:41
* @description :
*/
public class Main {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:beanlifecycle/init/app-context-initmethod.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 occurred in bean configuration: "
+ ex.getMessage());
return null;
}
}
}
输出
Initializing bean
Name: John Mayer
Age: 39
Initializing bean
Using default name
Name: Eric Clapton
Age: 72
Initializing bean
An error occurred in bean configuration: Error creating bean with name ‘singerThree’ defined in class path resource [beanlifecycle/init/app-context-initmethod.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: You must set the age property of any beans of type class com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer
2)、注解形式
测试
package com.luo.spring.guides.beanlifecycle.init.initmethod.annotation;
import com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ApplicationContext;
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;
/**
* @author : archer
* @date : Created in 2022/12/8 15:42
* @description :
*/
public class Main {
@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();
}
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 occurred in bean configuration: "
+ ex.getMessage());
return null;
}
}
}
输出
Initializing bean
Name: John Mayer
Age: 39
Initializing bean
Using default name
Name: Eric Clapton
Age: 72
Initializing bean
An error occurred in bean configuration: Error creating bean with name ‘singerThree’ defined in com.luo.spring.guides.beanlifecycle.init.initmethod.annotation.Main$SingerConfig: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: You must set the age property of any beans of type class com.luo.spring.guides.beanlifecycle.init.initmethod.domain.Singer
四、bean 销毁时调用特定方法
1、@PreDestroy
package com.luo.spring.guides.beanlifecycle.destory.predestory.domain;
import org.springframework.context.support.GenericXmlApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
public class DestructiveBeanWithJSR250 {
private File file;
private String filePath;
@PostConstruct
public void init() 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;
}
}
1)、xml 形式
<?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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="destructiveBean"
class="com.luo.spring.guides.beanlifecycle.destory.predestory.domain.DestructiveBeanWithJSR250"
p:filePath=
"#{systemProperties['java.io.tmpdir']}#{systemProperties['file.separator']}test.txt"/>
</beans>
测试
package com.luo.spring.guides.beanlifecycle.destory.predestory.xml;
import com.luo.spring.guides.beanlifecycle.destory.predestory.domain.DestructiveBeanWithJSR250;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 16:08
* @description :
*/
public class Main {
public static void main(String... args) throws Exception {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:beanlifecycle/destroy/app-context-predestory.xml");
ctx.refresh();
DestructiveBeanWithJSR250 bean =
(DestructiveBeanWithJSR250) ctx.getBean("destructiveBean");
System.out.println("Calling destroy()");
ctx.close();
System.out.println("Called destroy()");
}
}
输出
Initializing Bean
File exists: true
Destroying Bean
File exists: false
2)、注解形式
测试
package com.luo.spring.guides.beanlifecycle.destory.predestory.annotation;
import com.luo.spring.guides.beanlifecycle.destory.predestory.domain.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 Main {
@Configuration
static class DestructiveBeanConfig {
@Lazy // 表示 getBean 时再创建 bean
@Bean
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);
ctx.registerShutdownHook();
}
}
输出
Initializing Bean
File exists: true
Destroying Bean
File exists: false
2、DisposableBean
package com.luo.spring.guides.beanlifecycle.destory.disposable;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.io.File;
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;
}
}
测试
package com.luo.spring.guides.beanlifecycle.destory.disposable;
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;
/**
* @author : archer
* @date : Created in 2022/12/8 16:49
* @description :
*/
public class Main {
@Configuration
static class DestructiveBeanConfig {
@Lazy // 表示 getBean 时再创建 bean
@Bean
DestructiveBeanWithInterface destructiveBean() {
DestructiveBeanWithInterface destructiveBean = new DestructiveBeanWithInterface();
destructiveBean.setFilePath(System.getProperty("java.io.tmpdir") +
System.getProperty("file.separator") + "test.txt");
return destructiveBean;
}
}
public static void main(String... args) throws Exception {
GenericApplicationContext ctx =
new AnnotationConfigApplicationContext(DestructiveBeanConfig.class);
ctx.getBean(DestructiveBeanWithInterface.class);
ctx.close();
}
}
输出
Initializing Bean
File exists: true
Destroying Bean
File exists: false
3、destroy-method
package com.luo.spring.guides.beanlifecycle.destory.destroymethod;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.io.File;
public class DestructiveBean {
private File file;
private String filePath;
public void init() 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;
}
}
测试
package com.luo.spring.guides.beanlifecycle.destory.destroymethod;
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;
/**
* @author : archer
* @date : Created in 2022/12/8 16:55
* @description :
*/
public class Main {
@Configuration
static class DestructiveBeanConfig {
@Lazy // 表示 getBean 时再创建 bean
@Bean(initMethod = "init", destroyMethod = "destroy")
DestructiveBean destructiveBean() {
DestructiveBean destructiveBean = new DestructiveBean();
destructiveBean.setFilePath(System.getProperty("java.io.tmpdir") +
System.getProperty("file.separator") + "test.txt");
return destructiveBean;
}
}
public static void main(String... args) throws Exception {
GenericApplicationContext ctx =
new AnnotationConfigApplicationContext(DestructiveBeanConfig.class);
ctx.getBean(DestructiveBean.class);
ctx.registerShutdownHook();
}
}
输出
Initializing Bean
File exists: true
Destroying Bean
File exists: false
五、使用关闭钩子
在 Spring 中销毁回调函数唯一缺点是,它们不会自动触发,需要应用程序关闭之前调用 AbstractApplicationContext.destroy()。那么怎么让它随着应用程序的关闭自动调用呢?
一个可行的方案是创建一个关闭钩子(Shutdown Hook),它是应用程序关闭之前执行的一个线程,这是调用 AbstractApplicationContext.destroy() 的一种理想的方式。
- ctx.registerShutdownHook() 会与 ctx.close() 产生相同的结果
- ctx.registerShutdownHook() 方法自动指示 Spring 注册底层 JVM 运行时的关闭钩子。
代码实现如下
package com.luo.spring.guides.beanlifecycle.shutdownhook;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.File;
public class DestructiveBeanWithHook implements ApplicationContextAware {
private File file;
private String filePath;
private ApplicationContext applicationContext;
@PostConstruct
public void init() throws Exception {
System.out.println("Initializing Bean");
if (filePath == null) {
throw new IllegalArgumentException(
"You must specify the filePath property of " +
DestructiveBeanWithHook.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;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
if (applicationContext instanceof AbstractApplicationContext) {
((AbstractApplicationContext) applicationContext).registerShutdownHook();
}
}
}
测试
package com.luo.spring.guides.beanlifecycle.shutdownhook;
import com.luo.spring.guides.beanlifecycle.destory.destroymethod.DestructiveBean;
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 org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 17:11
* @description :
*/
public class Main {
@Configuration
static class DestructiveBeanConfig {
@Lazy // 表示 getBean 时再创建 bean
@Bean
DestructiveBeanWithHook destructiveBean() {
DestructiveBeanWithHook destructiveBean = new DestructiveBeanWithHook();
destructiveBean.setFilePath(System.getProperty("java.io.tmpdir") +
System.getProperty("file.separator") + "test.txt");
return destructiveBean;
}
}
public static void main(String... args) throws Exception {
GenericApplicationContext ctx =
new AnnotationConfigApplicationContext(DestructiveBeanConfig.class);
ctx.getBean(DestructiveBeanWithHook.class);
}
}
输出
Initializing Bean
File exists: true//若不注册 registerShutdownHook(),则没有下面两行,即 destroy() 方法不执行
Destroying Bean
File exists: false
六、使用 FactoryBean
package com.luo.spring.guides.factorybean;
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 MessageDigest getDigest1() {
return digest1;
}
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);
}
}
package com.luo.spring.guides.factorybean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import java.security.MessageDigest;
public class MessageDigestFactoryBean implements
FactoryBean<MessageDigest>, InitializingBean {
private String algorithmName = "MD5";
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;
}
}
测试
package com.luo.spring.guides.factorybean;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.GenericApplicationContext;
import java.security.MessageDigest;
/**
* Created by iuliana.cosmina on 3/7/17.
*/
public class Main {
@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) throws Exception {
GenericApplicationContext ctx = new AnnotationConfigApplicationContext(MessageDigesterConfig.class);
MessageDigester digester = (MessageDigester) ctx.getBean("digester");
digester.digest("Hello World!");
System.out.println("==========我是分割线=============");
MessageDigest shaDigest1 = ctx.getBean("shaDigest", MessageDigest.class);
System.out.println(shaDigest1.digest("Hello World".getBytes()).toString());
MessageDigestFactoryBean factoryBean =
(MessageDigestFactoryBean) ctx.getBean("&shaDigest");
MessageDigest shaDigest2 = factoryBean.getObject();
System.out.println(shaDigest2.digest("Hello World".getBytes()).toString());
System.out.println("shaDigest1==shaDigest2:" + (shaDigest1 == shaDigest2));
System.out.println("digester.getDigest1()==shaDigest2:" + (digester.getDigest1() == shaDigest2));
ctx.close();
}
}
输出
Using digest1
Using alogrithm: SHA1
[B@782859e
Using digest2
Using alogrithm: MD5
[B@34bde49d
我是分割线===
[B@1b1cfb87
[B@821330f
shaDigest1==shaDigest2:true
digester.getDigest1()==shaDigest2:true
七、factory-bean && factory-method
package com.luo.spring.guides.factorybean.xml;
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;
}
}
package com.luo.spring.guides.factorybean;
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 MessageDigest getDigest1() {
return digest1;
}
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);
}
}
<?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="shaDigestFactory"
class="com.luo.spring.guides.factorybean.xml.MessageDigestFactory"
p:algorithmName="SHA1"/>
<bean id="defaultDigestFactory"
class="com.luo.spring.guides.factorybean.xml.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.luo.spring.guides.factorybean.MessageDigester"
p:digest1-ref="shaDigest"
p:digest2-ref="defaultDigest"/>
</beans>
测试
package com.luo.spring.guides.factorybean.xml;
import com.luo.spring.guides.factorybean.MessageDigester;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.security.MessageDigest;
public class Main {
public static void main(String... args) throws Exception {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:factorybean/app-context-xml.xml");
ctx.refresh();
MessageDigester digester = (MessageDigester) ctx.getBean("digester");
digester.digest("Hello World!");
System.out.println("==========我是分割线=============");
MessageDigest shaDigest1 = ctx.getBean("shaDigest", MessageDigest.class);
System.out.println(shaDigest1.digest("Hello World".getBytes()).toString());
System.out.println("digester.getDigest1()==shaDigest2:" + (digester.getDigest1() == shaDigest1));
ctx.close();
}
}
输出
Using digest1
Using alogrithm: SHA1
[B@16a0ee18
Using digest2
Using alogrithm: MD5
[B@505fc5a4
我是分割线===
[B@5fbdfdcf
digester.getDigest1()==shaDigest2:true
八、PropertyEditor
1、使用内置的 PropertyEditor
- 默认情况下,Spring 不会注册 CustomDateEditor 和 StringTrimmerEditor
- 使用 Spring 内置的 PropertyEditor,可以将各种属性的 String 转换为正确的类型
package com.luo.spring.guides.propertyeditor.domain;
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;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
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");
//默认情况下,Spring 不会注册 CustomDateEditor 和 StringTrimmerEditor
registry.registerCustomEditor(Date.class,
new CustomDateEditor(dateFormatter, true));
registry.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
}
}
<?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.luo.spring.guides.propertyeditor.domain.PropertyEditorBean$CustomPropertyEditorRegistrar"/>
</util:list>
<bean id="builtInSample" class="com.luo.spring.guides.propertyeditor.domain.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>
测试
package com.luo.spring.guides.propertyeditor.xml;
import com.luo.spring.guides.propertyeditor.domain.PropertyEditorBean;
import org.springframework.context.support.GenericXmlApplicationContext;
import java.io.File;
/**
* @author : archer
* @date : Created in 2022/12/8 19:02
* @description :
*/
public class Main {
public static void main(String... args) throws Exception {
File file = File.createTempFile("test", "txt");
file.deleteOnExit();
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:propertyeditor/app-context-propertyeditor.xml");
ctx.refresh();
PropertyEditorBean bean =
(PropertyEditorBean) ctx.getBean("builtInSample");
ctx.close();
}
}
输出
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 CST 13
Setting file: test.txt
Setting float value: 123.45678
Setting locale: 英语 (美国)
Setting pattern: a*b
Loaded 1 properties
Setting stream: java.io.BufferedInputStream@4b14918a
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/
2、自定义 PropertyEditor
package com.luo.spring.guides.propertyeditor.domain;
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;
}
}
package com.luo.spring.guides.propertyeditor.domain;
import java.beans.PropertyEditorSupport;
public class NamePropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
//匹配任意空白字符(空格,制表符等)
String[] name = text.split("\\s");
setValue(new FullName(name[0], name[1]));
}
}
<?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 name="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="com.luo.spring.guides.propertyeditor.domain.FullName"
value="com.luo.spring.guides.propertyeditor.domain.NamePropertyEditor"/>
</map>
</property>
</bean>
<bean id="exampleBean" class="com.luo.spring.guides.propertyeditor.domain.CustomEditorExample"
p:name="John Mayer"/>
</beans>
测试
package com.luo.spring.guides.propertyeditor.domain;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/12/8 19:03
* @description :
*/
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:propertyeditor/app-context-02.xml");
ctx.refresh();
CustomEditorExample bean =
(CustomEditorExample) ctx.getBean("exampleBean");
System.out.println(bean.getName());
ctx.close();
}
}
输出
First name: John - Last name: Mayer