Spring 学习笔记(一)IOC

1. IOC(Inversion of Control)与DI(Dependency Injection)

“控制反转”“依赖注入”其实是同一个概念:
改变由调用者创建被调用者的过程。被调用者依赖Spring容器注入到调用者中。
之所以出现两个名词是因为“控制反转”比较难懂,听者不明所以,所以有了更形象的“依赖注入”

2. IOC的发展历程

3. Spring容器

简单说,就是Spring中生成“Bean”的工厂,并管理“Bean”的生命周期。Spring的配置文件说到底就是“Bean”的配置。

“Bean”是Spring管理的基本单位,不仅仅指简单的POJO类,组件也是“Bean”,例如数据源、事务管理器,Hibernate的SessionFactory。

Spring中内置了两种基础DI容器:BeanFactoryApplicationContext
ApplicationContext继承了BeanFactory的所有特性。关系如下图:


BeanFactory应用与内存、CPU资源受限的场合,一般情况下优先使用ApplicationContext,因为它除了拥有BeanFactory支持的所有功能,还可以:

  1. 载入多个配置文件
  2. 提供国际化支持
  3. 事件机制
  4. 默认初始化所有的singleton BeanBeanFactory则不会)

常用的ApplicationContext的子类如下图:

  • XmlWebApplicationContext:面向Web应用
  • AnnotationConfigApplicationContext:基于注解存储DI元数据
  • XmlPortlerApplicationContext:面向Portal应用
  • ClassPathXmlApplicationContext、FileSystemXmlApplicationContext:适用于各种场景

4. 注入方式

Spring的注入方式有三种:设值注入、构造器注入、工厂注入。

设置注入和构造器注入

实体类:

public class Person{
    public String name;
    private Axe axe;

    public Person(Axe axe, String name) {
        System.out.println("Person 的带参构造方法");
        this.axe = axe;
        this.name = name;
    }
    /**
     * 砍柴
     */
    public void choopWood() {
        if(axe!=null){
            axe.useAxe();           
        }
    }

    public void info() {
        System.out.println("This person's name is " + name);
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAxe(Axe axe) {
        this.axe = axe;

    }

}

注入类:

public interface Axe {
    
    public void useAxe();
}

public class StoneAxe implements Axe {

    public void useAxe() {
        System.out.println(test + "石斧砍柴很慢");
    }
}

public class IronAxe implements Axe{

    public void useAxe() {
        System.out.println("铁斧砍柴很快");
    }

}

Spring配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束(.xsd都是语言约束文件)
    xmlns:p配置可使用p标签更方便的配置属性 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
     xmlns:p="http://www.springframework.org/schema/p"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- id 表示你这个组件的名字,class表示组件类;autowire为是否自动加载; lazy-init为是否懒加载,即applicationContext容器启动不自动加载 -->
    <bean id="person" class="test.wsz.spring.bean.Person" lazy-init="true" autowire="byType" >
        <!--自定义set注入,name 为成员变量名称,value为自定义的值 -->
        <property name="name" value="wsz"></property>
        <!--引用set注入,name 为成员变量名称,ref为注入bean的id -->
        <property name="axe" ref="ironaxe"></property>
        <!--构造注入,index为形参的位置,从0开始,ref为注入bean的id -->
        <constructor-arg ref="stoneaxe" index="0" /> 
        <constructor-arg value="wsz" index="1" />
    </bean>

    <!--其余实体Bean-->
    <bean id="ironaxe" class="test.wsz.spring.bean.IronAxe" />
    <bean id="stoneaxe" class="test.wsz.spring.bean.StoneAxe" />
    
</beans>

从配置的注释可以知道“设置注入”和“构造器注入”的配置方法。

  • “设置注入”会调用成员变量的setXXX方法。
  • “构造器注入”会调用类对应的构造方法。

测试代码:

ApplicationContext context=new ClassPathXmlApplicationContext("config/spring-config.xml");
Person person=context.getBean("person",Person.class);
person.info();
person.choopWood();

工厂方式注入

工厂方式注入有好几种,这里只举“静态工厂注入”和“工厂Bean的例子”

  • 静态工厂注入

先创建一个工厂类:

/**
 * 静态简单工程类
 */
public class AxeFactory {
    /**
     * 获取产品的方法,根据形参返回不同的产品
     */
    public static Axe getAxe(String type) {
        if("stone".equals(type))
            return new StoneAxe();
        
        else if("iron".equals(type))
            return new IronAxe();
        
        return new StoneAxe();
    }
}

再修改之前的类当成产品类:

public class StoneAxe implements Axe {
    private String test;

    
    public StoneAxe() {
        System.out.println("调用StoneAxe的无参构造方法");
    }

