当前位置: 首页 > 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实例。

相关文章:

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

1&#xff0c;Bean的创建 1.1&#xff0c;调用构造器创建Bean 调用Bean类的无参构造函数来创造对象&#xff0c;因此要求提供无参构造函数。在这种情况下class元素是必须的&#xff0c;值就是Bean对象的实现类。 如果采用设值注入&#xff0c;Spring容器将使用默认的构造器来创…...

Flutter中的Extension关键字

目录 前言 一、什么是扩展(Extension) 二、扩展的语法 三、示例:为String 添加扩展方法 四、使用扩展的场景 五、复杂示例:为DateTime添加扩展 前言 在 Dart 和 Flutter 中&#xff0c;extension 关键字允许开发者为现有的类添加新的功能&#xff0c;而无需修改原有类的代…...

transformers 框架使用详解,bert-base-chinese

以 bert-base-chinese 模型为例&#xff0c;模型目录 model_name "C:/Users/Administrator.DESKTOP-TPJL4TC/.cache/modelscope/hub/tiansz/bert-base-chinese" bert-base-chinese 模型大小只有400多兆&#xff0c;参数的量级在百万级别&#xff0c;与现在动辄几十…...

STM32——ADC

目录 1、ADC的介绍 2、ADC主要特征 3、ADC结构与引脚 4、ADC配置流程 5、示例&#xff08;光敏电阻的ADC采样&#xff09; 6、提示 7、结语&#xff1a; 1、ADC的介绍 12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道&#xff0c;可测量16个外部和2个内部 信号…...

Unity SRP学习笔记(二)

Unity SRP学习笔记&#xff08;二&#xff09; 主要参考&#xff1a; https://catlikecoding.com/unity/tutorials/custom-srp/ https://docs.unity.cn/cn/2022.3/ScriptReference/index.html 中文教程部分参考&#xff08;可选&#xff09;&#xff1a; https://tuncle.blog/c…...

数据库第五次作业

一要求 二建库建表 触发器 存储过程 三查询 触发器 1 建立触发器&#xff0c;订单表中增加订单数量后&#xff0c;商品表商品数量同步减少对应的商品订单出数量,并测试 测试 2 建立触发器&#xff0c;实现功能:客户取消订单&#xff0c;恢复商品表对应商品的数量 测试 3…...

健身房业务流程优化:SpringBoot解决方案

3系统分析 3.1可行性分析 通过对本健身房管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本健身房管理系统采用SSM框架&#xff0c;JAVA作为开发语言&a…...

【产品经理】工业互联网企业上市之路

树根互联2022年6月2日提交招股书之后&#xff0c;因财务资料超过六个月有效期加三个月延长期&#xff0c;2022年9月30日上市审核中止&#xff1b;2022年12月26日树根互联更新了2022年半年度财务资料&#xff0c;又九个月过去了&#xff0c;其上市进程将面临再一次中止。 处于上…...

Java学习教程,从入门到精通,Java对象和类语法知识点(20)

1、Java对象和类语法知识点 类的定义 使用class关键字定义类。类名通常使用大写驼峰命名法&#xff08;PascalCase&#xff09;。类与对象 类是创建对象的模板或蓝图&#xff0c;它定义了对象的属性和行为。对象是类的实例&#xff0c;它包含了类定义的数据&#xff08;属性&am…...

金融场中的量化交易:民锋数据驱动策略的优势解析市

随着科技的发展&#xff0c;量化交易成为金融市场的重要组成部分。民锋公司通过智能算法和大数据分析&#xff0c;设计了一系列量化交易策略&#xff0c;帮助投资者实现科学投资。本文将探讨民锋在数据驱动策略上的优势&#xff0c;并展示如何通过量化模型在复杂的市场中获得收…...

Docker 配置镜像加速

docker 拉取代码时出现 ERROR: failed to solve: node:16: unexpected status from HEAD request to https:// xxxxxx.mirror.aliyuncs.com/v2/library/node/m…...

HTTP慢速攻击原理及解决办法

目录 引言 HTTP慢速攻击原理 解决办法 Nginx Tomcat 华宇TAS IIS 结论 引言 HTTP慢速攻击&#xff08;Slow HTTP Attack&#xff09;是一种拒绝服务攻击&#xff08;DoS&#xff09;&#xff0c;攻击者通过故意缓慢地发送HTTP请求来耗尽服务器资源&#xff0c;导致合法…...

【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、锁分类

目录 一、问题综述 1. 进程和线程的区别&#xff1f; 2. 进程的状态有哪些&#xff1f; 3. 进程之间的通信方式? &#xff08;1&#xff09;管道 &#xff08;2&#xff09;消息队列 &#xff08;3&#xff09;共享内存 &#xff08;4&#xff09;信号量 &#xff08…...

[C++]——哈希(附源码)

目录 ​编辑 ​编辑 一、前言 二、正文 2.1 unorder系列关联式容器 2.1.1 unordered_map 2.1.1.1 unorderer_map的介绍 ①unordered_map的构造 ②unordered_map的容量 ③unordered_map的迭代器 ④unordered_map的元素访问 ⑤unordered_map的查询 ⑥unordered_map的修改操…...

2024中国自动化大会(CAC2024)“智慧化工及复合人才培养”平行会议圆满落幕

2024中国自动化大会于11月1-3日在青岛举行&#xff0c;本次大会由中国自动化学会主办&#xff0c;青岛科技大学&#xff08;简称“青科大”&#xff09;承办。北京和隆优化科技股份有限公司&#xff08;简称“和隆优化”&#xff09;承办了重要的“智慧化工及复合人才培养”平行…...

计算机毕业设计——ssm基于JAVA的求职招聘网站的设计与实现演示录像 2021

