目录
5. BeanFactory和FactoryBean的区别(面试题)
Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)
第一种:通过构造方法实例化
第二种:通过简单工厂模式实例化
第三种:通过factory-bean实例化(工厂方法模式)
第四种:通过FactoryBean接口实例化
我们之前一直使用的就是这种方式!默认情况下,会调用Bean的无参数构造方法,这里在复习一遍!
SpringBean类
package com.bjpowernode.spring.bean;
public class SpringBean {
public SpringBean() {
System.out.println("SpringBean的无参数构造方法执行了");
}
}
spring.xml配置
第一种:在spring配置文件中直接配置类全路径,Spring会自动调用该类的无参数构造方法来实例化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">
<!--Spring提供的实例化方式,第一种-->
<bean id="sb" class="com.bjpowernode.spring.bean.SpringBean"/>
</beans>
BeanInstantiationTest测试类
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.SpringBean;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation1(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
SpringBean sb = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb);
}
}
执行结果:成功调用无参数构造方法实例化对象

简单工厂模式又叫做静态工厂方法模式,因为工厂类中有一个静态方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
public class Vip {
public Vip() {
System.out.println("我是一个Vip");
}
}
第二步:编写简单工厂模式当中的工厂类
package com.bjpowernode.spring.bean;
public class VipFactory {
// 里面有一个静态方法
public static Vip get(){
// 实际上对象的创建还是我们程序员自己完成的
return new Vip();
}
}
第三步:在Spring配置文件中指定创建该Bean的方法
第二种:通过简单工厂模式。
需要在Spring配置文件中告诉Spring框架,调用哪个类的哪个方法获取Bean?
①class属性指定的是工厂类的全限定类名!
②factory-method属性指定的是工厂类当中的静态方法,也就是告诉Spring框架,调用这个方法可以获取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">
<!--Spring提供的实例化方式,第二种-->
<bean id="vipBean" class="com.bjpowernode.spring.bean.VipFactory" factory-method="get"/>
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.SpringBean;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.bean.VipFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation2(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Object vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
}
执行结果:通过简单工厂模式也能实例化对象

本质上是:通过工厂方法模式进行实例化对象!
注:简单工厂模式和工厂方法模式的区别
①简单工厂模式是所有的产品对应一个工厂类,使用的是静态方法!
②工厂方法模式是一个产品对应一个工厂类,使用的是实例方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
// 工厂方法模式当中的:具体产品角色
public class Gun {
public Gun() {
System.out.println("Gun的无参数构造方法执行");
}
}
第二步:定义具体工厂类,工厂类中定义实例方法
package com.bjpowernode.spring.bean;
// 工厂方法模式当中:的具体工厂角色
public class GunFactory {
// 实例方法
public Gun get(){
// 还是我们自己new的对象
return new Gun();
}
}
第三步:在Spring配置文件中指定factory-bean以及factory-method
第三种:通过工厂方法模式。
通过 factory-bean属性 + factory-method属性来共同完成。告诉Spring框架,调用哪个对象(因为是实例方法需要创建对象)的哪个方法来获取Bean。
①factory-bean属性用来告诉Spring调用那个对象!
②factory-method属性用来告诉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">
<!--Spring提供的实例化方式,第三种-->
<bean id="gunBean" class="com.bjpowernode.spring.bean.GunFactory"/>
<bean id="gun" factory-bean="gunBean" factory-method="get"/>
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.Gun;
import com.bjpowernode.spring.bean.SpringBean;
import com.bjpowernode.spring.bean.Vip;
import com.bjpowernode.spring.bean.VipFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation3(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Gun gun = applicationContext.getBean("gun", Gun.class);
System.out.println(gun);
}
}
执行结果:通过工厂方法模式也能实例化对象

①在第三种方式中,factory-bean和factory-method都是我们自己定义的。
②在Spring中,当编写的类直接实现FactoryBean接口之后,factory-bean和factory-method就不需要指定了!factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法!
第一步:定义一个Bean
package com.bjpowernode.spring.bean;
public class Person {
public Person() {
System.out.println("Person的无参数构造方法执行了");
}
}
第二步:编写一个类实现FactoryBean接口,重写里面的方法
PersonFactory也是一个Bean,只不过这个Bean比较特殊,叫做工厂Bean。通过工厂Bean这个特殊的Bean可以获取一个普通的Bean!
package com.bjpowernode.spring.bean;
import org.springframework.beans.factory.FactoryBean;
public class PersonFactory implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
// 对象的创建也是自己new的
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
// 这个方法是默认存在的,true表示单例,false表示原型
return true;
}
}
第三步:在Spring配置文件中配置FactoryBean
第四种:通过FactoryBean接口来实现,这种方式实际上就是第三种方式的简化!
①由于你编写的类实现了FactoryBean接口,所以这个类是一个特殊的类,不需要你再手动指定:factory-bean、factory-method。
②通过一个特殊的Bean:工厂Bean,来返回一个普通的Bean Person对象。即通过FactoryBean这个工厂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">
<!--Spring提供的实例化方式,第四种-->
<bean id="person" class="com.bjpowernode.spring.bean.PersonFactory" />
</beans>
第四步:编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.*;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void tesInstantiation4(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person);
}
}
执行结果:通过FactoryBean接口实例化

注:FactoryBean在Spring中是一个接口,被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean,所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的!
(1)BeanFactory(是一个工厂)
BeanFactory是Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象!
(2)FactoryBean(是一个Bean)
FactoryBean是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean!
在Spring中,Bean可以分为两类:
- 第一类:普通Bean
- 第二类:工厂Bean(工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象)
①前面我们说过,java.util.Date在Spring中被当做简单类型,简单类型在注入的时候可以直接使用value属性或value标签来完成。
②但是之前我们已经测试过了,对于Date类型来说,采用value属性或value标签赋值的时候,对日期字符串的格式要求非常严格,必须是这种格式的:Mon Oct 10 14:30:26 CST 2022,其他格式是不会被识别的!
③当然我们也可以当成非简单类型处理,使用ref属性来处理,但是却有一个弊端,获取的都是当前的时间,并不能自己指定时间!
注:下面我们就使用FactoryBean来完成这个骚操作!
Student类
package com.bjpowernode.spring.bean;
import java.util.Date;
public class Student {
// 每个学生都有出生日期
private Date birth;
@Override
public String toString() {
return "Student{" +
"birth=" + birth +
'}';
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
编写DateFactory实现FactoryBean接口
package com.bjpowernode.spring.bean;
import org.springframework.beans.factory.FactoryBean;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFactory implements FactoryBean<Date> {
// 定义一个日期属性,用来处理传过来的日期字符串
private String date;
// 通过构造方法给日期字符串属性赋值
public DateFactory(String date) {
this.date = date;
}
@Override
public Date getObject() throws Exception {
// 处理
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(this.date);
}
@Override
public Class<?> getObjectType() {
return null;
}
}
编写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">
<!--通过这个类的构造方法,把字符串转换成Date-->
<bean id="date" class="com.bjpowernode.spring.bean.DateFactory">
<constructor-arg name="date" value="1999-01-14"/>
</bean>
<!--把上面的Date通过上面的类,使用ref属性引进来-->
<bean id="studentBean" class="com.bjpowernode.spring.bean.Student">
<property name="birth" ref="date"/>
</bean>
</beans>
编写测试程序
package com.bjpowernode.spring.test;
import com.bjpowernode.spring.bean.*;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanInstantiationTest {
@Test
public void testDate(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
}
}
执行结果

我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c