Spring3.X学习笔记1-IoC容器概述
2017-10-28 20:25
232 查看
2017即将接近尾声,每当年末的时候总要检查下今年还有什么事情没做,结果却发现今年好像也没做多少事情。于是就想着把之前一直想梳理的Spring知识,趁着年末好好整理下。本系列笔记基于”Spring3.x企业应用开发实战“一书,说来惭愧,书买了好多年了也没认真的看一遍,于是就有了这个想法,算是2017年的最后给自己的一份交代。
在开始之前,先简要介绍下Spring吧!
Spring是分层的Java SE/EE应用一站式的轻量级开源框架,由Rod Johnson创建,以IoC和AOP为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多企业级应用技术,并以海纳百川的胸怀整合了开源世界里众多的企业级应用技术,逐渐成为使用最多的Java EE企业应用开发框架。
控制反转:对于软件来说,即某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。
依赖注入:调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。
上面两个概念第一次接触的时候,你会发现都不太好理解,那是因为还不清楚IoC的实现机制,随着对IoC了解的深入,你会发现
构造函数注入: 在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入。
属性注入: 属性注入是指通过Setter方法完成调用类所需依赖的注入。
接口注入: 将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。
由于通过接口注入需要额外声明一个接口,增加了类的数目,而且它的效果和属性注入并无本质区别,所以不提倡采用这种方式。
通过查看运行结果,可以看到这和直接通过构造函数和方法调用类功能的效果是一致的,只不过前者是间接调用,后者是直接调用罢了。这说明我们完全可以通过编程方式调用Class的各项功能。如果我们将这些信息以一个配置文件的方式提供,就可以使用Java语言的反射功能编写一段通用代码对类似于Car的类进行实例化及功能调用操作了。有没有感觉这和上面提到的Spring框架的实现机制很相似,Spring正是基于Java语言自带的反射机制实现了IoC的功能。
下面简要介绍下上述例子用到得三个主要反射类,这些反射对象类在
Constructor: 类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。Constructor的一个主要方法是newInstance(Object[]… initargs),通过该方法可以创建一个对象类的实例,相当
Method: 类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法发射类对象数组Method[]。Method最主要的方法是invoke(Object obj, Objcet… args),obj表示操作的目标对象,args为方法入参。
Field: 类的成员变量反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组。Filed类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标类对象的成员变量设置值。
此外,Java还为包提供了Package反射类,在JDK5.0中还未注解提供了AnnotatedElement反射类。总之,Java的反射体系保证了可以通过程序化的方式访问目标类中的所有元素,对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用
注意: 通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一次调用时。对于单实例的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean()获取Bean时将直接从IoC容器的缓存中获取Bean实例。Spring在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例Bean的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。
ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径中加载配置文件,后者默认从文件系统中装载配置文件。
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。需要注意的是:ApplicationContext在初始化应用上下文时就实例化所有单实例的Bean。因此,相比BeanFactory,初始化时间也会相对较长些,不过之后的调用就不在有“第一次惩罚”的问题。
WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须在拥有Web容器的前提下才能完成启动的工作。有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet(spring3.0及以后版本中已经删除)或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个都可以完成启动Spring Web应用上下文的工作。
通过web容器监听器启动:
1. 当调用者通过getBean(beanName)向容器请求某一个Bean时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法;
2. 根据配置情况调用Bean构造函数或工厂方法实例化Bean;
3. 如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessAfterInstantiationn()方法,可以在这里对已经实例化的对象进行处理。
4. 如果Bean配置了属性信息,容器在这一步着手将配置值设置到Bean对应的属性中,不过在设置每个属性值之前先调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法;
5. 调用Bean的属性设置方法设置属性值;
6. 如果Bean实现了org.springframework.beans.factory.BeanNameAware接口,将调用setBeanName()接口方法,将配置文件中该Bean对应的名称设置到Bean中;
7. 如果Bean实现了org.springframework.bean.factory.BeanFactoryAware接口,将调用setBeanFactory()接口方法,将BeanFactory容器实例设置到Bean中;
8. 如果BeanFactory装配了org.springframework.beans.factory.config.BeanPostProcessor后处理器,将调用BeanPostProcessor的
9. 如果Bean实现了InitializingBean的接口,将调用该接口的afterPropertiesSet()方法;
10. 如果在通过init-method属性定义了初始化方法,将执行这个方法;
11. BeanPostProcessor后处理定义了两个方法:其一时postProcessBeforeInstantiation()在第8步调用;其二是Object postProcessAfterInstantiationn(Object bean, String beanName)方法,这个方法在此时调用,容器中再次获得对Bean进行加工处理的机会。
12. 如果在中指定Bean的作用范围是scope=”prototype”,将Bean返回给调用者,调用者负责Bean后续生命的管理,Spring不再管理这个Bean的生命周期。如果作用范围设置为scope=“singleton”,则将Bean放入到Spring IoC容器的缓存池中,并将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命管理。
13. 对于scope=“singleton”的Bean,当容器关闭时,将触发Spring对Bean的后续生命周期的管理工作,首先如果Bean实现了DisposableBean接口,则将调用接口的afterPropertiesSet()方法,可以在此编写释放资源、记录日志等操作。
14. 对于scope=“singleton”的Bean,如果通过的destroy-method属性指定了Bean的销毁方法,Spring将执行Bean的这个方法,完成Bean资源的释放等操作。
Bean生命周期实例:
运行上述代码,我们在控制台上得到以下输出信息,仔细观察,将发现它验证了我们前面所介绍的Bean生命周期过程。
此外,如果配置文件中声明了工作后处理器接口BeanFactoryPostProcessor的实现类,则应用上下文在装载配置文件之后初始化Bean实例之前将调用这些BeanFactoryPostProcessor对配置信息进行加工处理。工厂后处理器是容器级的,仅在应用上下文初始化时调用一次,其目的是完成一些配置文件的加工处理工作。
ApplicationContext和BeanFactory另一最大的不同之处在于:前者会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中拿你给通过手工调用addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用ApplicationContext而很少使用BeanFactory的原因之一。
————本文结束感谢您的阅读————
在开始之前,先简要介绍下Spring吧!
Spring是分层的Java SE/EE应用一站式的轻量级开源框架,由Rod Johnson创建,以IoC和AOP为内核,提供了展现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多企业级应用技术,并以海纳百川的胸怀整合了开源世界里众多的企业级应用技术,逐渐成为使用最多的Java EE企业应用开发框架。
1、IoC概述
1.1 IoC的概念
IoC(控制反转:Inverse of Control)是一个重要的面向对象编程理论,Spring核心模块实现了IoC的功能。Spring中的其他模块,像AOP、声明式事务等功能都是建立在IoC的基础之上,它将类和类之间的依赖从代码中脱离出来,用配置的方式进行依赖关系描述,由IoC容器负责依赖类之间的创建、拼接、管理、获取等工作。一般来说IoC的概念有两种表示方式,一个叫控制反转,一个叫依赖注入。由于控制反转并不好理解,业界也曾进行广泛的讨论,最终软件界的泰斗级人物Martin Folwer提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC。控制反转:对于软件来说,即某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定。
依赖注入:调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。
上面两个概念第一次接触的时候,你会发现都不太好理解,那是因为还不清楚IoC的实现机制,随着对IoC了解的深入,你会发现
依赖注入的概念直接明了。
1.2 IoC的类型
从注入方法上看,主要可以划分为三种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。构造函数注入: 在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入。
属性注入: 属性注入是指通过Setter方法完成调用类所需依赖的注入。
接口注入: 将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。
由于通过接口注入需要额外声明一个接口,增加了类的数目,而且它的效果和属性注入并无本质区别,所以不提倡采用这种方式。
2、IoC的底层实现原理
Spring的核心模块实现了IoC的功能,它通过配置文件或注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入的工作。让开发者们从底层实现类的实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作。这种“神奇”的力量归功于Java语言本身的类反射功能。2.1 Java反射知识
Java语言允许通过程序化的方式间接对Class的对象实例操作,Class文件由类装载器装载后,在JVM中将形成一份描述CLass结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。下面先看一个例子:public class Car { private String brand; private String color; private int maxSpeed; public Car() {} public Car(String brand, String color, int maxSpeed) { this.brand = brand; this.color = color; this.maxSpeed = maxSpeed; } public void introduce() { System.out.println("brand:" + brand + ";color:" + color + ";maxSpeed:" + maxSpeed); } // 省略参数的getter/Setter方法 } public class ReflectTest { public static Car initByDefaultConst() throws Throwable { // 通过类装载器获取Car类对象 ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class<?> clazz = loader.loadClass("com.hhxs.bbt.web.Car"); // 获取类的默认构造器对象并通过它实例化Car Constructor<?> cons = clazz.getDeclaredConstructor((Class[]) null); Car car = (Car) cons.newInstance(); // 通过反射方法设置属性 Method setBrand = clazz.getMethod("setBrand", String.class); setBrand.invoke(car, "红旗CA72"); Method setColor = clazz.getMethod("setColor", String.class); setColor.invoke(car, "黑色"); Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class); setMaxSpeed.invoke(car, 200); return car; } public static void main(String[] args) throws Throwable { Car car1 = initByDefaultConst(); car1.introduce(); } }
通过查看运行结果,可以看到这和直接通过构造函数和方法调用类功能的效果是一致的,只不过前者是间接调用,后者是直接调用罢了。这说明我们完全可以通过编程方式调用Class的各项功能。如果我们将这些信息以一个配置文件的方式提供,就可以使用Java语言的反射功能编写一段通用代码对类似于Car的类进行实例化及功能调用操作了。有没有感觉这和上面提到的Spring框架的实现机制很相似,Spring正是基于Java语言自带的反射机制实现了IoC的功能。
下面简要介绍下上述例子用到得三个主要反射类,这些反射对象类在
java.reflect包中定义:
Constructor: 类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。Constructor的一个主要方法是newInstance(Object[]… initargs),通过该方法可以创建一个对象类的实例,相当
new关键字。
Method: 类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法发射类对象数组Method[]。Method最主要的方法是invoke(Object obj, Objcet… args),obj表示操作的目标对象,args为方法入参。
Field: 类的成员变量反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组。Filed类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标类对象的成员变量设置值。
此外,Java还为包提供了Package反射类,在JDK5.0中还未注解提供了AnnotatedElement反射类。总之,Java的反射体系保证了可以通过程序化的方式访问目标类中的所有元素,对于private或protected的成员变量和方法,只要JVM的安全机制允许,也可以通过反射进行调用
setAccessible(boolean access)。
3、三个核心接口
Spring通过一个配置文件描述Bean及Bean之间的依赖关系,利用Java语言的反射功能实例化Bean并建立Bean之间的依赖关系。Spring的IoC容器在完成这些底层工作的基础上,还提供了Bean实例缓存、声明周期管理、Bean实例代理、事件发布、资源装载等高级服务。3.1 BeanFactory
Bean工厂(com.springframework.beans.factory.BeanFactory)是Spring框架最核心的接口,它提供了高级IoC的配置机制。BeanFactory使管理不同不同类型的Java对象成为可能,一般称BeanFactory为IoC容器。BeanFactory是类的通用工厂,它可以创建并管理各种类的对象,Spring称这些被创建和管理的Java对象为Bean。Bean最主要的方法就是getBean(String beanName),该方法从容器中返回特定名称的Bean。下面看一个小例子:
// spring文件 <?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-3.0.xsd"> <bean id="car" class="com.hhxs.bbt.web.Car" p:brand="红旗CA72" p:color="黑色" p:maxSpeed="200" /> </beans> // JAVA代码 public class BeanFactoryTest { public static void main(String[] args) throws Throwable{ ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource res = resolver.getResource("classpath:spring/beans.xml"); System.out.println(res.getURL()); BeanFactory bf = new XmlBeanFactory(res); System.out.println("init BeanFactory."); Car car = bf.getBean("car",Car.class); System.out.println("car bean is ready for use!"); car.introduce(); } }
注意: 通过BeanFactory启动IoC容器时,并不会初始化配置文件中定义的Bean,初始化动作发生在第一次调用时。对于单实例的Bean来说,BeanFactory会缓存Bean实例,所以第二次使用getBean()获取Bean时将直接从IoC容器的缓存中获取Bean实例。Spring在DefaultSingletonBeanRegistry类中提供了一个用于缓存单实例Bean的缓存器,它是一个用HashMap实现的缓存器,单实例的Bean以beanName为键保存在这个HashMap中。
3.2 ApplicationContext
应用上下文(com.springframework.context.ApplicationContext)建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。一般称ApplicationContext为应用上下文或者Spring容器。ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径中加载配置文件,后者默认从文件系统中装载配置文件。
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/beans.xml"); //或者 ApplicationContext ctx = new FileSystemXmlApplicationContext("spring/beans.xml");
在获取ApplicationContext实例后,就可以像BeanFactory一样调用getBean(beanName)返回Bean了。需要注意的是:ApplicationContext在初始化应用上下文时就实例化所有单实例的Bean。因此,相比BeanFactory,初始化时间也会相对较长些,不过之后的调用就不在有“第一次惩罚”的问题。
3.3 WebApplicationContext
WebApplicationContext是专门为Web应用准备的,它允许应用从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring应用上下文。WebApplicationContext的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要ServletContext实例,也就是说它必须在拥有Web容器的前提下才能完成启动的工作。有过Web开发经验的读者都知道可以在web.xml中配置自启动的Servlet(spring3.0及以后版本中已经删除)或定义Web容器监听器(ServletContextListener),借助这两者中的任何一个都可以完成启动Spring Web应用上下文的工作。
通过web容器监听器启动:
web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:/spring/spring-context.xml </param-value> </context-param> <!-- spring容器启动监听器 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
4、Bean的生命周期
4.1 BeanFactory中Bean的生命周期
我们知道Web容器中的Servlet拥有明确的生命周期,Spring容器中的Bean也拥有相似的生命周期。我们可以从两个层面定义Bean的生命周期:第一个层面是Bean的作用范围;第二个层面是实例化Bean时所经历的一系列阶段。1. 当调用者通过getBean(beanName)向容器请求某一个Bean时,如果容器注册了org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor接口,在实例化Bean之前,将调用接口的postProcessBeforeInstantiation()方法;
2. 根据配置情况调用Bean构造函数或工厂方法实例化Bean;
3. 如果容器注册了InstantiationAwareBeanPostProcessor接口,在实例化Bean之后,调用该接口的postProcessAfterInstantiationn()方法,可以在这里对已经实例化的对象进行处理。
4. 如果Bean配置了属性信息,容器在这一步着手将配置值设置到Bean对应的属性中,不过在设置每个属性值之前先调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues()方法;
5. 调用Bean的属性设置方法设置属性值;
6. 如果Bean实现了org.springframework.beans.factory.BeanNameAware接口,将调用setBeanName()接口方法,将配置文件中该Bean对应的名称设置到Bean中;
7. 如果Bean实现了org.springframework.bean.factory.BeanFactoryAware接口,将调用setBeanFactory()接口方法,将BeanFactory容器实例设置到Bean中;
8. 如果BeanFactory装配了org.springframework.beans.factory.config.BeanPostProcessor后处理器,将调用BeanPostProcessor的
Object postProcessBeforeInstantiation(Object bean, Stringn beanName)接口方法对Bean进行加工操作。其中入参bean是当前正在处理的Bean,而beanName是当前Bean的配置名,返回的对象为加工处理后的Bean。BeanPostProcessor在Spring框架中占有重要的地位,为容器提供duiBean进行后续架构处理的切入点,Spring容器所提供的各种“神奇功能”(如AOP,动态代理等)都通过BeanPostProcessor实施;
9. 如果Bean实现了InitializingBean的接口,将调用该接口的afterPropertiesSet()方法;
10. 如果在通过init-method属性定义了初始化方法,将执行这个方法;
11. BeanPostProcessor后处理定义了两个方法:其一时postProcessBeforeInstantiation()在第8步调用;其二是Object postProcessAfterInstantiationn(Object bean, String beanName)方法,这个方法在此时调用,容器中再次获得对Bean进行加工处理的机会。
12. 如果在中指定Bean的作用范围是scope=”prototype”,将Bean返回给调用者,调用者负责Bean后续生命的管理,Spring不再管理这个Bean的生命周期。如果作用范围设置为scope=“singleton”,则将Bean放入到Spring IoC容器的缓存池中,并将Bean引用返回给调用者,Spring继续对这些Bean进行后续的生命管理。
13. 对于scope=“singleton”的Bean,当容器关闭时,将触发Spring对Bean的后续生命周期的管理工作,首先如果Bean实现了DisposableBean接口,则将调用接口的afterPropertiesSet()方法,可以在此编写释放资源、记录日志等操作。
14. 对于scope=“singleton”的Bean,如果通过的destroy-method属性指定了Bean的销毁方法,Spring将执行Bean的这个方法,完成Bean资源的释放等操作。
Bean生命周期实例:
public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean { private String brand; private String color; private int maxSpeed; private BeanFactory beanFactory; private String beanName; public Car() { System.out.println("调用Car()构造函数。"); } public Car(String brand, String color, int maxSpeed) { this.brand = brand; this.color = color; this.maxSpeed = maxSpeed; } public void introduce() { System.out.println("brand:" + brand + ";color:" + color + ";maxSpeed:" + maxSpeed); } public String getBrand() { return brand; } public void setBrand(String brand) { System.out.println("调用setBrand()设置属性。"); this.brand = brand; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } // 5 DisposableBean方法 @Override public void destroy() throws Exception { System.out.println("调用DisposaleBean.destroy()。"); } // 4 IntializingBean接口方法 @Override public void afterPropertiesSet() throws Exception { System.out.println("调用IntializingBean.afterPropertiesSet()。"); } // 3 BeanNameAware接口方法 @Override public void setBeanName(String name) { System.out.println("调用BeanNameAware.setBeanName()。"); this.beanName = beanName; } // 2 BeanFactoryAware接口方法 @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("调用BeanFactoryAware.setBeanFactory()。"); this.beanFactory = beanFactory; } // 6 通过<bean>的init-method属性指定的初始化方法 public void myInit() { System.out.println("调用init-method所指定的myInit(),将maxSpeed设置为240。"); this.maxSpeed = 240; } // 7 通过<bean>的destroy-method属性指定的销毁方法 public void myDestroy() { System.out.println("调用destroy-method所指定的myDestroy()。"); } } public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter { // 1 在实例化Bean前进行调用 public Object postProcessBeforeInitialization(Class beanClass, String beanName) throws BeansException { // 1-1 仅对容器中Car Bean进行处理 if ("car".equals(beanName)) { System.out.println("InstantiationAware BeanPostProcessor.postProcessBeforeInstantiation"); } return null; } // 2 在实例化Bean后调用 public boolean postProcessAfterInstantiation(Object bean, String beanName) { // 2-1 仅对容器中Car Bean进行处理 if ("car".equals(beanName)) { System.out.println("InstantiationAware BeanPostProcessor.postProcessAfterInstantiation"); } return true; } // 3 在设置某个属性时调用 public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { // 3-1 仅对容器中Car Bean进行处理,还可以通过pdst入参进行过滤,仅对car的某个特定属性时进行处理 if("car".equals(beanName)) { System.out.println("InstantiationAware AwareBeanPostProcessor.postProcessPropertyValues"); } return pvs; } } public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("car")) { Car car = (Car)bean; if(car.getColor() == null) { System.out.println("调用BeanPostProcessor.postProcessBeforeInitialization(),color为空,设置为默认黑色。"); car.setColor("黑色"); } } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("car")) { Car car = (Car)bean; if(car.getMaxSpeed() >= 200) { System.out.println("调用BeanPostProcessor.postProcess AfterInitialization(), 将maxSpeed调整为200。"); car.setMaxSpeed(200); } } return bean; } } **beans.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-3.0.xsd"> <bean id="car" class="com.hhxs.bbt.web.Car" init-method="myInit" destroy-method="myDestroy" p:brand="红旗CA72" p:maxSpeed="200" scope="singleton" /> </beans> public class BeanLifeCycle { private static void lifeCycleInBeanFactory() { Resource res = new ClassPathResource("spring/beans.xml"); BeanFactory bf = new XmlBeanFactory(res); // 向容器中注册后处理器 ((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyBeanPostProcessor()); ((ConfigurableBeanFactory)bf).addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor()); // 第一次从容器中获取Car,将触发容器实例化该Bean,这将引发Bean生命周期方法的调用。 Car car1 = (Car)bf.getBean("car"); car1.introduce(); car1.setColor("红色"); car1.introduce(); // 第二次从容器中获取Car,直接从缓存池中获取 Car car2 = (Car)bf.getBean("car"); // 查看car1和car2是否指向同一引用 System.out.println("car1==car2:" + (car1==car2)); // 关闭容器 ((XmlBeanFactory)bf).destroySingletons(); } public static void main(String[] args) { lifeCycleInBeanFactory(); } }
运行上述代码,我们在控制台上得到以下输出信息,仔细观察,将发现它验证了我们前面所介绍的Bean生命周期过程。
调用Car()构造函数。 InstantiationAware BeanPostProcessor.postProcessAfterInstantiation InstantiationAware AwareBeanPostProcessor.postProcessPropertyValues 调用setBrand()设置属性。 调用BeanNameAware.setBeanName()。 调用BeanFactoryAware.setBeanFactory()。 调用BeanPostProcessor.postProcessBeforeInitialization(),color为空,设置为默认黑色。 调用IntializingBean.afterPropertiesSet()。 调用init-method所指定的myInit(),将maxSpeed设置为240。 调用BeanPostProcessor.postProcess AfterInitialization(), 将maxSpeed调整为200。 brand:红旗CA72;color:黑色;maxSpeed:200 brand:红旗CA72;color:红色;maxSpeed:200 car1==car2:true 调用DisposaleBean.destroy()。 调用destroy-method所指定的myDestroy()。
4.2 ApplicationContext中Bean的生命周期
Bean在应用上下文中的生命周期和BeanFactory中生命周期类似,不同的是,如果Bean实现org.springframework.context.ApplicationContextAware接口,会增加一个调用该接口方法setApplicationContext()的步骤。此外,如果配置文件中声明了工作后处理器接口BeanFactoryPostProcessor的实现类,则应用上下文在装载配置文件之后初始化Bean实例之前将调用这些BeanFactoryPostProcessor对配置信息进行加工处理。工厂后处理器是容器级的,仅在应用上下文初始化时调用一次,其目的是完成一些配置文件的加工处理工作。
ApplicationContext和BeanFactory另一最大的不同之处在于:前者会利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,并自动将它们注册到应用上下文中;而后者需要在代码中拿你给通过手工调用addBeanPostProcessor()方法进行注册。这也是为什么在应用开发时,我们普遍使用ApplicationContext而很少使用BeanFactory的原因之一。
————本文结束感谢您的阅读————
相关文章推荐
- Spring3.x企业开发实战之Ioc容器概述
- Spring学习3:IOC容器概述
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.5 Bean的生命周期
- spring学习总结(二):IOC & DI 概述及 IOC 容器
- Spring3.0读书笔记----(三)IoC容器概述
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.2 相关Java基础知识 类装载器 反射机制
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.5 Bean的生命周期
- IoC容器概述
- [Spring3.x] 第 3 章 IOC 容器概述
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.2 相关Java基础知识 类装载器 反射机制
- [Spring3.x] 第 3 章 IOC 容器概述
- IOC容器概述
- Spring IOC之容器概述
- Spring的IoC容器概述
- 深入解读Spring Framework IoC容器(第一弹:IoC容器和Bean概述)
- 笔记-Srping3.x-IOC容器概述
- spring3.x第三章 IOC容器概述
- 学习《spring 3.x企业应用开发实战》之IOC容器概述
- spring ioc容器的学习笔记1----ioc容器概述
- [Spring]IoC容器之进击的注解