1:IoC

       “控制反转(Ioc)”也称为“依赖注入(DI)”,是一个定义对象依赖的过程,对象只和构造参数,工厂方法参数,对象实例属性或工厂方法返回相关。容器在创建这戏额bean的时候注入这些依赖。这些过程是一个反向的过程,所以命名为依赖反转,对象实例的创建由其提供的构造方法或服务定位机制来实现。IoC最大的好处就是“解耦”。

1.1:容器初始化流程

new ClasspathXmlApplicationContext();
        ContextLoaderListener/DispatcherServlet->WebApplicationContext

        ApplicationContext容器的初始化流程主要由AbstractAppcationContext类中的refresh方法实现。在Spring中,构建容器的过程都是同步的。同步操作是为了保证容器构建的过程中,不出现多线程资源冲突问题。ApplicationContext容器的初始化流程大致过程如下:

BeanFactory的构建:BeanFactory是ApplicationContext的父接口。是Spring框架中的顶级容器工厂对象。BeanFactory只能管理bean对象。

创建BeanFactory中管理的bean对象。

postProcessBeanFactory:加载配置中BeanFactory无法处理的内容,如propertyplacehodler的加载。

invokeBeanFactoryPostProcessors:将上一步加载的内容。包装为一个容器可以管理的bean对象注册到ApplicationContext中。

registerBeanPostProcessors:继续完成上一步的注册操作。配置文件中配置的Bean对象都创建并注册完成。

initMessageSource:i18n,国际化,初始化国际化信息源。

initApplicationEvenMulticaster:注册事件多播监听。如ApplicationEvent事件。是Spring框架中的观察者模式实现机制。

onRefresh:初始化主题资源(ThemeSource)。Spring框架提供的视图主题信息。

registerListeners:创建监听器,并注册。

finishBeanFactoryInitialization:初始化配置中出现的所有的lazy-init=false的bean对象。且bean对象必须是singleton的。

finishRefresh:最后一步,发布最终事件,生命周期监听事件。Spring容器定义了生命周期接口(Lifecycle)。可以实现容器启动调用初始化,容器销毁之前调用回收资源。

 

1.2:多容器/父子容器概念

        Spring框架允许在一个应用中创建多个上下文容器。但是建议容器间有父子关系。可以通过ConfigurableApplicationContext接口中定义的setParent方法设置父容器。一旦设置父子关系,则通过子容器中获取父容器中除PropertyPlaceHolder以外的任意资源。父容器不能获取子容器的任意资源。

        典型的父子容器:Spring和SpringMVC同时使用的时候,SpringMVC是子容器,Spring是父容器。

        保证一个JVM中,只有一个树状结构的容器树。可以通过子容器访问父容器资源。

ApplicationContext parent = new ClassPathXmlApplicationContext("classpath:parentcontainer/applicationContext.xml");
ApplicationContext child = new ClassPathXmlApplicationContext(new String[]{"classpath:parentcontainer/applicationContext2.xml"}, parent);

1.3:p域/c域

Spring2.0之后引入了p(property标签)域,Spring3.1之后引入了c(constractor-arg标签)域。可以简化配置文件中property和constructor-arg的配置

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="oneBean" class="com.sxt.OneBean"
        p:a="10"
        p:o-ref="otherBean"
        c:a="20"
        c:o-ref="otherBean"/>
    <bean id="otherBean" class="com.hhxylwk.OtherBean" />
</beans>
class OneBean{
    int a;
    Object o;
    public OneBean(int a, Object o){ this.a = a; this.o = o;}
    // getters and setters for fields.
}

1.4:lookup-method

       lookup-method一旦应用,Spring框架会自动使用CGLIB技术为指定类型创建一个动态子类型,并自动实现抽象方法。可以动态实现依赖注入的数据准备。

       在效率上,比直接自定义子类型慢。相对来说更加通用。可以只提供lookup-method方法的返回值对象即可实现动态的对象返回。

       在工厂方法难以定制的时候使用。如:工厂方法返回对象类型为接口类型。且不同版本应用返回的对象未必相同时使用。可以避免多次开发工厂类

      

