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
面向切面编程,其底层原理就是动态代理实现。如果切面策略目标有接口实现,使用JDK的动态代理技术;无接口实现则使用CGLIB技术生成动态代理。
在商业环境中,接口使用度是非常高的,在这主要分析Spring如何使用JDK的动态代理技术生成动态代理对象。主要代码在JdkDynamicAopProxy,AdvisedSupport,DefaultAdvisorChainFactory,ReflectiveMethodInvocation类中。
2.1:AOP源码流程图
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)。