Java学习笔记-Day64 Spring 框架(二)



一、控制反转IOC和依赖注入DI


控制反转和依赖注入是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。控制反转是说不需要程序员管理和控制bean,是解耦的目的,而依赖注入是手段。IOC是指让生成类的方式由传统方式反过来,即程序员不调用new运算符生成,需要类的时候则是由Spring等框架注入(DI)。

1、控制反转IOC


IOC(Inversion of Control)控制反转:控制反转不是什么技术,而是一种设计思想。IOC是一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试。有了IOC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。其实IOC对编程带来的最大改变不是从代码上,而是从思想上,发生了主从换位的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IOC思想中,应用程序就变成被动的,应用程序被动的等待IOC容器来创建并注入它所需要的资源了。

控制反转IOC是指控制权由应用程序转到外部容器,即控制权的转移。在Java开发中,IOC意味着将设计好的对象交给Spring容器控制,而不是传统的在程序内部直接控制。

类的一些属性 (例如UserService中的userDao成员属性)原来是由当前类(UserService)自己控制其实例化,现在则是由Spring利用接口来控制。可以使用xml或者注解来进行相关配置,spring会根据配置和约定,对对象进行实例化和属性装配。

2、依赖注入DI


DI(Dependency Injection)依赖注入:一些成员属性(例如UserService中的userDao成员属性) 依赖Spring容器来注入,组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。

依赖注入DI是spring 的核心机制,可以使Spring的bean以配置文件组织在一起,而不是硬编码方式耦合,耦合性相对来降低了。另外,注入是使用配置文件来实现,这样修改非常的方便。

3、Spring IOC容器

3.1、简介


Spring IOC 容器的代表就是org.springframework.beans包中的BeanFactory接口,BeanFactory接口提供了 IOC 容器最基本功能;而org.springframework.context包下的ApplicationContext接口扩展了BeanFactory,还提供了与Spring AOP集成、国际化处理、事件传播及提供不同层次的context实现 (如针对web应用的WebApplicationContext)。简单说, BeanFactory提供了IoC容器最基本功能,而 ApplicationContext 则增加了更多支持企业级功能支持。ApplicationContext完全继承BeanFactory,因而BeanFactory所具有的语义也适用于ApplicationContext。

3.2、实现容器


• XmlBeanFactory:BeanFactory实现,提供基本的IOC容器功能,可以从classpath或文件系统等获取资源;
(1)方式一:

	 File file = new File("apppicationContext.xml");
	 Resource resource = new FileSystemResource(file);
	 BeanFactory context = new XmlBeanFactory(resource);

(2)方式二:

	Resource resource = new ClassPathResource("apppicationContext.xml");		
	BeanFactory context = new XmlBeanFactory(resource);


• ClassPathXmlApplicationContext:ApplicationContext实现,从classpath获取配置文件;

	BeanFactory context = new ClassPathXmlApplicationContext("apppicationContext.xml");


• FileSystemXmlApplicationContext:ApplicationContext实现,从文件系统获取配置文件。

	BeanFactory context = new FileSystemXmlApplicationContext("classpath:apppicationContext.xml");

3.2、获取Bean对象


通过ApplicationContext接口的getBean方法从Spring容器中获取Bean对象。

① 通过 Bean对象的id或者name来获取:
Object getBean(String name) 根据名称返回一个Bean,客户端需要自己进行类型转换;参数name可以是bean的属性id的值或者是name的值。

	Blog blog = (Blog) context.getBean("blog1");


② 通过 Bean对象的类名来获取:
T getBean(Class<T> requiredType) 根据指定的类型返回一个Bean,客户端无需自己进行类型转换,但是如果全局配置文件中没有或多于一个Bean存在将会抛出异常。

	Blog blog = context.getBean(Blog.class);


③ 通过 Bean对象的id或者name和类名来获取:
T getBean(String name, Class<T> requiredType) 根据名称和指定的类型返回一个Bean,客户端无需自己进行类型转换,如果类型转换失败,容器抛出异常。返回的是指定名称同时指定类型的bean对象。

	Blog blog = context.getBean("blog1", Blog.class);

3.4、IOC容器的操作


① 配置文件的准备工作:在配置文件applicationContext.xml中声明Bean,也就是为Bean配置元数据。

<?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 class="com.etc.blog.entity.Blog" name="blog1" scope="prototype">
		<property name="id" value="1"></property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
	</bean>
</beans>

