1. 程式人生 > >【一步一步學習spring】spring bean管理(上)

【一步一步學習spring】spring bean管理(上)

proto id屬性 table handle isp 基礎上 turn 聲明 設置方法

1. spring 工廠類

技術分享圖片

我們前邊的demo中用到的spring 工廠類是ClassPathXmlApplicationContext,從上圖可以看到他還有一個兄弟類FileSystemApplicationContext,這個類是加載非classpath路徑下的配置文件,本質是一樣的。

從繼承關系圖中可以看到我們經常看到的ApplicationContext,這個是新版本才引入的工廠類接口。在舊版本中是BeanFactory,是在舊版本的基礎上增加了幾個新功能,如國際化,resourceLoader等。還有一個區別就是加載bean的時機的區別,Beanfactory是在使用bean的時候創建的;ApplicationContext是在加載配置文件的時候就將所有非懶加載的單例創建出來了。

@Test
/**
* 使用beanFactory加載bean
*/
public void demo3() {
    // 老方式的工廠類,需要自己創建Resource對象進行加載。
    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("application-context.xml"));
    // 通過工廠獲取類對象
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.sayHello();
}

2. spring bean管理

2.1 三種實例化Bean的方式

  • 使用類構造器實例化(默認無參數)
  • 使用靜態工廠方法實例化(簡單工廠模式)
  • 使用實例工廠模式實例化(工廠方法模式)

一般情況都是用第一種方式,只有類的構造非常復雜的情況下才會用後邊的倆種。

2.1.1 使用類構造器實例化

  • bean
package com.ioc.demo2;

/**
 * Bean實例化的三種方式:采用無參的構造方法的方式
 */
public class Bean1 {
    public Bean1() {
        System.out.println("Bean1 被實例化了。");
    }
}
  • xml
<!-- 無參數構造方法構建 -->
<bean id="bean1" class="com.ioc.demo2.Bean1"></bean>
  • 調用
@Test
public void demo1() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
    Bean1 bean = (Bean1) applicationContext.getBean("bean1");
}

2.1.2 靜態工廠方法實例化

  • bean
/**
 * Bean的實例化三種方式:靜態工廠實例化方式
 */
public class Bean2 {
}
  • beanFactory
/**
 * Bean2的靜態工廠
 */
public class Bean2Factory {
    
    public static Bean2 createBean2() {
        System.out.println("Bean2Factory 執行了。");
        return new Bean2();
    }
}
  • xml
<!-- 靜態工廠的方式 -->
<bean id="bean2" class="com.ioc.demo2.Bean2Factory" factory-method="createBean2"></bean>
  • 調用
@Test
public void demo2() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
    Bean2 bean = (Bean2) applicationContext.getBean("bean2");
}

2.1.3 使用實例工廠模式實例化

  • bean
/**
 * 使用實例工廠模式實例化
 */
public class Bean3 {
}
  • beanFactory
/**
 * Bean3的實例工廠
 */
public class Bean3Factory {
    public Bean3 createBean3() {
        System.out.println("Bean3Factory 被調用了。");
        return new Bean3();
    }
}
  • xml
<!-- 實例工廠的方式 -->
<bean id="bean3Factory" class="com.ioc.demo2.Bean3Factory"></bean>
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>
  • 調用
@Test
public void demo3() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");
    Bean3 bean = (Bean3) applicationContext.getBean("bean3");
}

2.2 bean的常用配置

2.2.1 id和name

  • 一般情況下,裝配一個Bean時,通過指定一個id屬性作為Bean的名稱
  • id屬性在IOC容器中是唯一的
  • name其實也要是唯一的,同id的區別是可以存在特殊字符。這個應該是有歷史原因的,不再贅述。

2.2.2 class

  • 設置一個類的全路徑的名稱,主要作用是IOC容器生成類的實例

2.2.3 scope

類別 說明
singleton(默認) 在SpringIOC容器中僅存在一個Bean實例,Bean以單實例的方式存在
prototype 每次調用getBean()時都會返回一個新的實例
request 每個HTTP請求都會創建一個新的Bean,該作用域僅適用於WebApplicationContext環境
session 同一個HTTP session共享一個Bean,不同的HTTP session使用不同的Bean。該作用域僅適用於WebApplicationContext環境

2.3 spring容器中Bean的生命周期

spring初始化bean或銷毀bean時,有時候需要做一些處理工作,因此spring可以在創建和銷毀bean的時候調用bean的聲明周期方法。

<bean id="xxx" class="...Yoo" init-method="init" destory-method="destory" />

當bean在被載入到容器中時會調用init;當bean從容器中刪除的時候調用destroy(scope=singleton有效)

2.3.1 instantiate bean對象實例化

即調用類的構造方法。

2.3.2 populate properties封裝屬性

即調用類的屬性設置方法。

