used spring

spring日常问题

  • sofa中 context:property-placeholder 不生效,spring 版本为 5.1.14

BeanDefinition扫描器总结

Spring bean注册笔记

BeanPostProcessor 总结

内部的BeanPostProcessor接口总结
内部的BeanPostProcessor实现总结

接触到的扩展点

  1. BeanFactoryPostProcessor 可以在 bean 正式初始化之前改变其值,该对象会被提前初始化;什么场景下?注意事项?
  2. BeanPostProcessor bean 的生命周期中,可用于所有的 bean;如 @PostConstruct 注解。
  3. FactoryBean 创建bean时(MethodLocatingFactoryBean);什么场景下?注意事项?

ioc

ioc概述

  1. IOC 总体来说有两处地方最重要,一个是创建 Bean 容器,一个是初始化 Bean
  2. ApplicationContext 继承自 BeanFactory,但是它不应该被理解为 BeanFactory 的实现类,
    而是说其内部持有一个实例化的 DefaultListableBeanFactory。以后所有的 BeanFactory 相关的操作其实是委托给这个实例来处理的。
  3. BeanDefinition 解析/加载、注册 ;// 我们把这步叫做 注册Bean 吧
    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  4. Bean 的生命周期:创建、装填属性、回调 各种初始化函数(init-method,InitializingBean,BeanPostProcessor)

ioc总的流程

ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:applicationfile.xml”);
AbstractApplicationContext # refresh()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public void refresh() throws BeansException, IllegalStateException {
// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {

// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
prepareRefresh();

// 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
// 这块待会会展开说
prepareBeanFactory(beanFactory);

try {
// 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
// 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】

// 到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化。
// 这里是提供给子类的扩展点,具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
postProcessBeanFactory(beanFactory);
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);

// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);

// 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了
initMessageSource();

// 初始化当前 ApplicationContext 的事件广播器,这里也不展开了
initApplicationEventMulticaster();

// 从方法名就可以知道,典型的模板方法(钩子方法),
// 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();

// 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
registerListeners();

// 重点,重点,重点
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);

// 最后,广播事件,ApplicationContext 初始化完成
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// Destroy already created singletons to avoid dangling resources.
// 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
destroyBeans();

// Reset 'active' flag.
cancelRefresh(ex);

// 把异常往外抛
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

一些重要的类

AbstractRefreshableApplicationContext –> ResourceLoader,ApplicationEventPublisher,MessageSource
DefaultListableBeanFactory –> BeanDefinitionRegistry
AbstractAutowireCapableBeanFactory –主要为了 @Autowired 注解注入属性值
AnnotationConfigRegistry –ApplicationContext 基于注解配置的接口
beanDefinitionNames 一个 ArrayList, 可以看到从前到后加载的 beanname
InstantiationAwareBeanPostProcessor

  • AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation
    中调用了 ibp.postProcessBeforeInstantiation
    在这里用体现了此接口用于初始化自定义的bean,主要是在Spring内部使用(自定义了 TargetSource 时)。
  • AbstractAutowireCapableBeanFactory#populateBean
    中调用了 ibp.postProcessAfterInstantiation,没找到有实际的使用
  • 一个重要的实现 AutowiredAnnotationBeanPostProcessor
    对采用 @Autowired、@Value 注解的依赖进行设值

allowCircularReferences

spring到底支持哪样子 的循环依赖(循环引用)??? 答案:单例的 setter 属性方式依赖
构造器 的循环依赖;DependsOn 的循环依赖 都无法解决。
AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues//这里 递归设置属性
DefaultSingletonBeanRegistry#getSingleton(java.lang.String)//这里进入 提前引用的返回
isSingletonCurrentlyInCreation():判断当前 singleton bean 是否处于创建中;
singletonObjects;//一级缓存,返回完整可用的 单例
earlySingletonObjects;//二级缓存,返回提前创建的 单例
singletonFactories;//三级缓存,为提前创建的 bean 加入 AbstractAutowireCapableBeanFactory#getEarlyBeanReference 逻辑(aop 的扩展?)
原理:对象的属性由 spring 容器统一设置,所以可以解决循环引用问题。