② IOC容器解析元数据: IoC容器的Bean Reader读取并解析配置文件,根据定义生成BeanDefinition配置元数据对象,IoC容器根据BeanDefinition进行实例化、配置及组装Bean。

③ 实例化IOC容器:由客户端实例化容器,获取需要的Bean。

package com.etc.blog.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.etc.blog.entity.Blog;

public class test {
    
    
	public static void main(String[] args) {
    
    
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		Blog blog1 = context.getBean("blog1", Blog.class);
		System.out.println("blog1="+blog1);
	}
}

二、Spring Bean


Spring IOC容器目的就是管理Bean,这些Bean将根据配置文件中的Bean定义进行创建,而Bean定义在容器内部由BeanDefinition对象表示,该定义主要包含以下信息:

  • 全限定类名(FQN):用于定义Bean的实现类。限定类名就是类名全称,带包路径的用点隔开,例如: java.lang.String。非限定类名也叫短名,就是平时说的类名,不带包的,例如:String。
  • Bean行为定义:这些定义了Bean在容器中的行为;包括作用域(单例、原型创建)、是否惰性初始化及生命周期等;
  • Bean创建方式定义:说明是通过构造器还是工厂方法创建Bean;
  • Bean之间关系定义:即对其他bean的引用,也就是依赖关系定义,这些引用bean也可以称之为同事bean或依赖bean,也就是依赖注入。
<?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 class="com.etc.blog.entity.Blog" id="blog1">
		<property name="id" value="1"></property>
		<property name="title" value="特里克"></property>
	</bean>
</beans>

Bean命名约定:Bean的命名遵循XML命名规范,但最好符合Java命名规范,由字母、数字、下划线组成,而且应该养成一个良好的命名习惯, 比如采用驼峰式,即第一个单词首字母开始,从第二个单词开始首字母大写开始,这样可以增加可读性。

1、<bean> 标签主要用来进行Bean定义。class属性指定的类要求写完整的包名和类名。

	<bean class="com.etc.blog.entity.Blog" id="blog1"></bean>

2、<alias> 标签用于定义Bean别名的。alias属性表示别名,name表示已经定义Bean的name。

	<bean class="com.etc.blog.entity.Blog" name="blog1"></bean>
	<alias alias="otherBlogName" name="blog1"></alias>

3、<property > 标签用于进行Setter注入,也就是给实体类中的某个属性赋值,会调用对应实体类的Setter方法。name属性代表属性的名字,value属性代表属性的值。

	<property name="title" value="特里克"></property>

4、<import> 标签用于导入其他配置文件的Bean定义,这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[] {“applicationContext1.xml“, “applicationContext2.xml”})传给ApplicationContext实现进行加载多个配置文件。这两种方式都是通过调用Bean Definition Reader 读取Bean定义,内部实现没有任何区别。<import>标签可以放在<beans>中的任何位置,没有顺序关系。

	<import resource="applicationContext2.xml"></import>

注意:标签中的属性id与name作用是一样,区别在于id中不可以含有特殊字符,而name中可以有特殊字符;id是确定该bean的唯一属性,而name可以指定一个或者多个名称,各个名称之间用逗号或分号分开。后面的为bean的别名。

Bean的加载:

默认情况下,我们使用

	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

Spring容器就会自动初始化所有的单例<bean scope=”singleton”>的bean。但是如果是<bean scope=”prototype”>则不会自动初始化,而是等使用的时候才实例化,也就是调用context.getBean方法的时候。

对单例的对象,可以手动设置其 <bean lazy-init=”true” >来设置其延迟初始化,前提是这个对象没有被其他的bean引用。

三、注入方法和注入类型

1、注入方法


(1)Setter注入
Setter注入就是在bean中使用setXX方法进行注入,因此需要在bean类中,成员属性需要setXX方法如下:

  • Bean类
public class Blog implements Serializable {
    
    
	
	private int id;
	private String title;
	private String content;
	/* private Author author; */
	
	public Blog(int id, String title, String content) {
    
    
		super();
		System.out.println("blog有参构造 setId..."+id);
		this.id = id;
		this.title = title;
		this.content = content;
	}
	public Blog() {
    
    
		super();
		System.out.println("blog无参构造 "+"id="+id+","+"title="+title);
	}
	
	public int getId() {
    
    
		return id;
	}
	public void setId(int id) {
    
    
		System.out.println("setId..."+id);
		this.id = id;
	}
	public String getTitle() {
    
    
		return title;
	}
	public void setTitle(String title) {
    
    
		this.title = title;
	}
	public String getContent() {
    
    
		return content;
	}
	public void setContent(String content) {
    
    
		this.content = content;
	}
	
