02_Spring IOC(控制反转)

IOC的概念

  • IOC Inversion of Controller 控制反转。
  • IOC 就是将对象的创建、初始化及销毁交给 spring 容器来处理。

ApplicationContext 与 BeanFactory 的关系

  1. ApplicationContext 是 BeanFactory 的子接口。
  2. BeanFactory 采用延迟加载的方案,在getBean时才会实例化Bean。
    • XmlBeanFactory
    public void test4() {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        beanFactory.getBean("helloWorld");
    }
    // 经过测试,BeanFactory在getBean时才实例化Bean。
    
  3. ApplicationContext 在配置文件加载时,就会初始化Bean,并且提供不同应用层的实现。在开发中我们一般使用 ApplicationContext 的实现类:
    • FileSystemXmlApplicationContext 根据文件路径读取xml文件
    public void test5() {
        // 根据系统文件路径读取xml文件
        ApplicationContext context = new FileSystemXmlApplicationContext("src/applicationContext.xml");
        context.getBean("helloWorld");
    }
    
    • ClassPathXmlApplicationContext 根据classpath路径读取xml文件
    public void test5() {
        // 根据classpath路径读取xml文件
        ApplicationContext context = new FileSystemXmlApplicationContext("applicationContext.xml");
        context.getBean("helloWorld");
    }
    
    • WebApplicationContext web开发中常用

Bean的实例化方式

  1. 无参构造方法
    • 编写配置文件 applicationContext.xml
    <?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">
        <bean id="helloWorld" class="com.zhangquanli.spring.helloworld.HelloWorld"/>
    </beans>
    
    • 编写测试方法
    public void test1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
        helloWorld.show();
    }
    
  2. 静态工厂方法
    • 编写工厂类,在工厂类中提供一个静态方法,返回Bean对象
    public class HelloWorldFactory {
        public static HelloWorld getInstance() {
            return new HelloWorld();
        }
    }
    
    • 编写配置文件 applicationContext.xml
    <?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">
        <bean id="helloWorld2" class="com.zhangquanli.spring.helloworld.HelloWorldFactory" factory-method="getInstance"/>
    </beans>
    
    • 编写测试方法
    public void test2() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld2");
        helloWorld.show();
    }
    
  3. 实例工厂方法
    • 编写工厂类,在工厂类中提供一个非静态方法,返回Bean对象
    public class HelloWorldFactory2 {
        public HelloWorld getInstance() {
            return new HelloWorld();
        }
    }
    
    • 编写配置文件 applicationContext.xml
    <?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">
        <bean id="helloWorldFactory2" class="com.zhangquanli.spring.helloworld.HelloWorldFactory2"/>
        <bean id="helloWorld3" factory-bean="helloWorldFactory2" factory-method="getInstance"/>
    </beans>
    
    • 编写测试方法
    public void test3() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld3");
        helloWorld.show();
    }
    

Bean的别名

  1. 编写配置文件
<?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">
    <bean id="helloWorld" class="com.zhangquanli.spring.helloworld.HelloWorld"/>
    <alias name="helloWorld" alias="a"/>
    <alias name="helloWorld" alias="b"/>
    <alias name="helloWorld" alias="c"/>
</beans>
  1. 编写测试方法
public void testAlias() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    HelloWorld a = (HelloWorld) context.getBean("a");
    a.show();
    HelloWorld b = (HelloWorld) context.getBean("b");
    b.show();
    HelloWorld c = (HelloWorld) context.getBean("c");
    c.show();
}

Bean的创建时机

  1. 在 bean 标签中有 lazy-init 属性
    • default,相当于 false,在 spring 容器启动的时候创建对象。
    • true,在 context.getBean 时创建对象。
    • false,在 spring 容器启动的时候创建对象。
  2. lazy-init 属性的意义
    • 如果把 lazy-init 设置为 true ,则当 spring 容器启动的时候,检测不到任何错误,这样会存在很大的安全性隐患。所以一般情况应该设置 lazy-init 为 false/default 。
    • 但是如果一个bean中有一个属性,该属性含有大量的数据,这个时候不希望该bean过早的停留在内存中,这个时候需要用到 lazy-int 为 true 。

Bean的作用域

  1. 在 bean 标签中有 scope 属性,用于描述 bean 的作用域。
    • singleton,单例模式,代表在 spring ioc 容器中只有一个 bean 实例。(默认的scope)
    • prototype,多例模式,每一次从 spring ioc 容器中获取,都会返回一个新实例。
    • request,用在web开发中,通过 request.setAttribute() 将 bean 对象存储到request域中。
    • session,用在web开发中,通过 session.setAttribute() 将 Bean 对象存储到session域中。
  2. 默认情况下,放入spring容器中的bean是单例的。
    • 将来service层和dao层所有的类将放入到spring容器中,所以默认情况下这两个层的类的实例都是单例的,所以不能把数据声明到属性中。如果声明到属性中,将会成为共享的,涉及到线程安全问题。