aop

spring 1.2

proxy 的配置方式

  1. 配置 bean
  2. 配置 Advice –指定执行前/执行后,处理逻辑
  3. 指定 Advisor –配置指定方法用指定 Advice 增强
  4. 指定 Interceptor –一般的 MethodInterceptor,写处理逻辑
  5. 配置 Proxy –配置指定的 bean 用指定的 interceptorNames 属性增强
    • interceptorNames 可以指定为 Advice、Advisor、Interceptor 等

缺点:我们得为每个 bean 都配置一个代理,之后获取 bean 的时候需要获取这个代理类的 bean 实例(如 (UserService) context.getBean(“userServiceProxy”))

autoproxy 的配置方式

  1. 配置 bean
  2. 配置 Advice
  3. 配置 NameMatchMethodPointcutAdvisor, RegexpMethodPointcutAdvisor
  4. 配置 BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator

Advisor 有一个更加灵活的实现类 RegexpMethodPointcutAdvisor,它能实现正则匹配 指定方法增强
BeanNameAutoProxyCreator 是自己匹配方法,然后交由内部配置 advice 来拦截处理;
而 DefaultAdvisorAutoProxyCreator 是让 ioc 容器中的所有 advisor 来匹配方法,advisor 内部都是有 advice 的,让它们内部的 advice 来执行拦截处理。


proxy 与 autoproxy 比较
autoproxy 强调自动,也就是说 Spring 会自动做这件事,而不用像前面介绍的,我们需要显式地指定代理类的 bean。

Spring 2.0

@AspectJ 的配置方式

@AspectJ 和 AspectJ 没多大关系,并不是说基于 AspectJ 实现的,而仅仅是使用了 AspectJ 中的概念,包括使用了 AspectJ 的注解。

  1. 开启 @AspectJ 的注解配置方式(@EnableAspectJAutoProxy)
  2. 配置 @Aspect,切面类,包含以下两点:
    • 配置 Pointcut 用于定义哪些方法需要被增强或者说需要被拦截
    • 配置 Advice ,JoinPoint用于获取方法入参

Schema-based 的配置方式

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service.*.*(..))"/>

<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>

<aop:aspect ref="aopAdvice">
<aop:pointcut id="pointcutIn" expression="execution(* base.aop.AopDemo.send())" />
<aop:before method="beforeSend" pointcut-ref="pointcutIn" />
<aop:after method="afterSend" pointcut-ref="pointcutIn" />
</aop:aspect>
</aop:config>

<tx:advice id="tx-advice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>

<bean id="aopAdvice" class="base.aop.AopDemoAdvice" />
<!-- 必须配置,因为被代理的对象必须在Spring容器中 -->
<bean id="aopDemo" class="base.aop.AopDemo" />

aop 命名空间的解析类:AopNamespaceHandler。


  • 静态代理、动态代理的本质区别:代理对象创建时机不同,动态代理在调用时创建
  • Spring AOP 的原理很简单,就是动态代理,它和 AspectJ 不一样,AspectJ 是直接修改掉你的字节码。
  • 如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理,如果没有实现任何接口,会使用 CGLIB 实现代理,如果设置了 proxy-target-class=”true”,那么都会使用 CGLIB。
  • InvocationHandler 只有一个方法,当生成的代理类对外提供服务的时候,都会导到这个方法中

aop 的实现

DefaultAdvisorAutoProxyCreator,…,AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator
AbstractAutoProxyCreator#postProcessAfterInitialization
入口在 AbstractAutowireCapableBeanFactory#initializeBean#applyBeanPostProcessorsAfterInitialization


spring 其他

典型的设计模式

  1. 策略模式
    • 代理生成的两种方式:jdk 代理;cglib 代理
    • 不同的资源加载方式,Resource 的设计思想
  2. 模板方法 AbstractHandlerMapping 的设计思想
  3. 观察者 事件设计
  4. 代理模式
  5. 装饰器模式 EncodedResource
  6. 访问者模式 ReflectionUtils

