当前位置: 首页 > news >正文

Spring:Bean(创建方式,抽象继承,工厂Bean,生命周期)

1,Bean的创建

1.1,调用构造器创建Bean

调用Bean类的无参构造函数来创造对象,因此要求提供无参构造函数。在这种情况下class元素是必须的,值就是Bean对象的实现类。

  • 如果采用设值注入,Spring容器将使用默认的构造器来创建Bean实例,Spring对Bean实例的所有属性进行默认初始化,即所有基本数据类型的值初始化为0或false;所有引用类型的值初始化为null。接下来BeanFactory会根据配置文件决定依赖关系,先实例化被依赖的Bean实例,然后为Bean注入依赖关系,最后将一个完整的Bean实例返回给程序。
  • 如果采用构造注入,则要求配置文件为<bean.../>元素添加<constructor-arg.../>子元素,每个<constructor-arg.../>子元素配置一个构造器参数。Spring容器将使用带对应的构造器来创建Bean实例,Spring调用构造器传入的参数即可用于初始化Bean的实例变量,最后也将一个完整的Bean实例返回给程序。

1.2,使用静态工厂方法创建Bean

【问题】如何在Spring中不再使用Spring创建Bean实例,而是把Bean创建过程转移到开发者手中?

  • 使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。
  • 另外,还需要使用factory-method来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),来返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例则完全一样。需要注意的是,当使用静态工厂方法来创建Bean时,这个factory-method必须要是静态的。

<bean.../>元素需要指定如下两个属性:

  • class:该属性的值为静态工厂类的类名。
  • factory-method:该属性指定静态工厂方法来生产Bean实例。
public interface Being {public void testBeing();
}
------------------------------
public class Cat implements Being {private String msg;public void setMsg(String msg) {this.msg = msg;}public void testBeing() {System.out.println(msg + ",猫爱吃鱼");}
}
-------------------------------
public class Dog implements Being {private String msg;public void setMsg(String msg) {this.msg = msg;}public void testBeing() {System.out.println(msg + ",狗爱吃骨头。");}
}
package Bean;public class BeingFactory {public static Being getBeing(String arg){if (arg.equals("dog")){return new Dog();}else{return new Cat();}}
}

静态工厂类,该类的getBeing()方法是一个静态工厂方法,该方法根据传入的参数决定返回Cat对象,还是Dog对象。

<?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="dog" class="Bean.BeingFactory" factory-method="getBeing"><constructor-arg value="dog"/><property name="msg" value="我是dog"/></bean><bean id="cat" class="Bean.BeingFactory" factory-method="getBeing"><constructor-arg value="cat"/><property name="msg" value="我是cat"/></bean>
</beans>

cat和dog两个Bean配置的class属性和factory-method属性完全相同——这是因为这两个实例都是由同一个静态工厂类、同一个静态工厂方法生产得到。配置这两个Bean实例时指定的静态工厂方法的参数值不同,配置工厂方法的参数值使用<contructor-arg.../>元素。

一旦<bean.../>元素指定了factory-method属性,Spring就不再调用构造器来创建Bean实例,而是调用工厂方法来创建Bean实例。如果同时指定了class和factory-method两个属性,Spring就会调用静态工厂方法来创建Bean。

使用静态工厂方法创建实例时必须提供工厂类和产生实例的静态工厂方法。通过静态工厂方法创建实例时需要对Spring配置文件做如下改变:

  • class属性不在是Bean实例的实现类,而是生成Bean实例的静态工厂类。
  • 使用factory-method指定生产Bean实例的静态工厂方法。
  • 如果静态工厂方法需要参数,使用<constructor-arg />元素为其配置。

当我们指定Spring使用静态工厂方法来创建Bean实例时,Spring将先解析配置文件,并根据配置文件指定的信息,通过反射调用静态工厂类的静态工厂方法,并将该静态工厂方法的返回值作为Bean实例。在这个过程中,Spring不再负责创建Bean实例,Bean实例是由用户提供的静态工厂方法提供的。

当静态工厂方法创建了Bean实例后,Spring依然可以管理该Bean实例的依赖关系,包括为其注入所需要的依赖Bean、管理其生命周期等。

1.3,调用示例工厂方法创建Bean