	@Override
	public String toString() {
    
    
		return "Blog [id=" + id + ", title=" + title + ", content=" + content + "]";
	}	
	
}

  • Spring全局配置文件
	<bean class="com.etc.blog.entity.Blog" name="blog1">
		<!-- 属性赋值,在bean类中有setter方法 -->
		<property name="id" value="1"></property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
	</bean>


(2)构造方法注入
构造方法注入就是在bean中使用有参构造方法进行注入,在bean类中需要有成员属性作为参数的构造方法。

  • Bean类
public class Blog implements Serializable {
    
    
	
	private int id;
	private String title;
	private String content;
	/* private Author author; */
	
	public Blog(int id, String title, String content) {
    
    
		super();
		System.out.println("blog有参构造 setId..."+id);
		this.id = id;
		this.title = title;
		this.content = content;
	}
	public Blog() {
    
    
		super();
		System.out.println("blog无参构造 "+"id="+id+","+"title="+title);
	}
	
	public int getId() {
    
    
		return id;
	}
	public void setId(int id) {
    
    
		System.out.println("setId..."+id);
		this.id = id;
	}
	public String getTitle() {
    
    
		return title;
	}
	public void setTitle(String title) {
    
    
		this.title = title;
	}
	public String getContent() {
    
    
		return content;
	}
	public void setContent(String content) {
    
    
		this.content = content;
	}
	
	@Override
	public String toString() {
    
    
		return "Blog [id=" + id + ", title=" + title + ", content=" + content + "]";
	}	
	
}

  • Spring的全局配置文件
	<bean class="com.etc.blog.entity.Blog" id="blog1">
		<!-- 通过有参构造方法赋值 -->
		<constructor-arg name="id" value="1"></constructor-arg>
		<constructor-arg name="title" value="第一种方式"></constructor-arg>
		<constructor-arg name="content" value="第一种方式。。。"></constructor-arg>
	</bean>

	<bean class="com.etc.blog.entity.Blog" id="blog2">
		<!-- index为0代表第一个参数-->
		<constructor-arg index="0" value="2"></constructor-arg>
		<constructor-arg index="1" value="第二种方式"></constructor-arg>
		<constructor-arg index="2" value="第二种方式。。。"></constructor-arg>
	</bean> -->

2、注入类型


(1)简单属性的注入

在bean中存在简单属性时的注入方式。

<?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 class="com.etc.blog.entity.Blog" name="blog1">
		<property name="id">
			<value>123</value>
		</property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
	</bean>
</beans>


(2)其他数据类型的属性注入

Spring不仅能注入简单类型数据,还能注入自定义对象、集合(Collection、无序集合Set、有序集合List)类型数据、数组(Array)类型数据、字典(Map)类型数据、Properties类型数据。

  • List集合
    注入的属性类型为List时,在 property 标签中使用<list></list>表示集合,list标签的子标签有<value></value><ref bean="对象名"></ref>
	<property name="list">
		<list>
			<value>1</value>
			<value>aaa</value>
			<value>55.55</value>
		</list>
	</property>
	<property name="list">
		<list>
			<ref bean="blog1"></ref>
			<ref bean="blog2"></ref>
		</list>
	</property>

  • Map字典
    注入的属性类型为Map时,在 property 标签中使用<map></map>表示字典,map标签的子标签有<entry ></entry >,entry标签的属性有key(键)、value(值)、key-ref(类型为对象的键)、value-ref(类型为对象的值)。
	<property name="map">
		<map>
			<entry key="1" value="值1"></entry>
			<entry key="2" value="值2"></entry>
		</map>
	</property>
	<property name="map">
		<map>
			<entry key="1" value-ref="blog3"></entry>
			<entry key="2" value-ref="blog4"></entry>
		</map>
	</property>
	<property name="map">
		<map>
			<entry key-ref="blog1" value-ref="blog3"></entry>
			<entry key-ref="blog2" value-ref="blog4"></entry>
		</map>
	</property>

  • 自定义对象
    注入的属性类型为自定义对象时,需要使用 property 标签的ref属性引用对象,ref属性的值为对象的id或者name。
	<bean class="com.etc.blog.entity.Blog" name="blog1">
		<property name="id" value="1"></property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
		<property name="author" ref="author1"></property>
	</bean>
	
	<bean class="com.etc.blog.entity.Author" name="author1">
		<property name="id" value="1"></property>
		<property name="name" value="小明"></property>
	</bean>

