基于SpringIOC创建对象的四种方式总结

我们平时创建对象的方式无非就是以下两种:

有参构造 、无参构造

我们来看看在Spring中怎么处理这两种情况

首先我们先创建一个实体类:

package com.MLXH.pojo;
public class User {
    private String name;
    private String sex;
    private int age;
    public User() {
        System.out.println("User的无参构造");
    }
    public User(String name) {
        System.out.println("User的有参构造");
        this.name = name;
    }
    public User(String name, int age) {
        System.out.println("User的第二种有参构造");
        this.name = name;
        this.age = age;
    }
    public User(String name, String sex, int age) {
        System.out.println("User的第三种有参构造");
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public void setName(String name) {
        System.out.println(name);
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Spring创建对象的方式:

通过有参构造

  • 通过下标
  • 通过参数名 【推荐】
  • 通过参数类型

通过无参构造

  • 默认会用无参构造

注意点:实体类中一定要有一个无参构造方法

接下来我们看一下Spring是如何装配这些对象的:

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="user" class="com.MLXH.pojo.User">
    </bean>
    <!--无参构造,执行set方法-->
    <bean id="user1" class="com.MLXH.pojo.User">
        <property name="name" value="寒雪1"/>
    </bean>
    <!--使用构造器的参数下标进行赋值-->
    <bean id="user2" class="com.MLXH.pojo.User">
        <constructor-arg index="0" value="寒雪2"/>
        <constructor-arg index="1" value="18"/>
    </bean>
    <!--通过名字进行赋值-->
    <bean id="user3" class="com.MLXH.pojo.User">
        <constructor-arg name="name" value="寒雪3"/>
        <constructor-arg name="age" value="3"/>
    </bean>
    <!--通过类型进行赋值-->
    <bean id="user4" class="com.MLXH.pojo.User">
        <constructor-arg type="java.lang.String" value="寒雪4"/>
        <constructor-arg type="java.lang.Integer" value="18"/>
        <constructor-arg type="java.lang.String" value="男"/>
    </bean>
</beans>

测试类

package com.MLXH.pojo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User)context.getBean("user1");
        System.out.println(user.toString());
    }
    @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user2");
        System.out.println(user);
    }
    @Test
    public void test3(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user3");
        System.out.println(user);
    }
}

结果:运行第一个测试类的结果:

分析:我们针对输出的结果进行分析,我们拿到的是user1对象,那么为什么会输出这么多的语句呢?

原因是我们执行了spring的配置文件bean.xml的全部内容,当执行

ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

语句时,会加载beans.xml中的全部内容,因此其中所有的装配信息都会进行加载,会按照我们装配的顺序进行加载。

注意:

<bean id="user1" class="com.MLXH.pojo.User">
        <property name="name" value="寒雪1"/>
</bean>

看似像是执行了有参构造,但其实是执行了无参构造,然后通过set方法将,name赋值进去的…就像是执行了这样的代码:

User user1 = new User();
user1.setName="寒雪1";

这只是执行了Test1的,其他的类似…

SpringIOC——控制反转中创建对象的细节

在刚开始使用Java的时候,创建对象的方式多半是使用 new+对应的构造方法,为了进一步提高对象的安全性和降低耦合,开始使用工厂模式和单例模式,通过调用工厂里的方法获取对象,再后来通过反射加配置文件的方式,在创建对象的过程中进一步降低耦合。

那么SpringIOC里创建对象的过程是怎样的呢?具体来说:SpringIOC是什么时候创建对象的?通过哪种方法创建的对象?创建的对象是单例的还是多例的?创建的对象是由谁来保管和控制的?

这些问题不仅仅是Spring需要考虑的,而且是程序员应该考虑的,我们创建对象的效率和对内存的使用率很大程度上影响到开发代码的优劣性。

接下来,我们就通过Spring创建对象的过程,一步步来讨论一下Spring框架IOC组件部分,创建对象的细节。

一、Spring创建对象的过程(在此之前我们已经配置好了Spring的配置文件)

启动spring容器(根据Spring配置文件不同创建不同的Spring容器)

//启动spring容器(根据Spring配置文件不同创建不同的Spring容器)
ApplicationContextcontext =
newClassPathXmlApplicationContext(
"resources/spring_ioc.xml");

这里实现的步骤如下:

1.读取resources文件下spring_ioc.xml

2.Xml解析spring_ioc.xml

3.把解析xml里的内容存储到内存的map集合中

4.从map集合中读取集合内容,就是<Bean></Bean>清单里的内容

5.遍历集合中的所有数据,并反射实例化对象

Class.forName(“com.spring.ioc.Hello”).newInstance()