实例工厂方法与静态工厂方法只有一点不同:调用静态工厂方法只需要使用工厂类即可,调用实例工厂方法则必须使用工厂实例。所以在Spring配置上也只有一点区别:配置静态工厂方法使用class指定静态工厂类,配置实例工厂方法则使用factory-bean指定工厂实例。

实例工厂和静态工厂的区别:

  • 配置实例工厂方法创建Bean,必须将实例工厂配置成Bean实例;而配置静态工厂方法创建Bean,则无须配置工厂Bean。
  • 配置实例工厂方法创建Bean,必须使用factory-bean属性确定工厂Bean;而配置静态工厂方法创建Bean,则使用class元素确定静态工厂类。

实例工厂和静态工厂的相同:

  • 都需要使用factory-method属性指定产生Bean实例的工程方法。
  • 工厂方法如果需要参数,都使用<constructor-arg.../>元素指定参数值。
  • 普通的设值注入,都使用<property.../>元素确定参数值。

使用实例工厂方法时,配置Bean实例的<bean.../>元素无须class属性,因为Spring容器不再直接实例化该Bean,Spring容器仅仅调用实例工厂的工厂方法,工厂方法负责创建Bean实例。

采用实例工厂方法创建Bean的<bean…/>元素时需要指定如下属性:

  • factory-bean:工厂Bean的id。
  • factory-method:实例工厂的工厂方法。
public interface Person {public String sayHello(String name);public String sayGoodBye(String name);
}
----------------------------------
public class American implements Person{public String sayHello(String name) {return name+",Hellow";}public String sayGoodBye(String name) {return name+",Good Bye";}
}
----------------------------------
public class Chinese implements Person {public String sayHello(String name) {return name + ",您好";}public String sayGoodBye(String name) {return name + ",下次再见";}
}
public class PersonFactory {public Person getPerson(String ethnic){if (ethnic.equalsIgnoreCase("chin")){return new Chinese();}else{return new American();}}
}

PersonFactory是负责产生Person对象的实例工厂,该工厂类里提供了一个getPerson()方法,该方法根据传入的ethnic参数决定产生哪种Person对象。

<?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="personFactory" class="Bean.PersonFactory"/><bean id="chinese" factory-bean="personFactory" factory-method="getPerson"><constructor-arg value="chin"/></bean><bean id="american" factory-bean="personFactory" factory-method="getPerson"><constructor-arg value="ame"/></bean>
</beans>

2,深入理解容器中的Bean

2.1,抽象&继承

在实际开发中,可能出现:随着项目越来越大,Spring配置文件中出现了多个<bean.../>配置具有大致相同的配置信息,只有少量信息不同,这将导致配置文件出现很多重复的内容。这样可能导致:配置文件臃肿、后期难以修改维护。

【抽象Bean】为了解决上面的问题,可以考虑把多个<bean.../>配置中相同的信息提取出来,集中成配置模板——这个配置模板并不是这种的Bean,因此Spring不应该创建该配置模板,于是需要为该<bean.../>配置增加abstruct="true"。

抽象Bean不能被实例化,Spring容器不会创建抽象Bean实例(不能通过getBean()显示地获得抽象Bean实例)。抽象Bean的价值在于被继承,抽象Bean通常作为父Bean被继承。抽象Bean只是配置信息面板,指定abstruct=“阻止”Spring实例化该Bean,因此抽象Bean可以不指定class属性。继承仅仅是指配置上的继承,并不意味着这两个bean之间存在继承关系。继承bean配置仅仅是为了复用其配置信息。

将大部分相同的信息配置成抽象Bean之后,将实际的Bean实例配置成该抽象Bean的子Bean即可。子Bean定义可以从父Bean继承实现类、构造参数、属性值等配置信息,除此之外,子Bean配置可以增加新的配置信息,并可指定新的配置信息覆盖父Bean的定义子 Bean 也可以覆盖从父 Bean 继承过来的配置)。并不是 <bean> 元素里的所有属性都会被继承. 比如: autowire、abstract、singleton、scope、lazy-init、depends-on 等,这些属性总是从子Bean定义中获得,或采用默认值。

<bean id="personTemplate" abstract="true"><property name="name" value="ysy"/><property name="axe" value="steelAxe"/>
</bean>
<bean id="chinese" class="Bean.Chinese" parent="personTemplate"/>

