🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟
🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵
🍉本篇简介:🍉 本篇记录IOC容器创建bean实例的4种方式,如有出入还望指正。
关注公众号【可为编程】回复【面试】领取年度最新面试题!!!
了解了IOC是什么,接下来我们看IOC容器能做什么,首先其最最主要的功能就是对Bean进行管理和创建,IOC容器创建bean实例共有4种方式,具体如下:
通过反射调用构造方法创建bean对象
通过静态工厂方法创建bean对象
通过实例工厂方法创建bean对象
通过FactoryBean创建bean对象
Spring容器内部创建bean实例对象常见的有4种方式,这四种又可以分为两大种,一是基于反射机制,二是基于工厂模式,我将基于此并结合案例深入说明一下两者的区别和原理。
调用类的构造方法获取对应的bean实例,是使用最多的方式,这种方式只需要在xml bean元素中指定class属性,spring容器内部会自动调用该类型的构造方法来创建bean对象,将其放在容器中以供使用。
如果是采用注解形式创建和管理Bean,同样也是采用反射的机制,随着Spring的发展,注解(Annotation)逐渐成为主流的配置方式。使用注解可以减少配置文件的代码量,并且把相关的配置信息和代码放在一起,提高了可维护性。例如,使用@Component、@Service、@Repository、@Controller等注解可以自动创建Bean。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
"bean名称" name="bean名称或者别名" class="bean的完整类型名称"> "0" value="bean的值" ref="引用的bean名称" /> "1" value="bean的值" ref="引用的bean名称" /> - ....
"n" value="bean的值" ref="引用的bean名称" />
constructor-arg用于指定构造方法参数的值
index:构造方法中参数的位置,从0开始,依次递增
value:指定参数的值
ref:当插入的值为容器内其他bean的时候,这个值为容器中对应bean的名称
举个例子:这里我采用两种方式,首先采用Xml配置文件形配置并式定义Bean,二是采用注解形式生成Bean。
2、 IOC容器初始化细节
- public class Person {
-
- public String name;
- public Integer age;
- Wife wife;
-
- public Person(String s, Integer s2, Wife wife) {
- System.out.println("反射通过调用构造函数进行实例创建...");
- this.name = s;
- this.age = s2;
- this.wife = wife;
- }
- ...省略属性的get() set()方法
- }
beans.xml配置
"http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- https://www.springframework.org/schema/beans/spring-beans.xsd
- ">
-
-
-
"org.kewei.pojo.Person" id="person"> -
"org.kewei.pojo.Wife" ref="wife"/> -
"0" value="可为编程" /> -
"1" value="18" /> -
-
"org.kewei.pojo.Wife" id="wife" autowire-candidate="true"> -
"age" value="18"/> -
"name" value="可为"/> -
-
spring容器创建Person的时候,会通过反射的方式去调用Person类中对应的构造函数来创建Person对象。
- public class Main {
- public static void main(String[] args) {
- ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
- Person person = (Person) classPathXmlApplicationContext.getBean("person");
- System.out.println(person.name+person.age);
- System.out.println(person.getWifeName() + person.getWifeAge());
- }
- }
采用注解@Service定义Bean,如果没有指定BeanId,系统会自动以类名的首字母小写作为Bean名称进行生成。
- @Service
- public class KeWeiService {
- public KeWeiService() {
-
- System.out.println("基于注解形式创建正在创建KeWeiService--- " + this);
- System.out.println("反射通过调用构造函数进行实例创建...--- " + this);
- }
- }
- //获取Bean
- public class Main {
- public static void main(String[] args) {
- AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(KeWeiService.class);
- KeWeiService kw = (KeWeiService) annotationConfigApplicationContext.getBean("keWeiService");
- System.out.println(kw);
- }
- }
测试结果如下所示,可见都是采用了反射的机制进行Bean的生成创建。只不过两种不同的方式,根本原理上来说还是基于Java的反射原理。