创建时机和作用域的结合

  1. <font color="red">scope="prototype" lazy-init="true"</font> 在 context.getBean 时创建对象
  2. <font color="red">scope="prototype" lazy-init="false"</font> 在 context.getBean 时创建对象,lazy-init为false失效。即在 scope 为 prototype 时,始终在 context.getBean 时创建对象
  3. scope为singleton时,是默认情况。

Bean的生命周期

  1. Bean的生命周期方法
    • instantiate bean 实例化 Bean 对象
    • populate properties 给 Bean 对象注入属性
    • 如果 Bean 实现 BeanNameAware 执行 setBeanName
    • 如果 Bean 实现 BeanFactoryAware 或 ApplicationContextAware 执行 setBeanFactory 或 setApplicationContext
    • 如果存在类实现 BeanPostProcessor 执行postProcessBeforeInitialization
    • 如果 Bean 实现 InitializingBean 执行 afterPropertiesSet
    • 调用 Bean 中自定义的 init-method 方法
    • 如果存在类实现 BeanPostProcessor 执行postProcessorAfterInitialization
    • 执行业务逻辑代码
    • 如果 Bean 实现 DisposableBean 执行 destroy
    • 调用 Bean 中自定义的 destroy-method 方法
  2. Bean的生命周期测试代码
    • HelloWorld.java
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.BeanNameAware;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class HelloWorld implements BeanNameAware,ApplicationContextAware,InitializingBean,DisposableBean {
    
        private String info;
    
        public String getInfo() {
            return info;
        }
    
        public void setInfo(String info) {
            this.info = info;
        }
    
        public HelloWorld() {
            System.out.println("第一步:instantiate bean 实例化Bean对象");
        }
    
        public void show() {
            System.out.println("第九步:show time 执行业务逻辑代码");
        }
    
        public void myInit() {
            System.out.println("第七步:调用 Bean 中自定义的 init-method 方法");
        }
    
        public void myDestroy() {
            System.out.println("第十一步:调用 Bean 中自定义的 destroy-method 方法");
        }
    
        @Override
        public void setBeanName(String arg0) {
            System.out.println("第三步:如果 Bean 实现 BeanNameAware 执行 setBeanName" + info);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext arg0) throws BeansException {
            System.out.println("第四步:如果 Bean 实现 BeanFactoryAware 或 ApplicationContextAware 执行 setBeanFactory 或 setApplicationContext");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("第六步:如果 Bean 实现 InitializingBean 执行 afterPropertiesSet");
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("第十步:如果 Bean 实现 DisposableBean 执行 destroy");
        }
    
    }
    
    • MyProcessor.java
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyProcessor implements BeanPostProcessor{
    
        @Override
        public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException {
            System.out.println("第八步:如果存在类实现 BeanPostProcessor 执行postProcessorAfterInitialization");
            return arg0;
        }
    
        @Override
        public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException {
            System.out.println("第五步:如果存在类实现 BeanPostProcessor 执行postProcessBeforeInitialization");
            return arg0;
        }
    
    }
    
    • applicationContext.xml
    <?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">
        
        <bean id="helloWorld" class="com.zhangquanli.spring.life.HelloWorld" init-method="myInit" destroy-method="myDestroy">
            <property name="info" value="你好啊"></property>
        </bean>
        <!-- 此类是针对所有其他bean类的 -->
        <bean id="myProcessor" class="com.zhangquanli.spring.life.MyProcessor"/>
    
    </beans>
    
    • HelloWorldTest
    import org.junit.Test;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class HelloWorldTest {
        @Test
        public void test1() {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            HelloWorld helloWorld = (HelloWorld) context.getBean("helloWorld");
            helloWorld.show();
            context.close();
        }
    }
    
  3. Bean的生命周期的说明
    • 第3步和第4步,是让 Bean 了解 spring 容器。
    • 第5步和第8步,可以针对指定 的Bean 使用动态代理进行功能增强。
    • 第6步和第10步,可以实现指定的接口来完成 init 和 destroy 操作。
    • 在开发中,一般不使用第6步和第10步,因为第7步和第11步也可以完成 init 和 destroy 的操作。同时,第7步和第11步的初始化和销毁操作无耦合,只需要在配置文件制定初始化和销毁的方法。
    <bean id="helloWorld" class="com.zhangquanli.spring.life.HelloWorld" init-method="myInit" destroy-method="myDestroy">
        <property name="info" value="你好啊"></property>
    </bean>
    
  4. Bean的生命周期的总结
    • 增强 Bean 的功能,可以实现 BeanPostProcessor 来完成。
    • 初始化和销毁操作,可以使用 bean 标签上的 init-method、destroy-method 方法来完成。
    • <font color="red">注意:destroy-method 只在 scope="singleton" 才有效果。</font>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容