如果父Bean(抽象Bean)指定了class属性:那么子Bean连class属性都可以省略,子Bean将采用与父Bean相同的实现类。除此之外,子Bean也可覆盖父Bean的配置信息:当子Bean拥有和父Bean相同配置时,子Bean的配置信息取胜。

Bean继承于Java继承的区别:

  • Spring中的子Bean与父Bean可以是不同类型,但Java中的继承则保证子类是一种特殊的父类
  • Spring中的继承是参数值的延续;Java中的继承是类之间的方法和属性的延续
  • Spring中的子Bean不可作为父Bean使用,不具备多态性;Java中的子类对象完全可作为父类对象使用

2.2,工厂Bean(FactoryBean)

一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

此处的工厂Bean,与前面介绍的实例工厂方法创建Bean,或者静态工厂方法创建Bean的工厂有所区别:前面那些工厂是标准的工厂模式,Spring只是负责调用工厂方法来创建Bean实例;此处的工厂Bean是Spring一种特殊Bean,这种工厂必须实现FactoryBean接口。

FactoryBean接口是工厂Bean的标准接口,把工厂Bean(实现FactoryBean接口的Bean)部署在容器中之后,如果程序通过getBean()方法来获取它时,容器返回的不是FactoryBean实现类的实例,而是返回FactoryBean的产品(即该工厂Bean的getObject()方法的返回值,getObject()代理了getBean()方法)。

FactoryBean接口提供的相关方法:

  • T getObject()返回由FactoryBean创建的Bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中;
  • Boolean isSingleton()返回由FactoryBean创建的Bean实例的作用域是singleton还是prototype;
  • Class<T>getObjectType()返回FactoryBean创建的Bean类型。

实现FactoryBean接口的最大作用在于:Spring容器并不是简单地返回该Bean的实例,而是返回该Bean实例的getObject()方法的返回值,而getObject()方法则由开发者负责实现。

import org.springframework.beans.factory.FactoryBean;
import org.springframework.expression.spel.ast.Projection;
import java.lang.reflect.Field;public class GetFieldFactoryBean implements FactoryBean<Object> {private String targetClass;private String targetField;public void setTargetClass(String targetClass) {this.targetClass = targetClass;}public void setTargetField(String targetField) {this.targetField = targetField;}public Object getObject() throws Exception {Class<?> clazz = Class.forName(targetClass);Field field = clazz.getField(targetField);return field.get(null);}public Class<?> getObjectType() {return Projection.class;}public boolean isSingleton() {return false;    //不需要单例模式}
}

GetFieldFactoryBean是一个标准的工厂Bean,该工厂Bean的关键代码在于所实现的getObject()方法,该方法的执行体使用反射先获取targetClass对应的Class对象,再获取targetField对应的类变量的值。GetFieldFactoryBean的targetClass、targetField都提供了setter方法,因此可以接受Spring的设值注入,这样即可让GetFieldFactoryBean获取指定类的、指定静态Field的值。

<?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="theValue" class="Bean.GetFieldFactoryBean"><property name="targetClass" value="java.sql.ResultSet"/><property name="targetField" value="TYPE_SCROLL_SENSITIVE"/></bean>
</beans>

从上面的程序可以看出,部署工厂Bean与部署普通Bean其实没有任何区别,同样只需为该Bean配置id、class两个属性即可,但Spring对FactoryBean接口的实现类的处理有所不同。

Spring会自动检测容器中的所有Bean,如果发现某个Bean实现类实现了FactoryBean接口,Spring容器就会在实例化该Bean、根据<property.../>执行setter方法之后,额外调用该Bean的getObject()方法,并将该方法的返回值作为容器中的Bean。

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context.getBean("theValue"));
System.out.println(context.getBean("&theValue"));
========================================================
1005
Bean.GetFieldFactoryBean@185d8b6

FactoryBean是一个接口,当在IOC容器中的Bean实现了FactoryBean后,通过getBean(String BeanName)获取到的Bean对象并不是FactoryBean的实现类对象,而是这个实现类中的getObject()方法返回的对象。要想获取FactoryBean的实现类,就要getBean(&BeanName),在BeanName之前加上&。

2.3,强制初始化Bean

在大多数情况下,Bean之间的依赖非常直接,Spring容器在返回Bean实例之前,先要完成Bean依赖关系的注入。假如Bean A依赖于Bean B,程序请求Bean A时,Spring容器会自动初始化Bean B,再将Bean B注入Bean A,最后将具备完整依赖的Bean A返回给程序。

