自学Spring框架之 IoC

 IoC(Inversion of Control)控制反转

                    对象的创建控制权由程序内部转移到外部,这种思想称为控制反转.

简单来说就是在程序中使用对象时,不使new 产生对象,而由外部提供对象.

Spring 就提供了一个容器,称为IoC容器 ,该容器可以为程序内部提供对象,实现解耦

IoC容器负责对象的创建,初始化等工作,被创建或被管理的对象在Ioc容器中统称为bean

 解释:bean可以看成一个'变量',这个'变量'的类型是一个对象,在Spring配置文件中配置bean的具体类型属性

思考:如果一个类的内部需要另一个类对象时,需要配置几个bean?这俩个bean什么关系?

IoC提供了一种解决方法 DI(dependcy Injection)依赖注入

具体意思就是把两个bean 之间建立依赖关系,称为依赖注入

注意并不是随意绑定的,绑定bean关系的前提是在程序内部一个类中需要使用另一个类的对象时,就把这两个类分别配置一个bean,然后对这两个bean进行绑定

IoC入门案例

     分析:IoC是管理类对象的,IoC容器中可以由许多的bean,通过配置文件将被管理对象告知IoC容器,再通过接口以及接口方法获取IoC容器和bean

1.在pom.xml配置文件中添加Spring坐标 

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.10.RELEASE</version>
    </dependency>

 导入坐标后右键resources -->new  --> xml Configuration File --> Spring Config

创建一个Spring配置文件,起名最好是  applicationContext  

2.配置bean 

applicationContext配置文件中使用<bean>标签来配置bean,格式如下

<bean id = " " class = " " />

 其中bean标签可以是自闭和标签也可以是围堵标签

* id属性表示给bean起的名字,这个值唯一,且可以使用name属性起别名 

* class 属性表示给bean定义的而类型,它是一个url 需要配置bean 的类的路径

例子<bean id = "person " class = "com.itxiaobai.user.Person" />

 这样一个类型为Person的bean就配置好了

3.获取IoC容器

