Spring IoC的一點總結(jié)

1. 前言

Spring IoC(Inversion of Control,控制反轉(zhuǎn))和 AOP(Aspect Orient Programming,面向切面編程)是Spring的兩大核心,本文聊聊對Spring Ioc的一點點理解。

IoC又稱為DI(Dependency Injection,依賴注入),當A對象依賴B對象時,并不是由A對象直接創(chuàng)建B對象,而是由Spring創(chuàng)建B對象,并注入到A對象中,最終實現(xiàn)了解耦。

2. 依賴注入的方式

依賴注入的方式常用的包括:構(gòu)造器注入、屬性(setter)注入,其中最常用的是屬性(Setter)注入。在討論依賴注入之前,我們先來認識@Resource和@Autowired注解。

2.1 @Resource和@Autowired

  1. @Resource和@Autowired都可以用來裝配Bean,二者都可以作用在類的屬性(域)和setter方法上;@Autowired還可以作用在構(gòu)造器上,@Resource則不可以。

  2. @Resource為JSR-250標準的注解,屬于J2EE的,使用該注解可以減少代碼和Spring的耦合;@Autowired是Spring定義的注解。

  3. @Autowired默認按照類型來裝配Bean,默認情況下,必須要求依賴對象存在;如果要允許null,可以設置它的required屬性為false,例如@Autowired(required=false) 。如果想要使用name(名稱)來裝配Bean,需要配合@Qualifier一起使用。下面是@Autowired使用的例子。

     @Autowired(required = true) 
     @Qualifier("baseDao")
     private BaseDao baseDao;
    
  4. @Resource默認按照名稱來裝配Bean,如若沒有指定名稱,則按照屬性名稱來查找;當無法通過名稱找到匹配的Bean時,才按照類型裝配。如果name屬性一旦指定,就只會按照名稱來裝配Bean。下面是@Resource使用的例子。

     @Resource(name="baseDao")
     private BaseDao baseDao;
    

和@Resource相關的,還有兩個注解,分別是@PostConstruct和@PreDestroy。在方法上加上注解@PostConstruct,這個方法就會在Bean初始化之后被Spring容器執(zhí)行;在方法上加上注解@PreDestroy,這個方法就會在Bean銷毀前被Spring容器執(zhí)行。

2.2 構(gòu)造器注入

在構(gòu)造器上使用@Autowired和@Qualifier注入依賴Bean。前面已經(jīng)提到了,@Resource不能作用在構(gòu)造器上。

@Repository
public class CustomerDao extends SqlMapClientDaoSupport {

    @Autowired(required = true)
    public CustomerDao(@Qualifier(value="sqlMapClient4A") SqlMapClient sqlMapClient) {
        super.setSqlMapClient(sqlMapClient);
    }
}

2.3 屬性注入(setter注入)

在類的域上,或者該域的setter方法上使用@Autowired或者@Resource注解,注入依賴對象。

@Service
public class UserService {

    @Resource(name="userDao")
    private UserDao userDao;

    public User QueryUser(String id){
        return userDao.selectById(id);
    }
    /* setter注入要多寫一個方法,沒有屬性注入寫著方便。
    @Resource(name="userDao")
    public UserDao setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    */
}

3. Spring容器

Spring容器管理的基本單位是Bean,Bean可以是任何的java對象。Spring負責創(chuàng)建這些Bean的實例,管理Bean的生命周期,也管理Bean和Bean之間的依賴關系。

Spring容器最核心的兩個接口是BeanFactory和ApplicationContext。BeanFactory負責配置、創(chuàng)建和管理Bean;ApplicationContext繼承了BeanFactory接口,被稱為Spring上下文。觀察Spring Boot Web工程的啟動日志可以發(fā)現(xiàn),使用的是AnnotationConfigEmbeddedWebApplicationContext,看名字就知道是ApplicationContext的一個實現(xiàn)。

BeanFactory包含的基本方法

// 根據(jù)Bean的name返回Bean對象
Object getBean(String name) throws BeansException;

// 根據(jù)Bean的name和requiredType返回Bean對象
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

// 根據(jù)requiredType返回Bean對象
<T> T getBean(Class<T> requiredType) throws BeansException;

// 判斷Spring容器是否包含了key為name的Bean。
boolean containsBean(String name);

Bean的生命周期 如下所示。

1. Instantiate 實例化一個Bean
        ↓
2. Populate properties 設置Bean的屬性值
        ↓
3*. 調(diào)用BeanNameAware的setBeanName()                   
        ↓                                                   
4*. BeanFactoryAware的setBeanFactory()                     
        ↓                                                  
5*. 調(diào)用BeanPostProcessors的ProcessBeforeInitialization()   
        ↓
6*. 調(diào)用InitializingBean的afterPropertiesSet()
        ↓
7. 調(diào)用調(diào)用Bean定義的init-method
        ↓
8*. BeanPostProcessors的ProcessaAfterInitialization()
        ↓
[上面是Bean的創(chuàng)建階段]
[Bean的正常使用階段]
[下面是Bean的銷毀階段,例如容器銷毀的時候]
            