在极端的情况下,Bean之间的依赖不够直接。比如,某个类的初始化块中使用其他Bean,Spring总是先初始化主调Bean,当执行初始化块时,被依赖Bean可能还没实例化,此时将引发异常。

为了显式指定被依赖Bean在目标Bean之前初始化,可以使用depends-on属性,该属性可以在初始化主调Bean之前,强制初始化一个或多个Bean。

<bean id="person" class="Bean.Person" depends-on="manager"></bean>

3,Bean的生命周期管理

Spring可以管理singleton作用域的Bean的生命周期,Spring可以精确地知道该Bean何时被创建何时被初始化完成、容器何时准备销毁该Bean实例。

  • 对于prototype作用域的Bean:Spring容器仅仅负责创建,当容器创建了Bean实例之后,Bean实例完全交给客户端代码管理,容器不再跟踪其生命周期。每次客户端请求prototype作用域的Bean时,Spring都会产生一个新的实例,Spring容器无法知道它曾经创建了多少个prototype作用域的Bean,也无从知道这些propertype作用域的Bean什么时候才会被销毁。因此,Spring无法管理propertype作用域的Bean。
  • 对于singleton作用域的Bean:每次客户端代码请求时都返回同一个共享实例,客户端代码不能控制Bean的销毁Spring容器负责跟踪Bean实例的产生、销毁。Spring容器可以在创建Bean之后,进行某些通用资源申请;还可以在销毁Bean实例之前,先回收某些资源,比如数据库连接。Spring知道Bean何时结束、何时销毁,Spring可以管理实例化结束之后和销毁之前的行为。

3.1,Bean的生命周期

生命周期代码描述
实例化

ApplicationContext

scope = singleton

实例化一个 Bean,也就是我们常说的 new。
设置属性setXXX按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。
执行setBeanName方法

(可选)bean类继承BeanNameAware接口

(可选)bean类继承了BeanFactoryAware接口

如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的setBeanName(String) 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值
执行ApplicationContext方法(可选)bean类继承了ApplicationContextAware接口如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用 setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也 可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接 口,有更多的实现方法)
执行postProcessBeforeInitialization方法(可选)bean类继承了BeanPostProcessor接口如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用 作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应 用于内存或缓存技术。
执行afterPropertiesSet方法(可选)bean类继承了InitializingBean接口
执行自定义bean初始化方法(可选)bean中<bean init-method="init"/>如果 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法。
执行postProcessAfterInitialization方法(可选)bean类继承了BeanPostProcessor接口如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessAfterInitialization(Object obj, String s)方法。 注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一 般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中 也可以配置非 Singleton。
使用我们的bean
执行destory(可选)bean类实现了DisposableBean接口当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调 用那个其实现的 destroy()方法;
调用指定的销毁方法<bean destory-method="fun1"/>最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的 销毁方法。

3.2,依赖关系注入之后的行为

spring就是一个大的bean容器,管理各种不同的bean及之间的关系,在初始化bean的时候,spring也预留一些接口类,方便spring加载管理bean之前或之后额外处理bean的其它业务。

Spring IOC 容器对 Bean 的生命周期进行管理的过程:

  • 通过构造器或工厂方法创建 Bean 实例
  • 为 Bean 的属性设置值和对其他 Bean 的引用
  • 注入依赖关系之后,调用 Bean 的初始化方法
  • Bean 可以使用了
  • 当容器关闭时,调用 Bean 的销毁方法

Spring提供两种方式在Bean全部属性设置成功后执行特定行为:

  • 实现InitializingBean接口:也可达到同样的效果,就是让Bean类实现InitializingBean接口,该接口提供一个方法void afterPorpertiesSet() throws Exception;Spring会在为该Bean注入依赖关系之后,调用该Bean所实现的afterPropertiesSet()方法。
import org.springframework.beans.factory.InitializingBean;public class Person implements InitializingBean {public void afterPropertiesSet() throws Exception {System.out.println("正在执行初始化方法!");}
}
  • 使用init-method属性:使用init-method属性指定某个方法应在Bean全部依赖关系设置结束后自动执行。使用这种方式不需要将代码与Spring的接口耦合再一起,代码污染小。
public class Person {public void init() throws Exception {System.out.println("正在执行初始化方法!");}
}
<?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="person" class="Bean.Person" init-method="init"></bean>
</beans>

