无简介
容器
容器应该能够向该环境中所管理的组件提供一些基本服务:
- 生命周期管理
- 依赖解析
- 组件查找
- 应用程序配置
此外,如果容器还可以提供下面的中间件服务,就更好了
- 事务管理
- 安全性
- 线程管理
- 对象和资源池
- 对组件的远程访问
- 通过JMX之类的API管理组件
- 容器的扩展和定制
依赖注入原则
依赖注入的原则是应用程序对象不应该负责查找他们所依赖的资源或协作者,而是应该由IoC容器处理对象的创建和依赖注入,从而使资源查找外部化,将过程从应用程序代码转移到容器
循环引用
A引用B,B引用A,先创建A 先创建A,此时发现要B,那么去创建B,发现又要A,那么直接就把创建中的A放到了B中 如果要引用的Bean是创建中的,那就把这个创建中的Bean直接注入到带注入的Bean中,所以B中的Bean A是创建中的一个Bean,是不完整的Bean
依赖注入的好处
- 可以从应用程序代码中完全删除查找逻辑代码,依赖项可以以一种可插拔的方式注入到目标组件中.
- 组件的单元测试将会变得非常容易(以前在代码中查找的时候要先将那些组件给启动了,现在由容器控制了,只要启动容器就好了)
- 组件中没有对具体的类的依赖,所以不需要修改任何代码,就可以非常容易的针对不同环境进行应用程序的配置(其实就是RocketMq替换为RabbitMq不需要对代码进行大规模修改就是了)
- 对容器的API没有任何依赖,可以更换容器(在使用spring的时候,如果要切换成其他的容器,只要修改配置文件就好了,代码中的依赖注入会由新的容器去完成,侵入性很小了)
- 不需要实现任何特殊的接口,直接写业务就可以了(编写的类只是一个普通的类,如果用到以前的方式-注入查找,需要继承一个类然后实现它里面的多个回调方法)
两种注入方式的优缺点对比
setter注入
- 优点:
- 在组件被创建之后可以重新进行配置 ( 因为在创建的时候在类中需要显示的声明setter方法,所以在运行的时候其实是可以由外部调用者主动替换掉这个类的 )
- 可以处理循环依赖,但某些功能并不能用循环依赖去解决,因为注入到其中的可能是postprocess状态的
- 缺点:
并不是所有所需的依赖都可以在使用前被注入,从而使组件处于一种部分配置的状态 ( 在一些情况下,setter方法的调用顺序可能非常重要,但是该顺序无法在组件约定中表达出来 )
构造函数注入
- 优点:
可以保证容器中每一个被管理的组件都处于一致状态,在创建之后可以马上使用 代码量相对setter要少
- 缺点:
组件创建完毕后就无法对其进行修改了
依赖注入
autowire-candidate属性
如果一个或多个bean都设置autowire-candidate=“false”,那么如果在代码中使用@autowire的时候就不会有bean注入进去了
创建Bean的两个阶段
- 处理配置元数据并建立Bean的定义,过程中还会对这些Bean定义进行验证 (此时Bean没有创建,属性也没有赋值进去)
- 完成Bean的创建,完成依赖注入 (此时也不是所有的Bean都创建了)
- 一个Bean在被完全创建且自己的依赖项被注入之前是不会作为一个依赖项被注入到其他Bean中的
Bean被覆盖的两种原因
- 后创建的bean覆盖先创建的bean
- 子ApplicationContext中的Bean会覆盖掉父ApplicationContext中的Bean
容器中一般有两个ApplicationContext
一个父,一个子.父由ContextLoadListener来创建,子由DispatcherServlet来创建
xml中setter注入注意事项
xml定义setter注入property后面的name一定要是set方法搞出来的bean的名字
比如说setAccountDao,那么property后面的name一定要是accountDao,ref倒是无所谓
获取ApplicationContext实例
- 通过WebApplicationContextUtils
- 通过实现ApplicationContextAware接口,并实现下面的方法,即可在要使用的地方获得到ApplicationContext
public void setApplicationContext(ApplicationContext applicationContext)
bean重名
多个xml中定义了多个相同的bean名称,会出现bean覆盖的现象,解决方法可以有两种:
- 使用name属性来替代id,在name属性中可以用逗号来分隔,除第一个名称之外都称之为alias
- 使用alias标签,且可以设置多个.如:
Bean的实例化方法
- setter注入
- 构造方法注入
- 工厂注入
- 静态方法注入
public static Foo createFoo();
- 普通方法注入
public Foo createFoo(); 实现FactoryBean接口,并实现以下方法
public Foo getObject();
public Class<?> getObjectType(); //返回一个Foo.class
public boolean isSingleton();
Bean作用域
- 单例 (singleton) 整个生命周期中只创建一次
- 原型 (prototype) 每一次调用都会创建一个
- 如果在一个单例的bean A中每次调用都想要使用一个新的B,那么A必须设置为singleton,而B设置为prototype,但是A在创建的时候就已经将B放进去了,在整个生存期中只会注入这么一次,所以A中的B是不变的.这个时候就要在A中通过Bean查找的方式来每次创建一个新的B实例了
- 如果是在Web应用程序中,还可以使用Request和Session两种作用域
延迟加载
- 优点:加快了启动速度
- 缺点:如果配置错误,在使用这个bean的时候才会发现
xml中使用lazy-init方法 注解中使用@Lazy A引用B,A是正常加载,B是延迟加载,那么在A初始化的时候,B就算是延迟加载的也会被加载了
生命周期方法
- 使用init-method
方法名称无所谓,但是返回值必须是void 在bean创建完成之后,init被容器调用,这是bean实例已经可以使用了,所以在init方法中可以完成任何工作 (其实之前用的把配置表中的信息放到redis中就可以这个时候做了). 而destory方法调用的时间就不一定了,根据作用域的不同,情况可能会不同:* singleton时在整个spring容器关闭的时候调用
- propotype时这个bean在实例化之后不能被跟踪,所以不会调用
- request时在web请求结束时调用
- session时在Http会话超时或无效时调用
- 使用@PostConstruct和@PreDestroy
- 实现InitializingBean和DisposableBean接口并实现方法:
public void afterPropertiesSet();
public void destroy();