2.3.3 如果Bean實現BeanNameAware,則執行setBeanName

即為該bean設置name的時候調用,可以想象為容器為map,name就是那個key。

2.3.4 如果Bean實現BeanFactoryAware或者ApplicationContextAware設置工廠,則執行setBeanFactory或者上下文對象setApplicationContext

即為該bean設置容器的時候調用。

2.3.5 如果存在類實現BeanPostProcessor(後處理Bean),執行postProcessBeforeInitialization

即在調用init-method之前調用。

2.3.6 如果bean實現InitializingBean,則實行afterPropertiesSet

屬性設置後調用。

2.3.7 調用<bean init-method="init">指定的初始化方法

執行init方法。

2.3.8 如果存在類實現BeanPostProcessor(後處理Bean),執行postProcessAfterInitialization

即在調用init-method之後調用。

2.3.9 執行業務處理

即調用bean中的業務方法。

2.3.10 如果Bean實現DisposableBean,執行destory方法

spring自身中的銷毀方法。

2.3.11調用<bean destory-method="teardown">指定的銷毀方法

執行teardown方法。

2.3.12 相關代碼

  • bean
package com.ioc.demo3;

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 Bean implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean{
    private String name;
    public Bean() {
        System.out.println("第一步:構造方法");
    }
    public void setName(String name) {
        System.out.println("第二步:設置屬性");
        this.name = name;
    }
    public void setBeanName(String name) {
        System.out.println("第三步:設置bean的名稱:" + name);
    }
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第四步:設置bean工廠");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("第六步:屬性設置後");
    }
    public void init() {
        System.out.println("第七步:初始化方法");
    }
    public void run() {
        System.out.println("第九步:業務代碼");
    }
    public void destroy() throws Exception {
        System.out.println("第十步:spring自身的銷毀");
    }
    public void teardown() {
        System.out.println("第十一步:銷毀方法");
    }
}
  • MyBeanPostProcessor
package com.ioc.demo3;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {

    // 可以用beanName對要生效的bean做過濾操作
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第四步:bean初始化前");
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第八步:bean初始化後方法");
        return bean;
    }

}
  • xml
<!-- Bean的聲明周期============================================ -->
<bean id="bean" class="com.ioc.demo3.Bean" init-method="init" destroy-method="teardown">
    <property name="name" value="xxx"></property>
</bean>
<!-- 註意這個配置是對所有的bean都生效的 -->
<bean class="com.ioc.demo3.MyBeanPostProcessor"></bean>
  • 調用
package com.ioc.demo3;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo3 {
    
    @Test
    public void demo1() {
        // ApplicationContext接口沒有close方法,故直接使用了其實現類
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        Bean bean = (Bean)context.getBean("bean");
        bean.run();
        context.close();
    }
}
  • 執行結果
第一步:構造方法
第二步:設置屬性
第三步:設置bean的名稱:bean
第四步:設置bean工廠
第四步:bean初始化前
第六步:屬性設置後
第七步:初始化方法
第八步:bean初始化後方法
第九步:業務代碼
第十步:spring自身的銷毀
第十一步:銷毀方法

2.3.13 使用聲明周期中的BeanPostProcessor增強類中的方法

這個本質上是可以對類中的方法做動態代理,為後邊的AOP做鋪墊。

下邊舉個栗子:在delete方法前面加一個鑒權的功能,不更改具體的類方法

  • 接口類
package com.ioc.demo3;

public interface BeanDao {
    
    public void find();
    public void add();
    public void delete();
    public void update();

}
  • 接口實現類
package com.ioc.demo3;

public class BeanDaoImpl implements BeanDao {

    public void find() {
        System.out.println("查詢");
    }

    public void add() {
        System.out.println("添加");
    }

    public void delete() {
        System.out.println("刪除");
    }

    public void update() {
        System.out.println("更新");
    }

}
  • BeanPostProcessor
package com.ioc.demo3;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("第四步:bean初始化前");
        return bean;
    }

    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("第八步:bean初始化後方法");
        if (beanName.equals("beanDao")) {
            Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getName().equals("delete")) {
                        System.out.println("鑒權是否是root用戶");
                        return method.invoke(bean, args);
                    }
                    return method.invoke(bean, args);
                }
            });
            return proxy;
        }
        return bean;
    }

}
  • xml
<bean class="com.ioc.demo3.MyBeanPostProcessor"></bean>
<bean id="beanDao" class="com.ioc.demo3.BeanDaoImpl"></bean>
  • 調用
package com.ioc.demo3;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo3 {
    
    @Test
    public void demo1() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        BeanDao beanDao = (BeanDao)context.getBean("beanDao");
        beanDao.add();
        beanDao.delete();
        beanDao.find();
        beanDao.update();
    }
}

【一步一步學習spring】spring bean管理(上)