上一篇已经了解了spring的相关概念,而且建立了一个Spring项目。spring中有最重要的两个概念:IOC和AOP,咱们先从IOC入手。spring
IOC全称Inversion of Control,中文一般翻译为“控制反转”,这其实不是一种技术,而是一种思想。编程
简单理解就是把原先咱们代码里面须要实现的对象建立、依赖的代码,反转给容器来帮忙实现。安全
IoC是什么app
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为什么是反转(有反转就应该有正转了),哪些方面反转了”,那咱们来深刻分析一下:框架
●谁控制谁,控制什么:传统Java SE程序设计,咱们直接在对象内部经过new进行建立对象,是程序主动去建立依赖对象;而IoC是有专门一个容器来建立这些对象,即由Ioc容器来控制对 象的建立;谁控制谁?固然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不仅是对象包括好比文件等)。ide
●为什么是反转,哪些方面反转了:有反转就有正转,传统应用程序是由咱们本身在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙建立及注入依赖对象;为什么是反转?由于由容器帮咱们查找及注入依赖对象,对象只是被动的接受依赖对象,因此是反转;哪些方面反转了?依赖对象的获取被反转了。源码分析
用图例说明一下,传统程序设计如图1-1,都是主动去建立相关对象而后再组合起来:学习
图1-1 传统应用程序示意图测试
当有了IoC/DI的容器后,在客户端类中再也不主动去建立这些对象了,如图2-2所示:ui
图1-2有IoC/DI容器后程序结构示意图
IoC能作什么
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导咱们如何设计出松耦合、更优良的程序。传统应用程序都是由咱们在类内部主动建立依赖对象,从而致使类与类之间高耦合,难于测试;有了IoC容器后,把建立和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,因此对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得很是灵活。
其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序本来是老大,要获取什么资源都是主动出击,可是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来建立并注入它所须要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找咱们,咱们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
IoC和DI
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并不是为软件系统带来更多功能,而是为了提高组件重用的频率,并为系统搭建一个灵活、可扩展的平台。经过依赖注入机制,咱们只须要经过简单的配置,而无需任何代码就可指定目标须要的资源,完成自身的业务逻辑,而不须要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为何须要依赖,谁注入谁,注入了什么”,那咱们来深刻分析一下:
●谁依赖于谁:固然是应用程序依赖于IoC容器;
●为何须要依赖:**应用程序须要IoC容器来提供对象须要的外部资源**;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所须要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不一样角度描述,因为控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),因此2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“**依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
相信经过上面的文章,对IOC的理解会更深。下面讲讲三种依赖注入的方式
构造方法注入
顾名思义,构造方法注入,就是被注入对象能够经过在其构造方法中声明依赖对象的参数列表, 让外部(一般是IoC容器)知道它须要哪些依赖对象
public classA(IinterfaceA a,IinterfaceB b){ this.a=a; this.b=b; }
构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,能够立刻使用。
setter 方法注入
对于JavaBean对象来讲,一般会经过setXXX()和getXXX()方法来访问对应属性。这些setXXX()方法统称为setter方法,getXXX()固然就称为getter方法。
public class classB(){ private IinterfaceA a; private IinterfaceB b; public IinterfaceA getIinterfaceA(){ return a; } public void setIinterfaceA(IinterfaceA a){ this.a=a; } public IinterfaceB getIinterfaceB(){ return b; } public void setIinterfaceB(IinterfaceB b){ this.b=b; } }
接口注入
相对于前两种注入方式来讲,接口注入没有那么简单明了。被注入对象若是想要IoC Service Provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。IoC Service Provider最终经过这些接口来了解应该为被注入对象注入什么依赖对象。
建立Person (被注入对象)要实现的接口
interface UserInject{ void injectUser(User user);//这里必须 是被注入对象依赖的对象 }
Person 对象实现接口
class Person implements UserInject{ private User user; public Person(){} @Override public void injectUser(User user) { this.user = user;//实现注入方法,外部经过此方法给此对象注入User对象 } }
外部调injectUser方法为Persion对象注入User对象,此即接口注入
三种注入方式的比较
接口注入。从注入方式的使用上来讲,接口注入是如今不甚提倡的一种方式,基本处于“退役状态”。由于它强制被注入对象实现没必要要的接口,带有侵入性。而构造方法注入和setter方法注入则不须要如此。
构造方法注入。这种注入方式的优势就是,对象在构造完成以后,即已进入就绪状态,能够 9立刻使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而经过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。并且在Java中,构造方法没法被继承,没法设置默认值。对于非必须的依赖处理,可能须要引入多个构造方法,而参数数量的变更可能形成维护上的不便。
setter方法注入。由于方法能够命名,因此setter方法注入在描述性上要比构造方法注入好一些。 另外,setter方法能够被继承,容许设置默认值,并且有良好的IDE支持。缺点固然就是对象没法在构造完成后立刻进入就绪状态。
综上所述,构造方法注入和setter方法注入由于其侵入性较弱,且易于理解和使用,因此是如今使用最多的注入方式;而接口注入由于侵入性较强,近年来已经不流行了。
在学习spring的具体配置以前,先了解下源码的基本结构。上一篇的测试代码
ApplicationContext ctx=new ClassPathXmlApplicationContext("META-INF/applicationContext.xml"); //获取bean的实例 HelloWorld t=(HelloWorld) ctx.getBean("hello");
咱们大体分析下过程:
经过Resource对象加载配置文件
解析配置文件,获得bean
解析bean,id做为bean的名字,class用于反射获得bean的实例(Class.forName(className));
调用getBean的时候,从容器中返回对象实例。
固然这只是简单的理解,IOC核心内容是beanFactory与ApplicationContext
BeanFactory
BeanFactory 是 Spring 的“心脏”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 来实例化、配置和管理 Bean,BeanFactory有着庞大的继承、实现体系,有众多的子接口、实现类。
BeanFactory做为一个主接口不继承任何接口,暂且称为一级接口。
有3个子接口继承了它,进行功能上的加强。这3个子接口称为二级接口。
ConfigurableBeanFactory能够被称为三级接口,对二级接口HierarchicalBeanFactory进行了再次加强,它还继承了另外一个外来的接口SingletonBeanRegistry
ConfigurableListableBeanFactory是一个更强大的接口,继承了上述的全部接口,无所不包,称为四级接口。(这4级接口是BeanFactory的基本接口体系。继续,下面是继承关系的2个抽象类和2个实现类:)
AbstractBeanFactory做为一个抽象类,实现了三级接口ConfigurableBeanFactory大部分功能。
AbstractAutowireCapableBeanFactory一样是抽象类,继承自AbstractBeanFactory,并额外实现了二级接口AutowireCapableBeanFactory
DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory,实现了最强大的四级接口ConfigurableListableBeanFactory,并实现了一个外来接口BeanDefinitionRegistry,它并不是抽象类。
最后是最强大的XmlBeanFactory,继承自DefaultListableBeanFactory,重写了一些功能,使本身更强大。
最基本的IOC容器接口BeanFactory
public interface BeanFactory { /** * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,若是一个FactoryBean的名字为a,那么,&a会获得那个Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四个不一样形式的getBean方法,获取实例 */ //根据bean的名字,获取在IOC容器中获得bean实例 Objecpublic interface BeanFactory { /** * 用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,若是一个FactoryBean的名字为a,那么,&a会获得那个Factory */ String FACTORY_BEAN_PREFIX = "&"; /* * 四个不一样形式的getBean方法,获取实例 */ //根据bean的名字,获取在IOC容器中获得bean实例 Object getBean(String name) throws BeansException; //根据bean的名字和Class类型来获得bean实例,增长了类型安全验证机制。 <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; // 是否存在 boolean containsBean(String name); // 是否为单实例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否为原型(多实例) boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 名称、类型是否匹配 boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException; //获得bean实例的Class类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);// 根据实例的名字获取实例的别名 getBean(String name) throws BeansException; //根据bean的名字和Class类型来获得bean实例,增长了类型安全验证机制。 <T> T getBean(String name, Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; // 是否存在 boolean containsBean(String name); // 是否为单实例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // 是否为原型(多实例) boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // 名称、类型是否匹配 boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException; //获得bean实例的Class类型 Class<?> getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name);// 根据实例的名字获取实例的别名
BeanFactory接口只是作了最基本的定义,里面无论如何定义和加载,只关心如何获得对象,要知道如何获得对象,必须看具体的实现类,其中XmlBeanFactory就是针对最基本的ioc容器的实现。
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader; public XmlBeanFactory(Resource resource) throws BeansException { this(resource, (BeanFactory)null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader = new XmlBeanDefinitionReader(this); this.reader.loadBeanDefinitions(resource); } }
使用:
//根据Xml配置文件建立Resource资源对象,该对象中包含了BeanDefinition的信息 Resource resource = new ClassPathResource("META-INF/applicationContext.xml"); //建立XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之因此须要BeanFactory做为参数,是由于会将读取的信息回调配置给factory BeanFactory beanFactory = new XmlBeanFactory(resource); HelloWorld helloWorld = beanFactory.getBean("hello",HelloWorld.class); System.out.println(helloWorld.getInfo());
ApplicationContext
ApplicationContext是Spring提供的一个高级的IoC容器,它除了可以提供IoC容器的基本功能外,还为用户提供了如下的附加服务。
支持信息源,能够实现国际化。(实现MessageSource接口)
访问资源。(实现ResourcePatternResolver接口)
支持应用事件。(实现ApplicationEventPublisher接口)
二者的区别
1.BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,咱们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性建立了全部的Bean。这样,在容器启动时,咱们就能够发现Spring中存在的配置错误。 相对于基本的BeanFactory,ApplicationContext 惟一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
BeanFacotry延迟加载,若是Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;因此一般状况下咱们选择使用 ApplicationContext。 应用上下文则会在上下文启动后预载入全部的单实例Bean。经过预载入单实例bean ,确保当你须要的时候,你就不用等待,由于它们已经建立好了。
2.BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但二者之间的区别是:BeanFactory须要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。并且 beanFactory 的许多功能须要经过编程实现而 Applicationcontext 能够经过配置实现。好比后处理 bean , Applicationcontext 直接配置在配置文件便可而 beanFactory 这要在代码中显示的写出来才能够被容器识别。 )
3.beanFactory主要是面对与 spring 框架的基础设施,面对 spring 本身。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并不是 beanFactory 。