public class BeanTest{
    public static void main(String args[]) throws Exception{
//ApplicationContext是一个接口,通过其实现类ClassPathXmlApplicationContext得到一个IoC容器
//通过传入配置applicationContext.xml文件让这个IoC容器中有bean
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

4.获取bean

public class BeanTest{
    public static void main(String args[]) throws Exception{
//ApplicationContext是一个接口,通过其实现类ClassPathXmlApplicationContext得到一个IoC容器
//通过传入配置applicationContext.xml文件让这个IoC容器中有bean
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//上一步中创建了一个IoC容器对象ctx 通过调用getBean()方法以及IoC中管理的bean Id值生成一个Person对象p
        Person p = ctx.getBean("person",Person.class);
        p.say();
    }
}

DI入门案例思路

分析下列代码:

代码1

public class Person {//一个Person类 里面有一个son成员和一个say方法
prviate son ;
public void say(){
System.out.println(" Person...")
son.say();
}
}

代码2

public class Son {//一个son类 里面有一个say方法
public void say(){
System.out.println(" Son...")
}
}

问题,从IoC入门案例中配置文件中只配置了Person的一个bean,但是通过IoC容器获取到属性为Person的对象后调用say方法,Person中的say方法会使用Son对象去调用Son的say方法,但是Son的对象并没有初始化所以会报异常,所以代码1优化一下就是

代码3

public class Person {//一个Person类 里面有一个son成员和一个say方法
prviate son ;
public void say(){
System.out.println(" Person...")
son.say();
}
//新增一个set方法对son进行初始化
public void setSon(Son son){
this.son =son;
}
}

最后去applicationContext 配置文件里添加一个Son类型的bean

<bean id = "son" class = "com.itxiaobai.user2.Son"></bean>

 此时IoC容器里的两个bean并没有绑定,所以要在配置文件中绑定关系,分析出是Person类型的bean依赖Son类型的bean所以要在person中添加

<bean id = "person " class = "com.itxiaobai.user.Person" >

<property name = "son" ref = "son"/>

</bean>

 使用<property>标签来绑定两个bean之间的关系

*name 属性代表的是Person类中的私有son成员的名称

*ref 属性代表的是IoC容器中已经配置的类型为Son类型的bean id值

bean详解

Spring容器中的Bean

对于开发者来说,开发者使用Spring框架主要是做两件事:①开发Bean;②配置Bean。对于Spring框架来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成"依赖注入"——这就是所谓IoC的本质。

容器中Bean的作用域

当通过Spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下五种作用域:

  1. singleton: 单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
  2. prototype: 每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
  3. request: 对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
  4. session:该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
  5. global session: 每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。

如果不指定Bean的作用域,Spring默认使用singleton作用域。prototype作用域的Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成果,就可以重复使用。因此,应该尽量避免将Bean设置成prototype作用域。

创建Bean的3种方式

一.使用构造器创建Bean实例

使用构造器来创建Bean实例是最常见的情况,如果不采用构造注入,Spring底层会调用Bean类的无参数构造器来创建实例,因此要求该Bean类提供无参数的构造器。

采用默认的构造器创建Bean实例,Spring对Bean实例的所有属性执行默认初始化,即所有的基本类型的值初始化为0或false;所有的引用类型的值初始化为null

例子如下

例子<bean id = "person " class = "com.itxiaobai.user.Person" />

二.使用静态工厂方法创建Bean

使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类,Spring通过该属性知道由哪个工厂类来创建Bean实例。

除此之外,还需要使用factory-method属性来指定静态工厂方法,Spring将调用静态工厂方法返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例完全一样。如果静态工厂方法需要参数,则使用<constructor-arg.../>元素指定静态工厂方法的参数。

例如有一个工厂类

public class PersonFactory {
    public static person getPerson(){
        return new Person();
    }
}

那么想要通过静态工厂方法创建Bean实例,配置bean时这样写

<bean id="person" class="com.itxaiobai.factory.PersonFactory" factory-method="getPerson"/>

三.调用实例工厂方法创建Bean

实例工厂方法与静态工厂方法只有一个不同:调用静态工厂方法只需使用工厂类即可,而调用实例工厂方法则需要工厂实例。使用实例工厂方法时,配置Bean实例的<bean.../>元素无须class属性,配置实例工厂方法使用factory-bean指定工厂实例。
采用实例工厂方法创建Bean的<bean.../>元素时需要指定如下两个属性:

  • factory-bean: 该属性的值为工厂Bean的id。
  • factory-method: 该属性指定实例工厂的工厂方法。

若调用实例工厂方法时需要传入参数,则使用<constructor-arg.../>元素确定参数值。

例子如下

有一个实例工厂

//实例工厂创建对象
public class PersonFactory {
    public UserDao getPerson(){
        return new Person();
    }
}

那么配置person的bean时要先配置实例工厂的bean然后通过工厂bean来创建person的bean

代码如下

<bean id="PFactory" class="com.itxaiobai.factory.PersonFactory"/>

    <bean id="person" factory-method="getPerson" factory-bean="PFactory"/>

解析,大概意思就是,一个创建Person对象的工厂类PersonFactory ,是实例工厂,所以想要调用其方法时必须是这个工厂类的实例对象.方法,这种形式去调用,所以在配置Person时需要先创建一个PersonFactory类型的bean,得到工厂类的对象,然后在配置Person时,加上工厂对象和工厂方法创建一个Person的bean 

发现问题:可以看出这种方式存在很大的问题,一是PFactory这个bean 没有什么意义,就是为了创建Person而配置的,其次就是factory-method 中的方法名是硬编程,所以就有了改良版的调用实例工厂方法创建bean

三.改良版的调用实例工厂方法创建bean

代码如下

//此工厂类继承了接口 FactoryBean后面泛型是想要创造的bean类型
public class PersonFactoryBean implements FactoryBean<Person> {
    //代替原始实例工厂中创建对象的方法
    public Person getObject() throws Exception {
        return new Person();
    }

    public Class<?> getObjectType() {
        return Person.class;
    }
    public boolean isSingleton() {
        return true;//创建对象为单例的 false 是非单例
    }

}

bean的配置就可以简化一下,代码如下

<bean id="person" class="com.itxaiobai.factory.PersonFactoryBean"/>

 这个是重点

bean的生命周期

在类中定义初始化和销毁方法init和destroy方法,然后在配置bean时,使用属性init-method 和destroy-method 并把类中定义的初始化方法和销毁方法传入

代码如下

public class Person {//一个Person类 里面有init方法和destroy方法

public void init(){
System.out.println("初始化方法被调用");
}
public void destroy(){
System.out.println("销毁方法被调用");
}
}

 在配置bean如下

<bean id = "person" class = "com.itxiaobai.user.Person" init-method ="init" destroy-method ="destory"/>

 bean的生命周期和Ioc容器有关系,加入Java 虚拟机在获取IoC容器后,主程序内没有close.ctx虚拟机会直接关掉,所以Ioc容器中的bean还没来得及执行销毁方法就没了

但是上述代码还可以再优化一下如下

class Person implements InitializingBean, DisposableBean {

    public void say(){
        System.out.println("Person say方法....");
    }
    //重写这两个方法
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet方法...");
    }

    public void destroy() throws Exception {
        System.out.println("destroy方法.....");
    }
}

配置Person类型的bean时就正常的给id值和class路径

<bean id="person" class="com.itheima.factory.Person"/>

 在获取Ioc容器后直接获取Person类型的bean

        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = ctx.getBean("person", Person.class);
        person.say();
        ctx.close();

 可以看出,只要继承了InitializingBean, DisposableBean这个两个接口并且重写afterPropertiesSet和destroy方法,在获取对应bean时就会自动执行这两个方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值