对于实现InitializingBean接口的Bean,无须使用init-method属性来指定初始化方法,配置该Bean实例与配置普通Bean实例完全一样,Spring容器会自动监测Bean实例是否实现了特定生命周期接口,并由此决定是否需要执行生命周期的方法。

如果即采用init-method属性指定初始化方法,又实现InitializingBean接口来指定初始化方法,Spring容器会执行两个初始化方法:先执行InitializingBean接口中定义的方法,然后执行init-method属性指定的方法。

3.3,当容器关闭时的行为

  • 实现DisposableBean接口:让Bean实现DisposableBean接口,该接口提供一个方法:void destroy() throws Exception,该方法就是Bean实例销毁之前应该执行的方法。
import org.springframework.beans.factory.DisposableBean;
public class Person implements DisposableBean {public void destroy() throws Exception {System.out.println("已被摧毁!");}
}

配置该Bean与配置普通Bean没有任何区别,Spring可以自动检测容器中的DisposableBean,在销毁Bean实例之前,Spring会自动调用该Bean实例的destroy()方法。 

【问题】singleton作用域的Bean通常会随着容器的关闭而销毁,但是:ApplicationContext容器在什么时候关闭呢?在基于Web的ApplicationContext实现中,系统已经提供了响应的代码保证关闭Web应用时恰当的关闭Spring容器。如果是非Web应用的环境下,为了让Spring容器优雅地关闭,并调用singletonBean上的响应析构回调方法,则需要在JVM里注册一个关闭钩子(shutdown hook),这样就可保证Spring容器被恰当关闭,且自动执行singleton Bean实例的析构回调函数。

为了注册关闭钩子,只需要调用在AbstractApplicationContext中提供的registerShutdownHook()方法即可。

AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(context.getBean("person"));
context.registerShutdownHook();
  • 使用destroy-method属性:指定某个方法在Bean销毁之前被自动执行。使用这种方式,不需要将代码与Spring的接口耦合在一起,代码污染小。

除此之外,如果容器中的很多Bean都需要指定特定的生命周期行为,则可以利用<bean.../>元素的default-init-method属性和default-destroy-method属性,这两个属性的作用类似于<bean.../>元素的init-method和destroy-method属性的作用,区别是default-init-method属性和default-destroy-method属性是属于<bean.../>元素的,它们将使容器中的所有Bean生效。

3.4,协调作用域不同步的Bean

当两个singleton作用域的Bean存在依赖关系时,或者当prototype作用域的Bean依赖singleton作用域的Bean时,使用Spring提供的依赖注入进行管理即可。

singleton作用域的Bean只有一次初始化的机会,它的依赖关系也只在初始化阶段被设置,当singleton作用域的Bean依赖prototype作用域的Bean时,Spring容器会在初始化singleton作用域的Bean之前,先创建被依赖的prototype Bean,然后才初始化singleton Bean,并将prototype Bean注入singleton Bean,这会导致以后无论何时通过singleton Bean去访问prototype Bean时,得到的永远是最初那个protype Bean——这样就相当于singleton Bean把它所依赖的prototype Bean变成了singleton行为。

由于singleton Bean具有单例行为,当客户端多次请求singleton Bean时,Spring返回给客户端的将是同一个singleton Bean实例,这不存在任何问题。问题是:如果客户端通过该singleton Bean去调用prototype Bean的方法时——始终都是调用同一个prototype Bean实例,这就违背了设置prototype Bean的初衷——本来希望它具有prototype行为,但实际上它却表现出singleton行为。

通常情况下,使用方法注入,使用lookup方法注入,使用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个non-singleton Bean。Spring通过使用JDK动态代理或cglib库修改客户端的二进制码,从而实现上述要求。

假设程序中有一个Chinese类型的Bean,该Bean包含一个hunt()方法,执行该方法时需要依赖于Dog的方法——而程序每次执行hunt()方法时都使用不同的Dog Bean,因此首先需要将Dog Bean配置为prototpy作用域。

除此之外,不能直接使用普通依赖注入,将Dog Bean注入Chinese Bean中,还需要使用lookup方法注入来管理Dog Bean与Chinese Bean之间的依赖关系。

  • 将调用者Bean的实现定义为抽象类,并定义一个抽象方法来获取被依赖的Bean。
  • 在<bean.../>元素中添加<lookup-method.../>子元素让Spring为调用者Bean的实现类实现指定的抽象方法。
