Spring容器深刻(li)

  spring中最经常使用的控制反转和面向切面编程。java

1、IOC程序员

  IoC(Inversion of Control,控制倒转)。对于spring框架来讲,就是由spring来负责控制对象的生命周期和对象间的关系。在一个对象中,若是要使用另外的对象,就必须获得它(本身new一个,或者从JNDI中查询一个),使用完以后还要将对象销毁(好比Connection等),对象始终会和其余的接口或类藕合起来。正则表达式

  全部的类都会在spring容器中登记,告诉spring你是个什么东西,你须要什么东西,而后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其余须要你的东西。全部的类的建立、销毁都由 spring来控制,也就是说控制对象生存周期的再也不是引用它的对象,而是spring。对于某个具体的对象而言,之前是它控制其余对象,如今是全部对象都被spring控制,因此这叫控制反转。spring

  IoC的一个重点是在系统运行中,动态的向某个对象提供它所须要的其余对象。这一点是经过DI(Dependency Injection,依赖注入)来实现的。好比对象A须要操做数据库,原本须要在A中本身编写代码来得到一个Connection对象,有了spring咱们就只须要告诉spring,A中须要一个Connection,至于这个Connection怎么构造,什么时候构造,A不须要知道。在系统运行时,spring会在适当的时候制造一个Connection,而后像打针同样,注射到A当中,这样就完成了对各个对象之间关系的控制。A须要依赖Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3以后一个重要特征是反射(reflection),它容许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是经过反射来实现注入的。数据库

  在没有使用Spring的时候,每一个对象在须要使用他的合做对象时,本身均要使用像new object() 这样的语法来将合做对象建立出来,这个合做对象是由本身主动建立出来的,建立合做对象的主动权在本身手上,本身须要哪一个合做对象,就主动去建立,建立合做对象的主动权和建立时机是由本身把控的,而这样就会使得对象间的耦合度高了,A对象须要使用合做对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,而且是紧密耦合在一块儿,而使用了Spring以后就不同了,建立合做对象B的工做是由Spring来作的,Spring建立好B对象,而后存储到一个容器里面,当A对象须要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,而后交给A对象使用,至于Spring是如何建立那个对象,以及何时建立好对象的,A对象不须要关心这些细节问题(你是何时生的,怎么生出来的我可不关心,能帮我干活就行),A获得Spring给咱们的对象以后,两我的一块儿协做完成要完成的工做便可。编程

  因此控制反转IoC(Inversion of Control)是说建立对象的控制权进行转移,之前建立对象的主动权和建立时机是由本身把控的,而如今这种权力转移到第三方,好比转移交给了IoC容器,它就是一个专门用来建立对象的工厂,你要什么对象,它就给你什么对象,有了IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,经过IoC容器来创建它们之间的关系。缓存

2、IOC的好处安全

  可维护性比较好,很是便于进行单元测试,便于调试程序和诊断故障。代码中的每个Class均可以单独测试,彼此之间互不影响,只要保证自身的功能无误便可,这就是组件之间低耦合或者无耦合带来的好处。服务器

  每一个开发团队的成员都只须要关心实现自身的业务逻辑,彻底不用去关心其它的人工做进展,由于你的任务跟别人没有任何关系,你的任务能够单独测试,你的任务也不用依赖于别人的组件,不再用扯不清责任了。app

  可复用性好,咱们能够把具备广泛性的经常使用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,固然这也是面向对象的基本特征。

  IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当咱们更换一个实现子类将会变得很简单,只要修改配置文件就能够了,彻底具备热插拨的特性。

3、IOC常见的注入方式

  接口注入(Spring不支持),接口注入模式由于历史较为悠久,在不少容器中都已经获得应用。但因为其在灵活性、易用性上不如其余两种注入模式,于是在 IOC 的专题世界内并不被看好。

  setter注入,对于习惯了传统 javabean 开发的程序员,经过 setter 方法设定依赖关系更加直观。 若是依赖关系较为复杂,那么构造子注入模式的构造函数也会至关庞大,而此时设值注入模式则更为简洁。若是用到了第三方类库,可能要求咱们的组件提供一个默认的构造函数,此时构造子注入模式也不适用。

  构造器注入,在构造期间完成一个完整的、合法的对象。 全部依赖关系在构造函数中集中呈现。 依赖关系在构造时由容器一次性设定,组件被建立以后一直处于相对“不变”的稳定状态。 只有组件的建立者关心其内部依赖关系,对调用者而言,该依赖关系处于“黑盒”之中。