    public void useAxe() {
        System.out.println(test + "石斧砍柴很慢");
    }

    public void setTest(String test) {
        this.test = test;
    }
}

再在xml文件中进行对应的配置:

<!--静态工厂类获取Bean,class为工厂类;factory-method为工厂获取方法-->
    <bean id="stone" class="test.wsz.spring.factory.AxeFactory" factory-method="getAxe">
        <!--设置传入工厂类获取产品方法形参的值-->
        <constructor-arg value="stone"/> 
        <!--设置注入产品Bean的属性-->
        <property name="test" value="哈哈,"></property>
    </bean>

根据配置我们可以知道,现在设置的工厂类返回StoneAxe,我们进行测试:

Axe axe=context.getBean("stone",Axe.class);
axe.useAxe();

输出:

哈哈,石斧砍柴很慢
  • 工厂Bean注入
    这种方式配置比较简单。只需要创建一个实现了Spring工厂接口的工厂类,再将这个工厂类配置在<Beans />中即可。

工厂类,需实现接口的三个方法:

public class AxeBeanFactory implements FactoryBean<Axe>{
    Axe axe=null;
    /**
     * 返回产品
     */
    public Axe getObject() throws Exception {
        if(axe==null){
            axe=new StoneAxe();
        }
        return axe;
    }
    /**
     * 返回产品的类型
     */
    public Class<?> getObjectType() {
        return Axe.class;
    }
    /**
     * 是否单例
     */
    public boolean isSingleton() {
        return true;
    }
}

xml配置:

<!--容器中的工厂Bean-->
    <bean id="stonefactory" class="test.wsz.spring.factory.AxeBeanFactory" />

值得注意的是,虽然<Bean />中配置的好像是工厂类,但实现了Spring工厂接口的该类,最终getBean()时返回的是对应的产品类。

测试代码:

Axe axe=context.getBean("stonefactory",Axe.class);
axe.useAxe();

输出:

null石斧砍柴很慢

5. 配置细节

Spring的配置的参数太多,这里只总结一些常用的。复杂的等用到时再看书就行。

<bean />中的常用配置

  • scope
    Bean的作用域,有5种:
  • singleton:单例模式,即整个容器里面该Bean实例只有一个,默认
  • prototype:原型模式,每次getBean获取的是一个新创建的对象
  • request:每个HTTP请求,产生一个实例
  • session:每个HTTP session,产生一个实例
  • global session:每个全局的HTTP session,产生一个实例
  • lazy-init:
    懒加载,当使用使用ApplicationContext时,会自动初始化所有的singleton的“Bean”(“Bean”默认是singleton)。设置懒加载即不自动初始化这个类。
  • autowire:
    自动注入。即你不用在配置中设定成员变量的注入。设置为byType时,会自动匹配依赖类型相同的Bean,若有多个类型相同的会抛出异常。设置为byName时,会查找id名与属性名相等的Bean。
  • init-method:
    指定Bean依赖关系设置完毕后执行的方法。另外一种实现方式是让Bean类实现InitializingBean接口
  • destory-method
    指定Bean销毁之前执行的方法。另外一种实现方式是让Bean类实现DisposableBean接口
  • p名称空间
    可以看作一个语法糖,在<beans />中添加:
xmlns:p="http://www.springframework.org/schema/p"  

即可更简单的设置注入:

<bean id="person" class="test.wsz.spring.bean.Person"
      p:name="name" />

等效于

<bean id="person" class="test.wsz.spring.bean.Person" >
       <property name="name" value="wsz"></property>
</bean>

6. 免XML的JAVA类配置

这个其实应用场景比较少,毕竟单纯用Java类完成复杂的Spring配置还是比较困难的。下面简单给个例子:

@Configuration
public class Config {
    
    @Value("王树政") String personName;   //注入Bean的属性中的值
    @Bean(name="person")
    public Person person(){
        Person p=new Person();
        p.setName(personName);
        p.setAxe(stoneAxe());
        return p;
    }
    @Bean(name="stoneAxe")
    public Axe stoneAxe() {
        Axe axe=new StoneAxe();
        
        return axe;
    }   
}

除了纯粹的Java类配置,还可以在xml中引用Java类配置,或者在Java类配置中引用xml配置。有兴趣的朋友可以自行百度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容