Spring框架在创建Bean时,使用了Java反射(Reflection)机制。这种机制允许Spring在运行时检查和修改对象的一些行为。比如AOP就是采用了反射机制,对Bean可以在运行时对其进行行为修改,比如切面(Aspect),可以拦截目标对象的方法调用,并在调用前后加入额外的逻辑处理,Spring使用反射获取目标方法的信息,并动态地织入额外的代码逻辑。诚然,这块也用到了动态代理技术。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
总之,Spring容器使用反射来实例化、配置和管理Bean。当Spring容器启动时,它会读取配置文件(例如XML或Java类注释),并根据这些配置信息创建Bean实例。在这个过程中,Spring使用反射来调用对象的构造函数或静态方法来创建Bean,对于属性,Spring还使用反射来设置Bean的属性,当配置文件中定义了一个Bean的属性时,Spring会使用反射调用对象的setter方法来设置这些Bean的属性值。其实反射在Spring的很多地方都有体现,利用Java反射机制Spring实现了延迟加载、依赖注入以及AOP等核心功能。
我们还可以采用工厂模式,创建静态工厂,内部提供一些静态方法来生成所需要的对象,将这些静态方法创建的对象交给spring以供后续使用。
"bean名称" name="" class="静态工厂完整类名" factory-method="静态工厂的方法"> -
"0" value="bean的值" ref="引用的bean名称" /> -
"1" value="bean的值" ref="引用的bean名称" /> - ....
-
"n" value="bean的值" ref="引用的bean名称" />
class:指定静态工厂完整的类名
factory-method:静态工厂中的静态方法,返回需要的对象。
constructor-arg用于指定静态方法参数的值,用法和上面介绍的构造方法一样。
spring容器会自动调用静态工厂的静态方法获取指定的对象,将其放在容器中以供使用。
创建一个静态工厂类,用于生成Person对象。
- public class PersonStaticFactory {
-
- /**
- * 静态无参方法创建Person
- *
- * @return
- */
- public static Person build() {
- System.out.println(PersonStaticFactory.class + ".buildPerson1()");
- Person person = new Person();
- person.setName("我是无参静态构造方法创建的!");
- return person;
- }
-
- /**
- * 静态有参方法创建Person
- *
- * @param name 名称
- * @param age 年龄
- * @return
- */
- public static Person build2(String name, int age) {
- System.out.println(PersonStaticFactory.class + ".buildPerson2()");
- Person person2 = new Person();
- person2.setName(name);
- person2.setAge(age);
- return person2;
- }
- }
"createBeanByStaticFactoryMethod1" class="org.kewei.service.PersonStaticFactory" - factory-method="build"/>
"createBeanByStaticFactoryMethod2" class="org.kewei.service.PersonStaticFactory" - factory-method="build2">
-
"0" value="通过工厂静态有参方法创建UerModel实例对象"/> -
"1" value="30"/>
上面配置中,spring容器启动的时候会自动调用PersonStaticFactory中的build()静态方法获取Person对象,将其作为createBeanByStaticFactoryMethod1名称对应的bean对象放在IOC容器当中。
调用PersonStaticFactory的build2()方法,并且会传入2个指定的参数,得到返回的Person对象,将其作为createBeanByStaticFactoryMethod2名称对应的bean对象放在IOC容器中。
- ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
- Person createBeanByStaticFactoryMethod1 = (Person) classPathXmlApplicationContext.getBean("createBeanByStaticFactoryMethod1");
- Person createBeanByStaticFactoryMethod2 = (Person) classPathXmlApplicationContext.getBean("createBeanByStaticFactoryMethod2");
- System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
- System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);
- ---------------------------------------------
- 我是无参静态构造方法创建的!null
- 通过工厂静态有参方法创建Person实例对象30
从输出中可以看出,两个静态方法都被调用了,都输出了对应的信息,第一行为build()方法生成的Bean,第二行为build2()方法生成的Bean对象。
上面是通过配置文件的形式获取Bean对象,接下来我再演示一下通过注解的方式如何通过静态工厂生成Bean对象。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
当然,方法是静态的我们就可以直接调用,但为了演示注解的作用,交给Spring做管理,因此我们通过加注解的形式,获取Bean对象。
- @Configuration
- public class PersonStaticFactory {
-
- /**
- * 静态无参方法创建Person
- *
- * @return
- */
- @Bean("createBeanByStaticFactoryMethod1")
- public static Person build() {
- System.out.println(PersonStaticFactory.class + ".buildPerson1()");
- Person person = new Person();
- person.setName("我是无参静态构造方法创建的!");
- return person;
- }
-
- /**
- * 静态有参方法创建Person
- *
- * @return
- */
- @Bean("createBeanByStaticFactoryMethod2")
- public static Person build2() {
- System.out.println(PersonStaticFactory.class + ".buildPerson2()");
- Person person2 = new Person();
- person2.setName("通过工厂静态有参方法创建");
- person2.setAge(18);
- return person2;
- }
- }
通过@Bean注解使用一个静态方法创建一个Bean,并通过Bean的名称(在这里是createBeanByStaticFactoryMethod1)来获取它。首先要保障配置类被Spring扫描到使用@Configuration注解来标记配置类。在非Spring管理的类中直接通过名称获取Bean,需要手动从Spring上下文AnnotationConfigApplicationContext中获取它。
- AnnotationConfigApplicationContext annotationConfigApplicationContext1 = new AnnotationConfigApplicationContext(PersonStaticFactory.class);
- Person createBeanByStaticFactoryMethod1 = (Person) annotationConfigApplicationContext1.getBean("createBeanByStaticFactoryMethod1");
- Person createBeanByStaticFactoryMethod2 = (Person) annotationConfigApplicationContext1.getBean("createBeanByStaticFactoryMethod2");
- System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
- System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);
很显然,比我们用Xml配置的形式,少了好多代码,这也是Spring后期推行的主流方式。
让spring容器去调用某些对象的某些实例方法来生成bean对象放在容器中以供使用。
"bean名称" factory-bean="需要调用的实例对象bean名称" factory-method="bean对象中的方法"> -
"0" value="bean的值" ref="引用的bean名称" /> -
"1" value="bean的值" ref="引用的bean名称" /> - ....
-
"n" value="bean的值" ref="引用的bean名称" />
spring容器以factory-bean的值为bean名称查找对应的bean对象,然后调用该对象中factory-method属性值指定的方法,将这个方法返回的对象作为当前bean对象放在容器中供使用。
内部写2个方法用来创建Person对象。
- public class PersonFactory {
-
- /**
- * 静态无参方法创建Person
- *
- * @return
- */
- public Person build() {
- System.out.println(PersonFactory.class + ".buildPerson1()");
- Person person = new Person();
- person.setName("我是无参静态构造方法创建的!");
- return person;
- }
-
- /**
- * 静态有参方法创建Person
- * 关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
- *
- * @return
- */
- public Person build2(String name, int age) {
- System.out.println(PersonFactory.class + ".buildPerson2()");
- Person person2 = new Person();
- person2.setName(name);
- person2.setAge(age);
- return person2;
- }
- }
-
-
"personFactory" class="org.kewei.service.PersonFactory"/> -
-
"createBeanByBeanMethod1" factory-bean="personFactory" factory-method="build"/> -
-
"createBeanByBeanMethod2" factory-bean="personFactory" factory-method="build2"> -
"0" value="通过bean实例有参方法创建UserModel实例对象"/> -
"1" value="30"/> -
createBeanByBeanMethod1对应的bean是通过personFactory的build方法生成的。
createBeanByBeanMethod2对应的bean是通过personFactory的build2方法生成的。
- ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("test.xml");
- Person createBeanByStaticFactoryMethod1 = (Person) classPathXmlApplicationContext.getBean("createBeanByBeanMethod1");
- Person createBeanByStaticFactoryMethod2 = (Person) classPathXmlApplicationContext.getBean("createBeanByBeanMethod2");
- System.out.println(createBeanByStaticFactoryMethod1.name+createBeanByStaticFactoryMethod1.age);
- System.out.println(createBeanByStaticFactoryMethod2.name+createBeanByStaticFactoryMethod2.age);
- ---------------------------------------------
- 我是无参静态构造方法创建的!null
- 通过bean实例有参方法创建UserModel实例对象30
同样我们可以改成注解的形式
- @Service
- public class PersonFactory {
-
- /**
- * 静态无参方法创建Person
- *
- * @return
- */
- public Person build() {
- System.out.println(PersonFactory.class + ".buildPerson1()");
- Person person = new Person();
- person.setName("我是无参静态构造方法创建的!");
- return person;
- }
-
- /**
- * 静态有参方法创建Person
- *
- * @return
- */
- public Person build2(String name, int age) {
- System.out.println(PersonFactory.class + ".buildPerson2()");
- Person person2 = new Person();
- person2.setName(name);
- person2.setAge(age);
- return person2;
- }
- }
我们只需要在上面加一个@Service注解,就可以直接调用里面的方法。
- AnnotationConfigApplicationContext annotationConfigApplicationContext1 = new AnnotationConfigApplicationContext(PersonFactory.class);
- PersonFactory personFactory = (PersonFactory) annotationConfigApplicationContext1.getBean("personFactory");
- System.out.println(personFactory.build().name);
- ---------------------------------------------
- class org.kewei.service.PersonFactory.buildPerson1()
- 我是无参静态构造方法创建的!
前面我们学过了BeanFactory接口,BeanFactory是Spring容器的顶层接口,而这里要说的是FactoryBean,也是一个接口,这两个接口很容易搞混淆,FactoryBean可以让Spring容器通过这个接口的实现来创建我们需要的bean对象。
关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
FactoryBean接口源码:
- public interface FactoryBean
{ - /**
- * 返回创建好的对象
- */
- @Nullable
- T getObject() throws Exception;
- /**
- * 返回需要创建的对象的类型
- */
- @Nullable
- Class> getObjectType();
- /**
- * bean是否是单例的
- **/
- default boolean isSingleton() {
- return true;
- }
- }
接口中有3个方法,前面2个方法需要我们去实现,getObject方法内部由开发者自己去实现对象的创建,然后将创建好的对象返回给Spring容器;getObjectType需要指定我们创建的bean的类型;最后一个方法isSingleton表示通过这个接口创建的对象是否是单例的,如果返回false,那么每次从容器中获取对象的时候都会调用这个接口的getObject() 去生成bean对象。
<bean id="bean名称" class="FactoryBean接口实现类" />
- public class PersonFactoryBean implements FactoryBean
{ - int count = 1;
-
- @Nullable
- @Override
- public Person getObject() { //1
- Person person = new Person();
- person.setName("我是通过FactoryBean创建的第" + count++ + "对象");//4
- return person;
- }
-
- @Nullable
- @Override
- public Class> getObjectType() {
- return Person.class; //2
- }
-
- @Override
- public boolean isSingleton() {
- return true; //3
- }
- }
//1:返回了一个创建好的Person对象。
//2:返回对象的Class对象。
//3:返回true,表示创建的对象是单例的,那么我们每次从容器中获取这个对象的时候都是同一个对象。
//4:此处用到了一个count,通过这个一会可以看出isSingleton不同返回值的时候从容器获取的bean是否是同一个。
-
"createByFactoryBean" class="org.kewei.service.PersonFactoryBean"/>
启动类增加如下代码:
- System.out.println("-------------以下是FactoryBean创建的Bean对象-------------");
- //1.bean配置文件位置
- String beanXml = "classpath:/test.xml";
- //2.创建ClassPathXmlApplicationContext容器,给容器指定需要加载的bean配置文件
- ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
- System.out.println("spring容器中所有bean如下:");
- //getBeanDefinitionNames用于获取容器中所有bean的名称
- //关注公众号【可为编程】回复【面试】领取年度最新面试题大全!!!
- for (String beanName : context.getBeanDefinitionNames()) {
- System.out.println(beanName + ":" + context.getBean(beanName));
- }
- //多次获取createByFactoryBean看看是否是同一个对象
- System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
- System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));

注意最后3行输出,输出的都是同一个createByFactoryBean,并且对象唯一,程序中通过getBean从IOC容器中查找createByFactoryBean3次,3次结果都是相同对象,说明返回的都是同一个Person对象。
下面我们将UserFactoryBean中的isSingleton调整一下,返回false
- @Override
-
- public boolean isSingleton() {
-
- return false;
-
- }
当这个方法返回false的时候,表示由这个FactoryBean创建的对象是多例的,那么我们每次从容器中getBean的时候都会去重新调用FactoryBean中的getObject方法获取一个新的对象,再运行一下Client,最后3行输出:

很明显这3次获取的对象不一样,这也是SpringBean的作用域不同,下一篇进行讲解SpringBean的作用域。
SpringIOC容器提供了4种创建bean实例的方式,除了构造函数的方式,其他几种方式可以让我们手动去控制对象的创建,这几种方式大家都掌握一下,能够灵活使用。需要源码的联系我获取。