从容器中取出对象,以及使用对象

//从spring容器中获取对象hello
Hello hello = (Hello)context.getBean("Hello");
//使用hello对象调用sayHello方法
hello.sayHello();

以上关键的两个环节中,需要重点理解的是spring容器为什么能创建保管对象,因为Spring在初始化的时候,就将Bean 里的属性调入放入到一个map集合中区维护了,这个map集合的key=”id”,value=”object” (反射实例化的对象),spring容器在底层就相当于这个map<id,object>,而Bean就被存放在这个map中。

当我们把spring容器启动好以后,也就是context对象创建完成后,通过调用context对象里的getBean()方法来从map中用key寻找value,从而获取对象。

细化分析:启动Spring容器时,将Bean存储到Map集合中,需要调用反射来实例化Bean中封装的类路径,那么xml配置文件中每存储一个不同的Bean,就会实现一次反射,产生一个实例对象,也就是说,Spring容器中产生的对象仅和xml配置文件相关,与取出对象操作无关,Spring可以实例化一个类多次(<Bean/>中id不同,class相同),也可以只实例化一个类一次,但多次调用。单例的创建时间是在Spring容器启动时。

二、创建对象的四种方式

由上我们知道Spring底层是通过反射来创建对象的,那我们在创建类的时候,可以创建实体类,抽象类,接口等等,spring底层反射到底使用什么方法来创建对象呢?这就需要考虑我们是创建什么对象了。

1. 无参构造

反射是通过无参构造来实例化类的,所以,如果我们创建一个实体类,并想将类产生的实例放到spring容器中,必须保证这个类有无参构造。当这个类中未写构造器时,java编译器会在编译时自动添加一个无参构造,但是如果这个类中有含参构造时,只能通过人工添加无参构造来实现反射,这一点在以后的开发设计中要牢记。

2. 静态工厂设计模式

那抽象类怎么实例化呢?抽象类无法直接实例化,只能通过添加静态工厂的方式,在工厂中添加静态方法返回抽象类的getInstance()方法,从而获取实例对象。

这时,我们需要一个抽象类,一个工厂类,而且xml中的配置文件也需要发生变化,<Bean>中class类的加载路径需要指定为工厂类,而且需要指定新的属性为:factory-method=”get抽象类的实例对象方法”。

例:我们需要用util包下的Calendar这个抽象日期类。

静态工厂代码为:

        package staticfactory;
        import java.util.Calendar;
        public class StaticFactory {
               public static Calendar getCalendar(){
                      return Calendar.getInstance();
               }
         }

Xml文件中的Bean应该修改成:

<bean id="cal" class="staticfactory.StaticFactory" factory-method="getCalendar"/>

测试可使用:双击test2方法右键run as》JUnit得到当前电脑的系统时间。

@Test
publicvoid test2(){
      //启动spring容器
     ApplicationContextcontext =
     newClassPathXmlApplicationContext("staticfactory/applicationContext.xml");
     Calendar calendar = (Calendar)context.getBean("cal");
     System.out.println(calendar.getTime());
}

无法直接实例化的类就可以使用静态工厂法。

此时Spring中并未创建工厂这个类,而是根据<Bean/>中的factory—method属性,直接调用工厂的getCalendar静态方法,产生的实例只有一个,就是工厂中,getCalendar静态方法通过调用Calendar.getInstance()方法产生的实例。

3. 实例工厂设计模式

静态工厂设计模式不会创建工厂类对象,而是直接调用静态工厂的静态方法。而工厂方法设计模式会创建类,然后通过xml中配置去访问其方法。

例:同样是需要Calendar这个抽象类

package methodfactory;
import java.util.Calendar;
public class MethodFactory {
       publicMethodFactory() {
      System.out.println("如果创建工厂类的实例,必定会打印这句话!");
       }
       publicCalendar getCalendar(){
      returnCalendar.getInstance();
       }
}

Xml中的配置由一个变为两个(这也证明了上述结论,配置文件中<Bean/>的个数影响产生的实例对象的个数)

<bean id="methodFacotry" class="methodfactory.MethodFactory"/>
<bean id="cal" factory-bean="methodFacotry"  factory-method="getCalendar"/>

测试可使用:双击test2方法右键run as》JUnit得到当前电脑的系统时间。

@Test
public void test(){
//启动spring容器
ApplicationContext context =
newClassPathXmlApplicationContext("methodfactory/applicationContext.xml");
Calendar calendar = (Calendar)context.getBean("cal");
System.out.println(calendar.getTime());
}

输出结果为上述打印的那句话和系统时间,两个<Bean/>产生两个实例对象。

4. Spring工厂设计模式