9. 調(diào)用DisposableBean的destroy()
        ↓
10. 調(diào)用Bean中自定義的destroy-method

其中,Bean自身的方法包括:本身正常使用的方法,通過<bean>或者@Bean配置的init-method和destroy-method方法。在一般的開發(fā)過程中,我們只需要關心Bean自身的方法即可。

剩余的都是Bean級別的生命周期的接口方法,包括BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean這些接口的方法,只有Bean實現(xiàn)了這些接口,才會在生命周期中執(zhí)行接口的相關方法。

4. Bean的屬性

4.1 id

  • xml配置方式里,id是Bean的唯一標識。
  • 注解方式中,@Bean 里并沒有id這個屬性。

4.2 name

name是該Bean的一個或者多個別名,配置多個別名可以用“,”分割。當使用@Bean注解方式時,如果沒有配置name,那么默認使用方法名。

4.3 class

class 指定該Bean 的全限定名,例如com.example.dao.UserDao。這個屬性也是用于xml配置方式里,注解里自然用不到,因為被注解的方法上有class的信息。

4.4 autowire

  1. no: 不適用自動裝配。這是autowire的默認值。指必須顯示的指定依賴。
  2. byName: 根據(jù)屬性名自動裝配。
  3. byType: 根據(jù)屬性的類型自動裝配

4.5 initMethod

該Bean的初始化方法。

4.6 destroyMethod

該Bean的銷毀方法。

4.7 Scope

Scope用來聲明Bean的生存空間,最基本類型是singleton和prototype;如果我們沒有指定Bean的Scope類型,那么默認是singleton。

  1. singleton,單例。如果某個Bean的scope被設置成為singleton,那么Sping容器里只有一個實例,所有對該類型Bean的依賴,都引用這個單一實例。
  2. prototype,原型。容器在接收到該類型對象的請求的時候,Spring都會新建一個Bean的實例,并返回給程序。在這種情況下,Spring容器僅僅使用new關鍵字創(chuàng)建了Bean的實例,一旦創(chuàng)建成功,容器不再擁有該Bean的引用,完全交由調(diào)用方管理該對象的生命周期,包括對象的銷毀。
  3. 除此以外,還有request、session、application、globalSession,只適用于web程序。大體上,request、session、application分別對應servlet規(guī)范中三種scope:request、session、application;globalSession只應用于基于Portlet規(guī)范的Web程序,對應于portlet的global范圍的session。

5. Bean注冊的方式

Bean的注冊是指把Bean的信息注冊到Spring容器中,既可以通過xml配置的方式,也可以通過注解的方式。如果使用注解的方式把Bean信息注冊到Spring容器中,我們最熟悉的是:@Component。如果一個類使用了@Component注解,代表了這是Spring的一個Bean。@Controller、@Service和@Repository都和@Component等效。

  • @Component,泛指組件,當Bean不好歸類的時候,就可以使用這個注解。

  • @Controller,顧名思義,用于標記web層的Controller。同樣用于標記web層Controller的還有@RestController,它相當于是@Controller和@ResponseBody的組合。

  • @Service,用于標注業(yè)務層組件。

  • @Repository,用于標注數(shù)據(jù)訪問組件。

除此之外,注冊Bean還會用到另外兩個注解,分別是@Configuration和@Bean。這兩個注解通常用于注冊配置信息,或者把引入的類注冊到Spring 容器中,例如數(shù)據(jù)源的注冊、外部jar中Servlet、Filter等的注冊。下面看一個向Spring 容器注冊Druid數(shù)據(jù)庫連接池監(jiān)控Serlvet和Filter的例子。

@Configuration
public class DruidConfiguration {
  // Bean的name沒有配置,默認使用method的name。    
  @Bean
  public ServletRegistrationBean druidServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean();
    bean.setServlet(new StatViewServlet());
    bean.addUrlMappings("/druid/*");
    Map<String, String> initParameters = new HashMap<String, String>();
    initParameters.put("loginUsername", "admin");// 用戶名
    initParameters.put("loginPassword", "admin");// 密碼
    initParameters.put("resetEnable", "false");// 禁用HTML頁面上的“Reset All”功能
    initParameters.put("allow", ""); // IP白名單 (沒有配置或者為空,則允許所有訪問)
    //initParameters.put("deny", "192.168.20.38");// IP黑名單 (存在共同時,deny優(yōu)先于allow)
    bean.setInitParameters(initParameters);
    return bean;
  }

  @Bean
  public FilterRegistrationBean filterRegistrationBean() {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(new WebStatFilter());
    bean.addUrlPatterns("/*");
    bean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
    return bean;
  }

}

6. 總結(jié)

以上是對Spring IoC的一些總結(jié),到了最后不要忘記IoC的宗旨,它就是為了實現(xiàn)對象之間的松耦合。凡事有得必有失,IoC容器生成對象通過反射方式,在運行效率上有一定的損耗,同時也增加了不少的配置工作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容