public interface Person {public void hunt();
}
-----------------------------
public abstract class Chinese implements Person{public abstract Dog getDog();public void hunt() {System.out.println("我带着:" + getDog() + "出去打猎");System.out.println(getDog().run());}
}
-----------------------------
public class Dog {private String name;public void setName(String name) {this.name = name;}public String run(){return this.toString()+name+"迅速奔跑";}
}

<lookup-method .../>告诉Spring需要实现那个抽象方法。Spring为抽象方法提供实体之后,这个方法就会变成具体方法,这个类也就变成了具体类,接下来Spring就可以创建该Bean的实例了。

使用<lookup-method.../>元素需要指定如下两个属性:

  • name:指定需要让Spring实现的方法。
  • 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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="chinese" class="Bean.Chinese"><lookup-method name="getDog" bean="Dog"/></bean><bean id="Dog" class="Bean.Dog" scope="prototype"><property name="name" value="旭旭狗狗"/></bean>
</beans>

通常情况下,Java类里的所有方法都应该由程序员来负责实现,系统无法为任何方法提供实现。但在有些情况下,系统可以实现一些极其简单的方法,例如,此处Spring将负责实现getDog()方法,Spring实现该方法的逻辑是固定的,它总是采用传统的getBean()方法来实现。

-----------插曲,不是程序代码-------------
public Dog getDog(){//获取Spring容器return context.getBean("Dog");
}
import Bean.Person;
import org.springframework.beans.BeansException;
import org.springframework.context.support.*;
public class Test {public static void main(String[] args) throws BeansException  {AbstractApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");Person person1 = (Person) context.getBean("chinese");Person person2 = (Person) context.getBean("chinese");System.out.println(person1 == person2);person1.hunt();person2.hunt();}
}
==============================================
true
我带着:Bean.Dog@1fc2b765出去打猎
Bean.Dog@75881071旭旭狗狗迅速奔跑
我带着:Bean.Dog@2a70a3d8出去打猎
Bean.Dog@289d1c02旭旭狗狗迅速奔跑

执行结果表明:使用lookup方法注入后,系统每次调用getDog()方法时都将生成一个新的gunDog实例,这就可以保证当singleton作用域的Bean需要prototype Bean实例时,直接调用getDog()方法即可获取全新的实例,从而避免一直使用最早注入的Bean实例。

要保证lookup方法注入每次产生新的Bean实例,必须将目标Bean部署成prototype作用域;否则,如果容器中只有一个被依赖的Bean实例,即使采用lookup方法注入,每次也依然返回同一个Bean实例。


http://www.mrgr.cn/news/65924.html

相关文章:

  • 初识动态规划(由浅入深)
  • Django ORM详解:事务与F、Q函数使用
  • 【计网不挂科】计算机网络期末考试中常见【选择题&填空题&判断题&简述题】题库(3)
  • 不同的科技查新机构之间有什么区别?
  • centos7 安装python3.9.4,解决import ssl异常
  • 使用 pytorch 运行预训练模型的框架
  • H5开发指南|掌握核心技术,玩转私域营销利器
  • ES + SkyWalking + Spring Boot:日志分析与服务监控(三)
  • 数据结构————链表
  • MODBUS-TCP全解:有这一篇就够了
  • IP SSL证书
  • 2024年CISSP认证考试通关秘籍:备考方法与实战经验分享
  • idea java 项目右键new file时 为什么是 kotlin class 不是普通class
  • CDGP|数据资产入表:解锁数据价值,驱动数据要素流通的关键引擎
  • Memento 备忘录模式
  • 路径规划 | ROS中多个路径规划算法可视化与性能对比分析
  • 【Vue 全家桶】5、Vuex(更新中)
  • docker构建次数过多导致硬盘爆满,清除
  • 【windows命令详解】Windows系统信息查询终极指南:全面掌握`systeminfo`命令!
  • 深入解析:Percona Server 8.0.39 for CentOS 7 安装与优化全指南
  • springBoot集成shiro+权限刷新
  • SpringCloud-Nacos配置管理
  • Python 使用 OpenCV 进行全景拼接
  • 获取SKU详细信息API:揭秘商品背后的故事
  • 基于Springboot+安卓的健康饮食APP (含源码数据库)
  • 健康休息,注重休息