这是Spring框架自身提供的工厂,它需要实现FactoryBean接口,实现代码就必须写在getObject()方法中。Spring工厂实现一个接口就可以了,简单方便。

例:

创建SpringFactory类实现FactoryBean接口,定义泛型<Calendar>

package springfactory;
import java.util.Calendar;
import org.springframework.beans.factory.FactoryBean;
public class SpringFactory implements FactoryBean<Calendar>{
public SpringFactory() {
System.out.println("我是一个spring工厂类");
}

public Calendar getObject()throws Exception {
return Calendar.getInstance();
}

public Class<?> getObjectType() {
return Calendar.class;
}

public boolean isSingleton() {
return false;
}
}

配置xml文件中的Bean元素:

<bean id="cal"class="springfactory.SpringFactory"/>

测试可使用:双击test3方法右键run as》JUnit得到当前电脑的系统时间。

@Test
public void test3(){
//启动spring容器
ApplicationContext context =
newClassPathXmlApplicationContext("springfactory/applicationContext.xml");
Calendar calendar = (Calendar)context.getBean("cal");
System.out.println(calendar.getTime());
}

三、Spring创建对象是单例还是多例?+懒加载

单例就是指在在Spring容器中的对象只有一个,每次从spring容器中取出不会产生新的对象。

多例就是每次从Spring容器中取出对象的时候会产生新的对象,内存中存在多个对象。

默认情况下,spring中创建的对象都是单例,并且维护其生命周期。单例对象的生命周期与spring容器共命运,同生共死。

但如果对象是多例的,那么Spring容器只负责对象的创建,不负责维护其生命周期,也就是说如果容器关闭,对象并未销毁,需要用户自行关闭。

具体实现代码是在Bean标签中插入属性


<bean id="Hello" class="com.spring.ioc.Hello"scope="prototype"/>

Scope属性代表了spring创建对象是单例还是多例。

那么单例多例各有什么优缺点呢?

单例的好处就是,spring创建后,那就不会再频繁创建,缓存在map中,省内存。坏处,对象里的数据不安全,即线程不安全

多例的好处就是随时用随时创建,线程安全,缺点是占用内存。

单例多例各有各的特点,思考一个问题,如果Spring容器中要使用很多次单例,那么单例是不是丧失了它的优势呢?即使没有业务调用这些对象,这些对象依然在spring容器加载的时候产生,不仅仅浪费了容器的容量,还延长了加载时间,这时候我们就需要用懒加载,堆单例模式进行改造了。

首先我们可以在全局配置(第一个<Bean/>)中添加default-lazy-init="true",这样就默认使用懒加载。

或者在单例的配置中添加:

<bean id="Hello"class="com.spring.ioc.Hello" scope="singleton"lazy-init="true"/>

另外,多例模式都是懒加载,当多例模式的懒加载被设置为false时,会报错!

四、对象的创建和保管(初始化+销毁)

Spring 容器执行过程

1.new Instance----调用构造方法创建对象

2.Init-method------执行初始化方法

3.对象调用方法。

4.容器关闭,执行销毁方法 ,如果scope=”property”时让其不负责对象的销毁

总结

通过对Spring内对象产生的细节进行探究,了解Spring底层创建实例的方式,通过熟练使用这些方式,掌握对Spring框