4、AOP

  AOP(Aspect-OrientedProgramming,面向切面编程)。例如日志功能。日志代码每每水平地散布在全部对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其余类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它致使了大量代码的重复,而不利于各个模块的重用。

  而AOP技术则偏偏相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减小系统的重复代码,下降模块间的耦合度,并有利于将来的可操做性和可维护性。AOP表明的是一个横向的关系。

  使用“横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特色是,他们常常发生在核心关注点的多处,而各处都基本类似。好比权限认证、日志、事务处理。Aop 的做用在于分离系统中的各类关注点,将核心关注点和横切关注点分离开来。

  实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法建立“方面”,从而使得编译器能够在编译期间织入有关“方面”的代码。

5、AOP概念

  切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。切面用spring的 Advisor或拦截器实现。

  链接点(Joinpoint): 程序执行过程当中明确的点,如方法的调用或特定的异常被抛出。

  通知(Advice): 在特定的链接点,AOP框架执行的动做。各类类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器作通知模型,维护一个“围绕”链接点的拦截器链。Spring中定义了四个advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。

  切入点(Pointcut): 指定一个通知将被引起的一系列链接点的集合。AOP框架必须容许开发者指定切入点:例如,使用正则表达式。 Spring定义了Pointcut接口,用来组合MethodMatcher和ClassFilter,能够经过名字很清楚的理解, MethodMatcher是用来检查目标类的方法是否能够被应用此通知,而ClassFilter是用来检查Pointcut是否应该应用到目标类上。

  引入(Introduction): 添加方法或字段到被通知的类。 Spring容许引入新的接口到任何被通知的对象。例如,你可使用一个引入使任何对象实现 IsModified接口,来简化缓存。Spring中要使用Introduction, 可有经过DelegatingIntroductionInterceptor来实现通知,经过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口。

  目标对象(Target Object): 包含链接点的对象。也被称做被通知或被代理对象。POJO

  AOP代理(AOP Proxy): AOP框架建立的对象,包含通知。 在Spring中,AOP代理能够是JDK动态代理或者CGLIB代理。

  织入(Weaving): 组装方面来建立一个被通知对象。这能够在编译时完成(例如使用AspectJ编译器),也能够在运行时完成。Spring和其余纯Java AOP框架同样,在运行时完成织入

6、AOP通知类型

  前置通知(before advice):在切入点以前执行。

  后置通知(after returning advice):在切入点执行完成后,执行通知。

  环绕通知(around advice):包围切入点,调用方法先后完成自定义行为。

  异常通知(after throwing advice):在切入点抛出异常后,执行通知。

7、Spring启动过程

  经过对ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");这条语句的跟踪,最重要的是调用了AbstractApplicationContext类的refresh()方法。refresh()方法有如下几个步骤:

  1.第一个方法是prepareRefresh(),prepareRefresh()只包含了两个方法,这两个方法加载了服务器的一些信息和判断必填项是否都完整。第二个方法:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();refreshBeanFactory()这个方法,若是容器如今已经启动,而咱们要新启动一个容器,那么首要的就是销毁之前的容器,清空容器和容器中注册了的bean。新建容器对象DefaultListableBeanFactory beanFactory = createBeanFactory();新建一个DefaultListableBeanFactory.注意在这里面有一个方法getInternalParentBeanFactory(),这个方法得到并设置了父容器。固然在spring启动的过程当中这个父容器是空的。

  2.判断bean是否容许覆盖,bean是否容许循环引用,把java注解和spring标准注解都放到了容器里面来。

  3.要加载bean loadBeanDefinitions(beanFactory)。实现了该方法的类一共有三个一、XmlWebApplicationContext 二、AbstractXmlApplicationContext 三、AnnotationConfigWebApplicationContext.在项目启动的过程当中XmlWebApplicationContext真正的被执行。接下来的bean加载过程主要涉及到的就是配置文件解析。配置文件解析大致步骤一、根据命名空间将标签名称和解析类放置到map中二、读取配置文件的过程当中遇到标签就将找到解析类去解析。

8、如何感知到Spring容器启动成功这件事情?

  spring提供了事件监听器的处理机制,spring提供了内置的几类的事件:

    ContextClosedEvent 当使用ConfigurableApplicationContext接口的close()方法关闭ApplicationContext容器时触发该事件。

    ContextRefreshedEvent ApplicationContext容器初始化或者刷新时触发该事件。

    ContextStartedEvent 当使用ConfigurableApplicationContext接口的start()方法启动ApplicationContext容器时触发该事件。

    ContextStoppedEvent 当使用ConfigurableApplicationContext接口的stop()方法中止ApplicationContext容器时触发该事件。

    RequestHandleEvent。

