Spring IoC的一点总结

1. 前言

Spring IoC(Inversion of Control,控制反转)和 AOP(Aspect Orient Programming,面向切面编程)是Spring的两大核心,本文聊聊对Spring Ioc的一点点理解。

IoC又称为DI(Dependency Injection,依赖注入),当A对象依赖B对象时,并不是由A对象直接创建B对象,而是由Spring创建B对象,并注入到A对象中,最终实现了解耦。

2. 依赖注入的方式

依赖注入的方式常用的包括:构造器注入、属性(setter)注入,其中最常用的是属性(Setter)注入。在讨论依赖注入之前,我们先来认识@Resource和@Autowired注解。

2.1 @Resource和@Autowired

  1. @Resource和@Autowired都可以用来装配Bean,二者都可以作用在类的属性(域)和setter方法上;@Autowired还可以作用在构造器上,@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容器执行;在方法上加上注解@PreDestroy,这个方法就会在Bean销毁前被Spring容器执行。

2.2 构造器注入

在构造器上使用@Autowired和@Qualifier注入依赖Bean。前面已经提到了,@Resource不能作用在构造器上。

@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负责创建这些Bean的实例,管理Bean的生命周期,也管理Bean和Bean之间的依赖关系。

Spring容器最核心的两个接口是BeanFactory和ApplicationContext。BeanFactory负责配置、创建和管理Bean;ApplicationContext继承了BeanFactory接口,被称为Spring上下文。观察Spring Boot Web工程的启动日志可以发现,使用的是AnnotationConfigEmbeddedWebApplicationContext,看名字就知道是ApplicationContext的一个实现。

BeanFactory包含的基本方法

// 根据Bean的name返回Bean对象
Object getBean(String name) throws BeansException;

// 根据Bean的name和requiredType返回Bean对象
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

// 根据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*. 调用BeanNameAware的setBeanName()                   
        ↓                                                   
4*. BeanFactoryAware的setBeanFactory()                     
        ↓                                                  
5*. 调用BeanPostProcessors的ProcessBeforeInitialization()   
        ↓
6*. 调用InitializingBean的afterPropertiesSet()
        ↓
7. 调用调用Bean定义的init-method
        ↓
8*. BeanPostProcessors的ProcessaAfterInitialization()
        ↓
[上面是Bean的创建阶段]
[Bean的正常使用阶段]
[下面是Bean的销毁阶段,例如容器销毁的时候]
            
9. 调用DisposableBean的destroy()
        ↓
10. 调用Bean中自定义的destroy-method

其中,Bean自身的方法包括:本身正常使用的方法,通过<bean>或者@Bean配置的init-method和destroy-method方法。在一般的开发过程中,我们只需要关心Bean自身的方法即可。

剩余的都是Bean级别的生命周期的接口方法,包括BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法,只有Bean实现了这些接口,才会在生命周期中执行接口的相关方法。

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: 根据属性名自动装配。
  3. byType: 根据属性的类型自动装配

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关键字创建了Bean的实例,一旦创建成功,容器不再拥有该Bean的引用,完全交由调用方管理该对象的生命周期,包括对象的销毁。
  3. 除此以外,还有request、session、application、globalSession,只适用于web程序。大体上,request、session、application分别对应servlet规范中三种scope:request、session、application;globalSession只应用于基于Portlet规范的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,用于标注业务层组件。

  • @Repository,用于标注数据访问组件。

除此之外,注册Bean还会用到另外两个注解,分别是@Configuration和@Bean。这两个注解通常用于注册配置信息,或者把引入的类注册到Spring 容器中,例如数据源的注册、外部jar中Servlet、Filter等的注册。下面看一个向Spring 容器注册Druid数据库连接池监控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优先于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. 总结

以上是对Spring IoC的一些总结,到了最后不要忘记IoC的宗旨,它就是为了实现对象之间的松耦合。凡事有得必有失,IoC容器生成对象通过反射方式,在运行效率上有一定的损耗,同时也增加了不少的配置工作。

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

推荐阅读更多精彩内容