架的理解,这样对程序员的自我成长是很好的~

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring创建bean对象三种方式代码实例

    一.使用无参构造方法创建 二.使用静态工厂创建 三.使用实例工厂创建 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们.

  • Spring中实例化bean的四种方式详解

    前言 在介绍Bean的实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果把Spring看作一个大型工厂,那么Spring容器中的Bean就是该工厂的产品.要想使用Spring工厂生产和管理Bean,就需要在配置文件中指明需要哪些Bean,以及需要使用何种方式将这些Bean装配到一起. Spring容器支持两种格式的配置文件,分别为Properties文件格式和xml文件格式,而在实际的开发当中,最常使用的额是xml文件格式,因此在如下的讲解中,我们以xml文件格

  • Spring IOC创建对象的两种方式

    IOC创建对象的方式 一. 使用无参构造创建对象(默认方式) 创建实体类 注意:属性必须要有set方法,来完成注入 public class User { private String name; public User() { System.out.println("执行了User类的无参构造方法~"); } public User(String name){ this.name = name; System.out.println("执行了User类的有参构造方法&quo

  • Spring实现IoC的多种方式小结

    控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法.没有IoC的程序中我们使用面向对象编程对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了. IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现Io

  • 基于SpringIOC创建对象的四种方式总结

    我们平时创建对象的方式无非就是以下两种: 有参构造 .无参构造 我们来看看在Spring中怎么处理这两种情况 首先我们先创建一个实体类: package com.MLXH.pojo; public class User { private String name; private String sex; private int age; public User() { System.out.println("User的无参构造"); } public User(String name)

  • js学习总结_基于数据类型检测的四种方式(必看)

    1.typeof 用来检测数据类型的运算符 console.log(typeof 12)//Number 使用typeof检测数据类型,首先返回的都是字符串 ,其次字符串中包含了对应的数据类型 例如:"number"."string"."boolean"."undefined"."function"."object" console.log(typeof typeof function(

  • JS创建对象的四种方式

    目录 1.通过字面量的方式去创建对象 2.使用new字符创建对象 3.自定构造函数创建对象 4.工厂模式创建对象 创建对象的4种方式: 1.字面量的方式去创建对象 2.使用new字符创建对象 3.自定义构造函数 4.工厂模式创建对象 对象是指:具有属性和方法 1.通过字面量的方式去创建对象 var person1={ name:'柯南', age:12, sex:'男', eat:function(){ console.log("我饿了,想吃饭") }, say:function(){

  • 详细分析Javascript中创建对象的四种方式

    前言 使用Javascript创建对象的方式有很多,现在就来列举一下其中的四种方式,并且罗列出了每种方式的优缺点,可以让大家进行选择使用,下面来看看. 工厂模式 function createPerson(name, age){ var obj = new Object(); obj.name = name; obj.age = age; return obj; //一定要返回,否则打印undefined:undefined } var person1 = new createPerson('Y

  • 基于spring三方包类注入容器的四种方式小结

    如果引用第三方jar包,肯定是不能直接使用常用注解@Controller.@Service.@Repository.@Component将类的实例注入到spring容器中.以下四种方法可以向spring容器中导入三方包中类实例 . 1 xml配置 这种情况大家用的比较多,就是在spring的xml文件中配置需要导入的bean.在springweb项目工程web.xml中 ContextLoaderListener或者DispatcherServlet的初始参数contextConfigLocat

  • linux下实现web数据同步的四种方式(性能比较)

    实现web数据同步的四种方式 ======================================= 1.nfs实现web数据共享2.rsync +inotify实现web数据同步3.rsync+sersync更快更节约资源实现web数据同步4.unison+inotify实现web数据双向同步 ======================================= 一.nfs实现web数据共享 nfs能实现数据同步是通过NAS(网络附加存储),在服务器上共享一个文件,且服务器需

  • javascript中类的定义方式详解(四种方式)

    本文实例讲述了javascript中类的定义方式.分享给大家供大家参考,具体如下: 类的定义包括四种方式: 1.工厂方式 function createCar(name,color,price){ var tempcar=new Object; tempcar.name=name; tempcar.color=color; tempcar.price=price; tempcar.getName=function(){ document.write(this.name+"-----"+

  • android全局监控click事件的四种方式(小结)

    本文主要给大家分享如何在全局上去监听 click 点击事件,并做些通用处理或是拦截.使用场景可能就是具体的全局防快速重复点击,或是通用打点分析上报,用户行为监控等.以下将以四种不同的思路和实现方式去监控全局的点击操作,由简单到复杂逐一讲解. 方式一,适配监听接口,预留全局处理接口并作为所有监听器的基类使用 抽象出公共基类监听对象,可预留拦截机制和通用点击处理,简要代码如下: public abstract class CustClickListener implements View.OnCli

  • Python3实现定时任务的四种方式

    最近做一个小程序开发任务,主要负责后台部分开发:根据项目需求,需要实现三个定时任务: 1>定时更新微信token,需要2小时更新一次: 2>商品定时上线: 3>定时检测后台服务是否存活: 使用Python去实现这三个任务,这里需要使用定时相关知识点: Python实现定点与定时任务方式比较多,找到下面四中实现方式,每个方式都有自己应用场景:下面来快速介绍Python中常用的定时任务实现方式: 1>循环+sleep: 2>线程模块中Timer类: 3>schedule模块

  • Oracle 遍历游标的四种方式汇总(for、fetch、while、BULK COLLECT)

    1.情景展示 Oracle 遍历游标的四种方式(for.fetch.while.bulk collect+forall) 2.问题分析 我们可以把游标想象成一张表,想要遍历游标,就要取到游标的每行数据,所以问题的关键就成了:如何取到行数据? 3.解决方案 方式一:FOR 循环(推荐使用) 变形一:遍历显式游标 /* 如果是在存储过程外使用显式游标,需要使用DECLARE关键字 */ DECLARE   /*创建游标*/   CURSOR CUR_FIRST_INDEX IS     SELECT

随机推荐