package com.hhxylwk.lookupmethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestLookupMethod {
    public static void main(String[] args) {
        ApplicationContext context =new
        ClassPathXmlApplicationContext("classpath:lookupmethod/applicationContext.xml");
        CommandManager manager = context.getBean("manager",CommandManager.class);
        System.out.println(manager.getClass().getName());
        manager.process();
    }
}
abstract class CommandManager{
    public void process() {
        MyCommand command = createCommand();
        // do something ...
        System.out.println(command);
    }
    protected abstract MyCommand createCommand();
}
class MyCommand{
    public MyCommand(){
        System.out.println("MyCommand instanced");
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="manager" class="com.hhxylwk.lookupmethod.CommandManager">
        <lookup-method bean="command" name="createCommand"/>
    </bean>
    <bean id="command" class="com.hhxylwk.lookupmethod.MyCommand"></bean>
</beans>

2:AOP

springboot el表达式注入rce_Spring

        面向切面编程,其底层原理就是动态代理实现。如果切面策略目标有接口实现,使用JDK的动态代理技术;无接口实现则使用CGLIB技术生成动态代理。

        在商业环境中,接口使用度是非常高的,在这主要分析Spring如何使用JDK的动态代理技术生成动态代理对象。主要代码在JdkDynamicAopProxy,AdvisedSupport,DefaultAdvisorChainFactory,ReflectiveMethodInvocation类中。

2.1:AOP源码流程图

springboot el表达式注入rce_spring_02

3:AOP中常用的Pointcut-expression

        AOP开发中,有非常重要的几个概念,其中有一个概念叫“切点”。代表通知切入到代码执行流程的那个位置点。切点一般通过表达式定义。Spring框架会通过SpringEL来解析表达式,表达式有多种定义方式,分别队形不同的解析结果

3.1:execution表达式

         语法格式:execution(返回类型 包名.类名.方法名(参数表))。

         如:execution(java.lang.String com.xxx.service.AService.test(java.lang.Integer)),在类型com.xxx.service.AService种有方法test,且参数为Integer,返回类型为String时增加切面。

                excution(* com.xxx.service.AService.*(..)), com.xxx.AService类型中的任意方法,任意类型返回结果,参数表不限定,都增加切面。

         应用:最常用。也是相对最通用。根据方法执行的标准,定义切点。如:事务处理,日志处理。

3.2:target表达式

         以目标对象作为切点的表达式定义方式。

         语法:target(包名.接口名)。

         如:target(com.xxx.IA),所以实现了IA接口的实现类,作为代理的目标对象,会自动增加通知的织入,实现切面。

         应用:为某个具体的接口是实现提供的配置。如:登陆。登陆的时候需要执行的附属逻辑是比较多的。在不同的业务流程中,附属逻辑也不同。如:电商中,可能在登陆的时候,需要去执行购物车合并。

3.3:this表达式

        实现了某接口的代理对象,会作为切点。和target很类似。

        语法:this(包名.接口名)。

        如:this(com.xxx.IA),代理对象Proxyx如果实现了IA接口,则作为连接点。

        应用:针对某个具体的代理提供的配置。比target切点粒度细致。因为目标对象可以多实现。代理对象可以针对目标对象实现的多个接口的某一个接口。

3.4:within表达式

        以包作为目标,定义切点。

        语法:within(包名.*),代表在包中任意接口或类型都作为切点。

        应用:针对某一个包提供的切点,粒度比target粗糙。如:某包中的所有接口都需要执行某附属逻辑。

3.5:args表达式

        以参数标准作为目标,定义切点。

        语法:args(类型,类型),代表方法的参数表符合要求的时候,作为切点。参数表是有顺序的。

        应用:主要应用在参数校验中。如:登陆的时候必须传递两个字符串参数(登录名和密码),配合execution实现,如execution(* xxxx.*.xxxlogin(..)) args(String,String)。