在spring容器启动完成后会触发ContextRefreshedEvent事件,在spring容器启动过程当中调用AbstractApplicationContext的refresh()方法,其中调用了finishRefresh()用来发布这个事件。

ApplicationEventMulticaster在接收到ApplicationEvent事件以后,经过multicastEvent方法,通知全部的观察者ApplicationListener。

好比hsf中经过建立一个ContextRefreshedEvent,ContextClosedEvent事件监听器,在spring容器启动完成后和容器关闭时,作一些处理动做。

9、FactoryBean与BeanFactory

  Spring中有两种类型的Bean,一种是普通Bean,另外一种是工厂Bean,即FactoryBean,这两种Bean都被容器管理,但工厂Bean跟普通Bean不一样,其返回的对象不是指定类的一个实例,其返回的是该FactoryBean的getObject方法所返回的对象。

  BeanFactory是IoC容器的核心接口。它的职责包括:实例化、定位、配置应用程序中的对象及创建这些对象间的依赖。从本质上讲,BeanFactory仅仅只是一个维护bean定义以及相互依赖关系的高级工厂接口。经过BeanFactory咱们能够访问bean定义。

10、如何在Bean初始化先后作一些事情

  首先看下AbstractAutowireCapableBeanFactory的createBean方法:(删去些占地方的try catch)。

protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) throws BeanCreationException {
    resolveBeanClass(mbd, beanName); /1解析Bean的class
    mbd.prepareMethodOverrides(); //2 方法注入准备
    Object bean = resolveBeforeInstantiation(beanName, mbd); //3 第一个BeanPostProcessor扩展点
    if (bean != null) { //4 若是3处的扩展点返回的bean不为空,直接返回该bean,后续流程不须要执行
        return bean;
    }
    Object beanInstance = doCreateBean(beanName, mbd, args); //5 执行spring的建立bean实例的流程啦
    return beanInstance;
}

接着:

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                //3.一、执行InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation回调方法
                bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
                if (bean != null) {
                    //3.二、执行InstantiationAwareBeanPostProcessor的postProcessAfterInitialization回调方法
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
}

接着:

protected Object applyBeanPostProcessorsBeforeInstantiation(Class beanClass, String beanName)
            throws BeansException {
 
        for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext();) {
            BeanPostProcessor beanProcessor = (BeanPostProcessor) it.next();
            if (beanProcessor instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) beanProcessor;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

接着:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

经过代码咱们能够看到BeanPostProcessor,能够在spring容器实例化bean以后,在执行bean的初始化方法先后,添加一些本身的处理逻辑。

10、如何在bean销毁的时候作一些事情?

  有两种方法:A.利用destroy-method配置 B.实现DisposableBean接口。实现DisposableBean接口,在destroy()方法里作一些操做。或者对配置文件加入destroy-method属性指定方法;若是二者同时出现,先执行DisposableBean接口的destroy()方法,而后再执行destroy-method属性指定方法。

11、Bean的生命周期

  何时初始化Bean?当scope=singleton,即默认状况,会在容器初始化时实例化。但咱们能够指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有第一次获取bean才会初始化bean,即第一次请求该bean时才初始化。以下配置所示:

<bean id=”xxx” class=”examples.test.OrderServiceBean” lazy-init=”true” />

若是想对全部bean都应用延迟初始化,能够在根节点beans设置default-lazy-init=”true”,以下所示:

<beans default-lazy-init=”true” >

scope=prototype时,也会延迟初始化bean,即第一次请求该bean时才初始化(如调用getBean()方法时)。

  Bean的生命周期,构造器、init方法、获取bean后的操做、destroy方法(ctx.close时执行)。注意:若是bean的scope设为prototype时,当ctx.close时,destroy方法不会被调用。缘由:对于prototype做用域的bean,有一点很是重要,那就是Spring不能对一个prototype bean的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例漠不关心了。无论何种做用域,容器都会调用全部对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会 被调用。清除prototype做用域的对象并释听任何prototype bean所持有的昂贵资源,都是客户端代码的职责。(让Spring容器释放被prototype做用域bean占用资源的一种可行方式是,经过使用bean的后置处理器,该处理器持有要被清除的bean的引用。)

在spring容器启动时,refresh方法中,调用invokeBeanFactoryPostProcessors(beanFactory);

 

     在 invokeBeanFactoryPostProcessors 这个方法里,获取实现 BeanFactoryPostProcessor 接口的 bean ,将其排序后,依次调用invokeBeanFactoryPostProcessors。

最终调用了postProcessor.postProcessBeanFactory(beanFactory)方法,该方法会对bean作初始的处理,具体处理方式与子类具体实现有关。

 

相关文章
相关标签/搜索