四、Bean的作用域


作用域(scope),在面向对象程序设计中一般指对象或变量之间的可见范围。而在Spring容器中是指其创建的Bean对象相对于其他Bean对象的请求可见范围。

Spring提供 singleton 和 prototype 两种基本作用域,另外提供 request 、 session 、 global session 三种web作用域,Spring还允许用户定制自己的作用域。作用域利用<bean></bean>标签中的scope属性来指定。

scope属性的值:
(1)singleton(单例):默认作用域,每次取出的Bean对象都是同一个Bean对象。
(2)prototype(原型):懒加载,在使用getBean方法调用Bean对象时,才会创建Bean对象,每次获取的bean对象时,都会重新创建一个新的Bean对象。
(3)request:一次请求一个bean定义对应一个实例(web相关)
(4)session:一个session定义对应一个实例(web相关)
(5)globalsession类似于session作用域,只是其用于portlet环境的web应用。如果在非portlet环境将视为session作用域(web相关)

	<bean class="com.etc.blog.entity.Blog" name="blog1" scope="prototype">
		<property name="id">
			<value>123</value>
		</property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
	</bean>

五、自动装配autowire


自动装配就是指由Spring来自动地注入依赖对象。

Spring的装配支持 no 、 byName 、 byType 、 constructor 四种自动装配,默认是 no(不支持自动装配)。

自动装配的好处是减少构造器注入和setter注入配置,减少配置文件的长度。自动装配通过配置<bean></bean>标签的 autowire 属性来改变自动装配方式。

(1)default:表示使用默认的自动装配,默认的自动装配需要在<bean></bean>标签中使用default-autowire属性指定,其支持 no 、 byName 、 byType 、 constructor 四种自动装配。
(2)no:意思是不支持自动装配,必须明确指定依赖。
(3)byType:按照类型来进行装配,但是要求同一个类型的bean在xml中只能有一个.调用setter方法.
(4)byName:通过设置Bean定义属性autowire=“byName”,意思是根据名字进行自动装配,只能用于setter注入。

	<bean class="com.etc.blog.entity.Blog" name="blog1" autowire="byName">
		<property name="id">
			<value>123</value>
		</property>
		<property name="title" value="特里克"></property>
		<property name="content" value="特里克....."></property>
	</bean>

六、单元测试JUnit

1、JUnit4


可以使用Junit完成简单的单元测试,在一个方法上增加一个@Test 注解,要注意大小写,同时要将junit4.jar添加到构建路径中来。Run as Junt test 运行这个方法的时候,如果出现了绿色,表示单元测试成功,如果出现红色,则表示单元测试失败,程序代码有问题。

@Before:org.junit.Before
@Test:org.junit.Test
@After:org.junit.After


import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.etc.blog.entity.Blog;

public class TestJunit4 {
    
    
	BeanFactory context;
	
	@Before
	public void before() {
    
    
		System.out.println("before");
		context = new FileSystemXmlApplicationContext("classpath:applicationContext4.xml");
	}
	
	@Test
	public void test1() {
    
    
		System.out.println("test1");
		Blog blog1 = context.getBean("blog1",Blog.class);
		System.out.println("blog1="+blog1);
	}
	
	@Test
	public void test2() {
    
    
		System.out.println("test2");
		Blog blog2 = context.getBean("blog2",Blog.class);
		System.out.println("blog2="+blog2);
	}
	
	@After
	public void after() {
    
    
		System.out.println("after");
	}
}

2、JUnit5


可以使用Junit完成简单的单元测试,在一个方法上增加一个@Test 注解,要注意大小写,同时要将junit5.jar添加到构建路径中来。Run as Junt test 运行这个方法的时候,如果出现了绿色,表示单元测试成功,如果出现红色,则表示单元测试失败,程序代码有问题。

@BeforeEach:org.junit.jupiter.api.BeforeEach
@Test:org.junit.jupiter.api.Test
@AfterEach:org.junit.jupiter.api.AfterEach


import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.etc.blog.entity.Blog;

public class TestJunit5 {
    
    
	BeanFactory context;
	
	@BeforeEach
	public void before() {
    
    
		System.out.println("beforeEach");
		context = new FileSystemXmlApplicationContext("classpath:applicationContext4.xml");
	}
	
	@Test
	public void test1() {
    
    
		System.out.println("test1");
		Blog blog1 = context.getBean("blog1",Blog.class);
		System.out.println("blog1="+blog1);
	}
	