作者&#xff1a;程序媛9688开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等。 &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还有大家在毕设选题&#xff08;免费咨询指导选题&#xff09;&#xff0…...

跨平台Flutter 、ReactNative 开发原理

一、跨平台Flutter开发原理 Flutter是一个跨平台的应用程序开发框架&#xff0c;它允许你使用一组代码库来构建同时运行在Android和iOS上的应用程序。 1.1.Flutter的核心原理基于以下几点&#xff1a; Dart异步、Widget构建块灵活配置、自工化工具链、热重载、Skia图库、Dar…...

qt QToolBar详解

1、概述 QToolBar是Qt框架中的一个控件&#xff0c;用于在工具栏中显示一组操作按钮和其他控件。它提供了一种方便的方式来组织和管理应用程序中的工具和操作。工具栏通常位于软件或应用程序界面的上方&#xff0c;包含一系列常用工具和命令按钮&#xff0c;用于快速访问和执行…...

MongoDB基础介绍以及从0~1语法介绍

目录 MongoDB 教程导读 NoSQL 简介 关系型数据库遵循ACID规则 分布式系统 分布式计算的优点 分布式计算的缺点 什么是NoSQL? 为什么使用NoSQL ? RDBMS vs NoSQL NoSQL 简史 CAP定理&#xff08;CAP theorem&#xff09; NoSQL的优点/缺点 BASE ACID vs BASE N…...

利用Docker Compose构建微服务架构

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 利用Docker Compose构建微服务架构 引言 Docker Compose 简介 安装 Docker Compose 创建项目结构 编写 Dockerfile 前端 Dockerf…...

数据中台一键大解析!

自从互联玩企业掀起了数据中台风&#xff0c;数据中台这个点马上就火起来了&#xff0c;短短几年数据中台就得到了极高的热度&#xff0c;一大堆企业也在跟风做数据中台&#xff0c;都把数据中台作为企业数字化转型的救命稻草&#xff0c;可是如果我告诉你数据中台并不是万能钥…...

MySQL45讲 第十六讲 “order by”是怎么工作的?

文章目录 MySQL45讲 第十六讲 “order by”是怎么工作的&#xff1f;一、引言二、全字段排序&#xff08;一&#xff09;索引创建与执行情况分析&#xff08;二&#xff09;执行流程&#xff08;三&#xff09;查看是否使用临时文件 三、rowid 排序&#xff08;一&#xff09;参…...

智慧商城项目-VUE2

实现效果 项目收获 通过本项目的练习&#xff0c;可以掌握以下内容&#xff1a; 创建项目 ##基本创建 基于 VueCli 自定义创建项目架子,并对相关的配置进行选择 vue create demo-shopping调整目录 删除文件 删除初始化的一些默认文件 src/assets/logo.pngsrc/components…...

音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)

本文接着《音视频入门基础&#xff1a;FLV专题&#xff08;21&#xff09;——FFmpeg源码中&#xff0c;获取FLV文件音频信息的实现&#xff08;上&#xff09;》&#xff0c;继续讲解FFmpeg获取FLV文件的音频信息到底是从哪个地方获取的。本文的一级标题从“四”开始。 四、音…...

Chrome与火狐哪个浏览器的性能表现更好

在数字时代&#xff0c;浏览器是我们日常生活中不可或缺的工具。无论是工作、学习还是娱乐&#xff0c;一个好的浏览器都能显著提高我们的效率和体验。市场上有许多优秀的浏览器&#xff0c;其中Google Chrome和Mozilla Firefox无疑是最受欢迎的两款。本文将比较这两款浏览器的…...

uniapp在js方法中,获取当前用户的uid(uni-id-user)表中的用户id

// 1.判断当前用的权限 let uid uniCloud.getCurrentUserInfo().uid //获取当前用户的uid // 用户uid等于发布者id或者用户权限等于admin或者用户角色等于webmaster if (uid this.item.user_id[0]._id || this.uniIDHasRole…...

影响神经网络速度的因素- FLOPs、MAC、并行度以及计算平台

影响神经网络速度的四个主要因素分别是 FLOPs&#xff08;浮点操作数&#xff09;、MAC&#xff08;内存访问成本&#xff09;、并行度以及计算平台。这些因素共同作用&#xff0c;直接影响到神经网络的计算速度和资源需求。 1. FLOPs&#xff08;Floating Point Operations&a…...

【万字详解】如何在微信小程序的 Taro 框架中设置静态图片 assets/image 的 Base64 转换上限值

设置方法 mini 中提供了 imageUrlLoaderOption 和 postcss.url 。 其中&#xff1a; config.limit 和 imageUrlLoaderOption.limit 服务于 Taro 的 MiniWebpackModule.js &#xff0c; 值的写法要 &#xff08;&#xff09;KB * 1024。 config.maxSize 服务于 postcss-url 的…...

复合选择器,CSS特性,背景属性,显示模式(HTML)

目录 复合选择器,CSS特性,背景属性,显示模式知识点: 练习一: 练习二: 复合选择器,CSS特性,背景属性,显示模式知识点: 复合选择器:后代选择器 :父选择器 子选择器(中间用空格隔开) eg:对div中的span进行设置,会对后代中的所有span都进行设置 选中所有后代(后代选择器.html)…...

加密货币行业与2024年美国大选

加密货币行业经历了近十年的飞速发展&#xff0c;尤其是在比特币、以太坊等主要加密资产的兴起之后&#xff0c;越来越多的美国人开始将其视为一种财富积累或交易的工具。然而&#xff0c;尽管这一新兴行业的市场规模在持续扩大&#xff0c;但加密货币仍面临着重重监管难题&…...