IOC是什么
IoC(Inversion of Control)控制反转,包含了两个方面:一、控制。二、反转
类与类依赖关系交给容器处理。IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
IOC不够开门见山,于是Martin Fowler提出了DI(dependency injection)依赖注入来替代IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖。
IOC有两种方式:DI(依赖注入)和DL (依赖查找)
IOC的优点
- 减少了对象的创建和管理 ,使代码层次更加清晰。
- Spring 的IOC容器是一个轻量级的容器 ,没有侵入性(不依赖容器的API) ,不需要实现一些特殊接口。
- 鼓励我们面向接口编程。
- 减少了代码的耦合,将耦合的部分推到了配置文件中 ,如果他们的关系发生了改变,只需要修改配置文件。
DL (依赖查找)
程序提供查找方式,交给容器去查找(回调函数)
容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都使用这种方式
下面代码展示了基于JNDI实现的依赖查找机制。
public class MyBusniessObject{
private DataSource ds;
private MyCollaborator myCollaborator;
public MyBusnissObject(){
Context ctx = null;
try{
ctx = new InitialContext();
ds = (DataSource) ctx.lookup(“java:comp/env/dataSourceName”);
myCollaborator =
(MyCollaborator) ctx.lookup(“java:comp/env/myCollaboratorName”);
}……
但是日常开发中,EJB类似的已经很少用到了,所以很多同学没听过DL(依赖查找),这很正常,大家更熟悉的是DI(依赖注入)。
不过这两个查找大家应该用过:
- 名称查找 - autowireByName
- 类型查找 - autowireByType
名称查找 - autowireByName
直接从 BeanFactory 中取出这个 bean 就可以了,常用的就是@Qualifier
类型查找 - autowireByType
常用的就是@autowire
如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个该类型的bean,那么将会抛出异常
简单的理解就是通过类名去匹配
DI(依赖注入)
一个对象需要另外一个对象时,无需在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序
用图例说明一下,传统程序设计如下图1,都是主动去创建相关对象然后再组合起来:
图1
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2所示
图2
IOC容器
IOC容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系。
- 原理就是通过Java的反射技术来实现的!通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
- 再通过配置文件(xml)或者注解来描述类与类之间的关系
- 我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了!
Spring容器(Bean工厂)
- BeanFactory:这是最基础、面向Spring的
- ApplicationContext:这是在BeanFactory基础之上,面向使用Spring框架的开发者。提供了一系列的功能!
ApplicationContext这个大家就很熟悉了吧,spring绝大部分应用都是使用ApplicationContext
BeanFactory和ApplicationContext区别
BeanFactory 可以理解为含有bean集合的工厂类。BeanFactory 包含了种bean的定义,以便在接收到客户端请求时将对应的bean实例化。
BeanFactory还能在实例化对象的时生成协作类之间的关系。此举将bean自身与bean客户端的配置中解放出来。BeanFactory还包含了bean生命周期的控制,调用客户端的初始化方法(initialization methods)和销毁方法(destruction methods)。
applicationcontext是beanFactory的子接口,拥有BeanFactory的所有功能,但applicationcontext在此基础上还提供了其他的功能。
- 提供了支持国际化的文本消息
- 统一的资源文件读取方式
- 已在监听器中注册的bean的事件
且beanFactory是延迟加载,需要类的时候才创建类的实例,而ApplicationContext在初始化时就加载完成了所有的单例bean
以下是三种较常见的 ApplicationContext 实现方式:
1、ClassPathXmlApplicationContext:从classpath的XML配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);
2、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”);
3、XmlWebApplicationContext:由Web应用的XML文件读取上下文。
Spring Bean的生命周期
Spring Bean的生命周期简单易懂。在一个bean实例被初始化时,需要执行一系列的初始化操作以达到可用的状态。同样的,当一个bean不在被调用时需要进行相关的析构操作,并从bean容器中移除。
Spring bean factory 负责管理在spring容器中被创建的bean的生命周期。Bean的生命周期由两组回调(call back)方法组成。
- 初始化之后调用的回调方法。
- 销毁之前调用的回调方法。
Spring框架提供了以下四种方式来管理bean的生命周期事件:
- InitializingBean和DisposableBean回调接口
- 针对特殊行为的其他Aware接口
- Bean配置文件中的Custom init()方法和destroy()方法
- @PostConstruct和@PreDestroy注解方式 使用
customInit()
和 customDestroy()
方法管理
bean
生命周期的代码样例如下:
<beans> <bean id="demoBean" class="com.somnus.task.DemoBean" init-method="customInit" destroy-method="customDestroy"> </bean> </beans>
装配Bean方式
Spring4.x开始IOC容器装配Bean有4种方式:
- XML配置
- 注解
- JavaConfig
- 基于Groovy DSL配置(这种很少见)
日常开发中,常用到的是XML配置+注解。
剩下的两种有兴趣的可以自行百度+google
依赖注入方式
依赖注入的方式有3种方式:
- 属性注入-->通过
setter()
方法注入 - 构造方法注入
- 工厂方法注入
构造方法注入和属性注入有什么区别
- 在属性注入方法支持大部分的依赖注入,如果我们仅需要注入int、string和long型的变量,我们不要用设值的方法注入。对于基本类型,如果我们没有注入的话,可以为基本类型设置默认值。在构造方法注入不支持大部分的依赖注入,因为在调用构造方法中必须传入正确的构造参数,否则的话为报错。
- 属性注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方法注入的话,那么构造方法将不能覆盖由设值方法注入的值。很明显,因为构造方法尽在对象被创建时调用。
- 在使用属性注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可能是不完整的。而在另一种情况下,构造器注入则不允许生成依赖关系不完整的对象。
- 在属性注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出s
ObjectCurrentlyInCreationException异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然。所以Spring用设值注入的方法解决了循环依赖的问题,因对象的设值方法是在对象被创建之前被调用的。
Bean的作用域
Spring容器中的bean可以分为5个范围。所有范围的名称都是自说明的,但是为了避免混淆,还是让我们来解释一下:
使用3,4,5作用域的,需要手动设置代理
- singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
- prototype:多例范围与单例范围相反,为每一个bean请求提供一个实例。
- request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
bean的自动装配
使用bean元素的autowire属性来指定Bean定义的自动装配,共有5中模式:
- no 默认的方式是不进行自动装配,通过手工设置ref 属性来进行装配bean
- byName 依赖的 bean 名称需要与类中引用的名称一致 ,就会匹配依赖关系,我们在类中的引用的名称是 userAutowireDao 所以就会去匹配我们的 userAutowireDao 方法
- byType 通过参数的数据类型自动自动装配,如果一个bean的数据类型和另外一个bean的property属性的数据类型兼容,就自动装配,简单的理解就是通过类名去匹配
- construct 构造方法中的参数通过byType的形式,自动装配。
- default 由上级标签<beans>的default-autowire属性确定。
常用注解详解
注解注入就是用注解标签的方式来替换掉我们 xml 配置文件里面 bean 的注册和依赖
@Component
用于类上
所有的类上面都可以这么写,通用注解,这是不规范的写法,哈哈哈
@Repository
用于类上
这个注解主要是声明 dao 的类组件
@Service
这个注解主要是声明 service 服务类
@Controller
主要是声明控制类 (springmvc/struts2 action/controller)
@Resource
用于类内
javaEE 的注解 ,默认是以 byName 方式注入,byName 找不到的话,再用 byType 去匹配
效果跟Autowired一样,查找顺序相反
@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Autowired
用于类内
spring 的注解,默认是以 byType 注入,-如果有多个实现类,他再用 byName 的方式(@Qualifier)去匹配
效果跟Resource一样,查找顺序相反
Autowired和Qualifier一起用,
eg:
@Autowired
@Qualifier(value = "TestService2")
private TestService testService;
//实现类
@Service("TestService1")
public class TestServiceImpl implements TestService {...}
//实现类
@Service("TestService2")
public class TestServiceImpl implements TestService {...}
@Qualifier
spring的注解,可以指定实现的方法名称
@Scope
bean的作用域,可以查看上面的概念,这里就不再重复了
总结
借鉴了其他博主的思路:会整理出Spring思维导图出来,等AOP写好一并放出来。
今天的spring介绍就写到这里,再见!
本文摘自 :https://blog.51cto.com/u