	@Test
	public void test2() {
    
    
		System.out.println("test2");
		Blog blog2 = context.getBean("blog2",Blog.class);
		System.out.println("blog2="+blog2);
	}
	
	@AfterEach
	public void after() {
    
    
		System.out.println("afterEach");
	}
}

3、JUnit4-Spring5


(1)添加spring-test.jar到构建路径。
(2)编写测试类,使用注解配合JUnit代码。

  • @RunWith(SpringJUnit4ClassRunner.class):让测试运行于Spring测试环境。
  • @RunWith(SpringRunner.class):让测试运行于Spring测试环境。
  • @ContextConfiguration(locations= {"/applicationContext.xml"}):加载Spring配置文件。
  • @Autowired:自动装配。
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

import com.etc.blog.entity.Blog;

//@RunWith(SpringJUnit4ClassRunner.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(locations= {
    
    "/applicationContext3.xml"})
public class TestJunit4Spring5 {
    
    
	@Autowired
	Blog blog;
	
	@Test
	public void test1() {
    
    
		System.out.println("blog="+blog);

	}
}

4、JUnit5-Spring5


(1)添加spring-test.jar到构建路径。
(2)编写测试类,使用注解配合JUnit代码。

  • @SpringJUnitConfig(locations="/applicationContext.xml"):@SpringJUnitConfig = @ContextConfiguration +@RunWith
  • @Qualifier(value=“blog1”):通过这个注解,表明了哪个实现类才是我们所需要的,添加@Qualifier注解,需要注意的是@Qualifier的参数名称为已经定义的Bean名称。
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.etc.blog.entity.Blog;

//@SpringJUnitConfig =  @ContextConfiguration +@RunWith
@SpringJUnitConfig(locations="/applicationContext4.xml")
public class TestJunit5Spring5 {
    
    
	@Autowired
	@Qualifier(value="blog1")
	Blog blog;
	
	@Test
	public void test1() {
    
    
		System.out.println("blog="+blog);
	}
}

七、Spring Bean的生命周期

1、生命周期


在这里插入图片描述

(1)Bean的装配。
Bean的装配是指将java对象转换为spring Bean的过程。
注意:Spring支持xml方式、java代码方式、自动装配等。

	<bean class="com.etc.blog.entity.Blog" name="blog1">
		<property name="id" value="1"></property>
		<property name="title" value="测试生命周期"></property>
		<property name="content" value="测试生命周期。。。。"></property>
	</bean>

(2)加载Spring的全局配置文件并实例化。
加载配置文件applicationContext.xml并实例化,在java代码中可以通过ClassPathXmlApplicationContext()方法来加载,当Bean的作用域为singleton时,加载配置文件的同时就会实例化对象,当Bean的作用域为prototype时,需要等待java代码中调用applicationContext的getBean()方法获取bean时,才会实例化对象。

	ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext5.xml");
	Blog blog1 = context.getBean("blog1", Blog.class);

(3)Bean属性注入
Bean属性注入,即为Bean对象的属性赋值,底层采用反射方式注入bean,通过实体类的Setter方法实现。

	public void setId(int id) {
    
    
		System.out.println("setId..."+id);
		this.id = id;
	}
	public void setTitle(String title) {
    
    
		this.title = title;
	}
	public void setContent(String content) {
    
    
		this.content = content;
	}

(4) 实体类实现BeanNameAware接口,并实现接口中的setBeanName方法。获取bean的id或name。

	@Override
	public void setBeanName(String arg0) {
    
    
		System.out.println("3、Blog setBeanName方法 获取bean id/name :"+arg0);
	}

(5)实体类实现BeanFactoryAware接口,并实现接口中的setBeanFactory方法。获取bean工厂。

	@Override
	public void setBeanFactory(BeanFactory arg0) throws BeansException {
    
    
		System.out.println("4、Blog setBeanFactory方法 获取bean工厂 :"+arg0);
	}

(6)实体类实现ApplicationContextAware接口,并实现接口中的setApplicationContext方法。获取应用上下文。

	@Override
	public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    
    
		System.out.println("5、Blog setApplicationContext方法 获取应用上下文 :"+arg0);
	}

(7)实现后置处理器,创建一个实现BeanPostProcessor接口的自定义类,并实现接口中的postProcessBeforeInitialization和postProcessAfterInitialization方法,该过程是通过AOP方式实现的。要在Spring配置文件中编写该自定义类的Bean。此处会调用postProcessBeforeInitialization方法。