策略模式 与 模板方法 的区别
模板方法 侧重于继承结构,父类定义模板(或流程),子类重写指定方法;
策略模式 侧重于环境中的变化条件,根据不同的条件选择对应的策略。

使用到的容器

spring 容器实战

spring 中的 java 基础知识

  1. 反射-以一种更加通用的方式间接地操作目标类(相对于一般的调用)
  2. 动态代理(代理实例-相对一般的实例;一般用于横向逻辑)
    • JDK动态代理(有接口的场景)
    • InvocationHandler,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。
    • Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。
  3. PropertyEditor
    JavaBean 规范通过 java.beans.PropertyEditor 定义了设置 JavaBean 属性的方法,通过 BeanInfo 描述了 JavaBean 哪些属性是可定制的,
    此外还描述了可定制属性与 PropertyEditor 的对应关系。(属性描述器 PropertyDescriptor 为 JavaBean 属性指定编辑器)
    PropertyEditorRegistrySupport -Spring 为常见的属性类型提供了默认的属性编辑器。
    ConversionService,它提供了更为强大的类型转换的能力,可以完成任意类型之间的转换,还可以在转换过程中参考目标对象所在宿主类的上下文信息。
  4. XML基础知识
    • 默认命名空间:它没有空间名,用于 Spring Bean 的定义;
    • xsi 命名空间:这个命名空间用于为每个文档中命名空间指定相应的 Schema 样式文件,是标准组织定义的标准命名空间;
    • aop 命名空间:这个命名空间是Spring配置AOP的命名空间,是用户自定义的命名空间。
    • 命名空间的定义分为两个步骤:第一步指定命名空间的名称,第二步指定命名空间的 Schema 文档样式文件的位置,用空格或回车换行进行分隔。
  5. 注解-Java 语言解释器会忽略这些注解,而由第三方工具负责对注解进行处理。
  6. ThreadLocal
    思路浅析:在 ThreadLocal 类中有一个 Map,用于存储每一个线程的变量副本,Map 中元素的键为线程对象,而值对应线程的变量副本。
    在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它的身影(ThreadLocal)。
  7. 国际化信息
    Java通过 java.util.Locale 类表示一个本地化对象,它允许通过语言参数和国家/地区参数创建一个确定的本地化对象。
    支持多国语言的Web应用程序 == 多种资源文件(<资源名>_<语言代码>_<国家/地区代码>.properties)
    spring 中,国际化信息资源应该是容器级。
  8. HTTP 报文
    HTTP 请求报文由3部分组成(请求行+请求头+请求体)。
  9. @Value 注解
    目前Spring的注解不支持静态的变量和方法,至于原因:有人说是因为Spring是基于对象层面的依赖注入的,而且使用静态的变量或类什么的话,扩大了其生命周期,给Testing带来困难,故Spring不推荐这样做。

spring in action

常用应用上下文:

  • AnnotationConfigApplicationContext 基于 java 配置类中加载 spring 应用上下文
  • ClassPathXmlApplicationContext 基于类路径下的 xml 文件中加载 spring 应用上下文

bean 的生命周期

实例化、填充属性、调用 XXXAware 接口、调用 postProcessBeforeInitialization()、调用 InitializingBean 接口、调用自定义初始化方法、调用 postProcessAfterInitialization()、创建完成使用、销毁 bean 对象、、、

spring 从两个角度来实现自动化装配

  • 组件扫描(component scanning):spring 会自动发现应用上下文中所创建的 bean。
  • 自动装配(autowiring):spring 自动满足 bean 之间的依赖。
  • @Component –注解申明组件。

通过 Java 代码装配 bean

@Configuration 和 @Bean 的组合使用,但也少不了要开启组件扫描功能。

导入和混合配置

在 JavaConfig 中引用 XML 配置

@Configuration
@Import(MyJavaConfig.class)
@ImportResource(“classpath:WEB-INF/spring-context.xml”)

在 XML 中引用 JavaConfig 配置

1
<bean class="org.poprabbit.smvcdemo.config.MyJavaConfig"/>