public class TestBeanPostProcessor implements BeanPostProcessor {
    
    
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
		System.out.println("调用postProcessBeforeInitialization方法");
		return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
	}
	
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
		System.out.println("调用postProcessAfterInitialization方法");
		return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
	}
}
	<bean class="com.etc.blog.entity.TestBeanPostProcessor" id="processor1"></bean>

(8)实体类实现InitializingBean接口,并实现接口中的afterPropertiesSet方法,获取初始化方法。

	@Override
	public void afterPropertiesSet() throws Exception {
    
    
		System.out.println("6、Blog afterPropertiesSet方法 获取初始化方法");
	}

(9)调用自定义初始化方法init-method。
① 在配置文件的对应的bean标签中添加属性 init-method=“init_method”。

	<bean class="com.etc.blog.entity.Blog" id="blog1" init-method="init_method">
		<property name="id" value="1"></property>
		<property name="title" value="测试数据标题"></property>
	</bean>

② 在实体类中创建init_method方法。

	public void init_method() {
    
    
		System.out.println("7、Blog init_method方法");
	}

(10)调用Bean后置处理器的postProcessAfterInitialization方法。

(11)此处需要使用AbstractApplicationContext.registerShutDownHook() 关闭容器,如果不关闭,我们无法看到destory销毁的两个方法,这里不算生命周期的一部分。

	context.registerShutdownHook();

(12)实体类实现DisposableBean接口,并实现接口中的destroy方法。

	@Override
	public void destroy() throws Exception {
    
    
		System.out.println("8、Blog destroy方法");
	}

(13) 调用自定义销毁方法destroy-method方法。
① 在配置文件的对应的bean标签中添加属性 destroy-method=“destroy_method”。

	<bean class="com.etc.blog.entity.Blog" id="blog1" init-method="init_method" destroy-method="destroy_method">
		<property name="id" value="1"></property>
		<property name="title" value="测试数据标题"></property>
	</bean>

② 在实体类中创建destroy_method方法。

	public void destroy_method() {
    
    
		System.out.println("9、Blog destroy_method方法");
	}

2、代码案例

  • applicationContext.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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean class="com.etc.blog.entity.Blog" id="blog1" init-method="init_method" destroy-method="destroy_method">
		<property name="id" value="1"></property>
		<property name="title" value="测试数据标题"></property>
	</bean>
	
	<bean class="com.etc.blog.entity.TestBeanPostProcessor" id="processor1"></bean>
</beans>

  • Blog.java
package com.etc.blog.entity;

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

public class Blog implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean {
    
    
	private int id;
	private String title;
	
	public Blog(int id, String title) {
    
    
		super();
		this.id = id;
		this.title = title;
	}
	
	public Blog() {
    
    
		super();
		System.out.println("1、Blog 无参构造方法(实例化)");
	}

	public int getId() {
    
    
		return id;
	}

	public void setId(int id) {
    
    
		System.out.println("2、Blog setId方法(属性注入)");
		this.id = id;
	}

	public String getTitle() {
    
    
		return title;
	}

	public void setTitle(String title) {
    
    
		this.title = title;
	}

	@Override
	public String toString() {
    
    
		return "Blog [id=" + id + ", title=" + title + "]";
	}

	@Override
	public void setBeanName(String arg0) {
    
    
		System.out.println("3、Blog setBeanName方法 获取bean id/name :"+arg0);
	}

	@Override
	public void setBeanFactory(BeanFactory arg0) throws BeansException {
    
    
		System.out.println("4、Blog setBeanFactory方法 获取bean工厂 :"+arg0);
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    
    
		System.out.println("5、Blog setApplicationContext方法 获取应用上下文 :"+arg0);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
    
    
		System.out.println("6、Blog afterPropertiesSet方法 获取初始化方法");
	}
	
	public void init_method() {
    
    
		System.out.println("7、Blog init_method方法");
	}

	@Override
	public void destroy() throws Exception {
    
    
		System.out.println("8、Blog destroy方法");
	}
	
	public void destroy_method() {
    
    
		System.out.println("9、Blog destroy_method方法");
	}
}

  • TestLifeCycle.java
package com.etc.blog.test;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.etc.blog.entity.Blog;

public class TestLifeCycle {
    
    
	public static void main(String[] args) {
    
    
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext4.xml"); 
		Blog blog1 = context.getBean("blog1",Blog.class);
		context.registerShutdownHook();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_42141141/article/details/113043533