事务部分

1. TransactionTemplate使用Connection怎么获取datasource的连接

如果使用最原生的import java.sql.Connection、datasource,在重写TransactionCallbackWithoutResult()的doInTransaction方法的时候需要通过DataSourceUtils.getConnection(dataSource)来获取连接,而不能直接datasource.getConnection()。主要是DataSourceUtils是从TransactionSynchronizationManager中获取的连接,而datasource.getConnection()是新建一个连接。transactionTemplate.execute()方法中会先进行transactionManager.getTransaction(),在这个方法里面会进行绑定TransactionSynchronizationManager。然后然后再调用doInTransaction方法,也就是复写的方法,然后绑定之后就可以通过DataSourceUtils.getConnection(dataSource)来获取。

2. TransactionSynchronizationManager是啥

里面包含了一堆ThreadLocal,包含这个线程下面的连接的一些信息,包括Transactional resources(datasource)、Transaction synchronizations、Current transaction name、Current transaction read-only status、Current transaction isolation level、Actual transaction active。里面提供了绑定数据源,解绑等操作。

3. 使用TransactionFacade将业务与事务管理相关代码抽象

TransactionFacade属于一个外观模式。如,A类有一个方法A(methodA),B类有一个方法B(methodB)。然后创建一个facade类,包含属性A,B,提供方法domethodA()其中调用A方法、domethodB()其中调用B方法。引用到这里就是使用一个TransactionFacade,里面提供两个方法,一个采用事务的方法txService()方法开始时候开始一个事务,业务代码结束之后提交事务;一个采用非事务方式,直接调用业务代码。示意图如下:

怎么下载spring源码_ide

4. @Transaction原理

如果使用注释@Transaction,用于分离事务管理代码和业务代码(例子可以看使用TransactionTemplate进行事务管理例子),业务管理代码去获取有@Transaction注释的method,然后创建要给TransactionTemplate,根据@Transaction的属性设置去设置这个TransactionTemplate,最后调用transactionTemplate.execute(),在execute中调用业务代码。

IOC部分

1. 关于Component注入时候的bean名称问题

根据java.beans.Introspector.decapitalize中的命名规则,注入的bean如果开头两个是大写,就返回原类目,不然就把开头字母变小写,具体代码如下。这就是为什么@Component写在类NewsProvider上面时候不能直接用.getBean("NewsProvider")获取的原因。当然也可以加别名@Component("NewsProvider")。至于原书上面,他的类名是FXNewsProvider开头两个字母是大写,自然没有这个问题。

public static String decapitalize(String name) {
	if (name == null || name.length() == 0) {
		return name;
	}
	if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
		Character.isUpperCase(name.charAt(0))){
		return name;
	}
	char chars[] = name.toCharArray();
	chars[0] = Character.toLowerCase(chars[0]);
	return new String(chars);
}

2. 为什么注册的过程可以将BeanDefinition注册到BeanFactory后再配置依赖等。

因为注册的过程中只是将BeanDefinition放到BeanFactory的beanDefinitionMap中而真正初始化是在getBean()的时候,这也就是为什么BeanFactory默认是懒加载机制。而如果在配置依赖之前,就getBean()也就无法初始化Bean。

3. depends-on的疑惑

为什么不加depends-on就不会实例化那个依赖的对象。因为就像2所说的用xml只是注册绑定bean并没有真正的实例化bean,所以如果没有depends-on,也就不会实例化了。

4. 使用org.springframework.beans.factory.config.CustomEditorConfigurer来自定义propertyEditer

spring高版本中,将传入的map从原本的<Class,?>改为了<Class,Class<? extends PropertyEditor>>也就是说原本的value是先实例化后的,现在无法实例化了,因为我的PropertyEditor实现类没有无参构造函数。所以这个方法不可行。需要采用实现PropertyEditorRegistrar类的方式,override的registerCustomEditors这个函数入参提供的方法存在Class,PropertyEditor形式可以直接实例化自定义的propertyEditer
代码可以看重写propertyediter部分和registrar部分配置bean的xml,其中包括了CustomEditorConfigurer的配置、具体实现在测试在main里面

5.bean的加载过程

主要分为两个阶段,容器启动阶段和实例化阶段。

  1. 容器启动阶段主要是加载Configuration MetaData。通过各种BeanDefinitionReader类来将xml文件映射BeanDefinition,里面包含了类的所有信息,但是不包括类的实例。容器启动最后可以附加一步BeanFactoryPostProcessor,可以将读完的BeanDefinition中的某一些信息进行修改,也就是上面第四点的内容,通常情况下,用于读取.yml中的配置信息,比如一个springboot的项目,启动的时候如果没有.yml就会加载默认的,如果有了的话就覆盖默认的值。
  2. 实例化阶段:下图为BeanFactory中的实例化过程。
  1. 实例化bean,实例化bean主要是根据BeanDefintion中的信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,返回BeanWrapper类型,然后通过这个beanwrapper设置属性,类似于下面的代码。
BeanWrapper newsProvider = new BeanWrapperImpl(provider); 
newsProvider.setPropertyValue("newsListener", listener);
newsProvider.setPropertyValue("newPersistener", persister);
  1. 容器检查Aware接口,同时对于依赖的对象进行实例化。实现Aware接口的类,可以将一些信息放到bean里面,例如:BeanNameAware就可以把类名放到这个bean里面、ApplicationContextAware可以把实现这个Bean的ApplicationContext引用放到里面。
  2. BeanPostProcessor会处理满足要求的实例化后的对象。两个方法前一个就是在Initialization之前执行,后一个在之后,前一个主要用于上一个所说的Aware实现类的修改,或者生成代理对象。如果需要自定义,我们只需要实现BeanPostProcessor类,override它的postProcessBeforeInitialization方法,然后对指定的类进行修改就好了,具体实现可以看PasswordDecodePostProcessor
public interface BeanPostProcessor {
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; 
}
  1. InitializingBean,在上一步结束后检测当前对象是否实现了InitializingBean,如果是就会调用对象的afterPropertiesSet方法,这个主要是用于一些初始化的操作,比如一个类,有init()方法或者初始化一些默认值的方法,就可以实现InitializingBean,并在里面调用init()方法。applicationcontext可以使用init-method在xml中配置初始化方法。
  2. DisposableBean,然后查看是否实现了DisposableBean,如果实现了,就会为该实例注册一个用于对象销毁的回调(callback),也就是在销毁之前先执行这个逻辑。applicationcontext可以使用destroy-method在xml中配置初始化方法。可以用于数据库关闭连接等。在getBean()中只是把这个类注册到disposableBeans中(一个map),如果要摧毁这个bean,还是需要在程序中((ConfigurableListableBeanFactory)container).destroySingletons();

怎么下载spring源码_实例化_02

6. AbstractApplicationContext和ResourceLoader之间的关系

AbstractApplicationContext自身继承了实现了DefaultResourceLoader和ResourcePatternResolver。具体继承实现关系如下图。

同时自身有一个PathMatchingResourcePatternResolver类型的变量,在初始化ResourcePatternResolver的时候因为自身继承了ResourceLoader,会将自身作为参数添加到ResourcePatternResolver中,所以当调用getResources的时候,其实就是调用一个以DefaultResourceLoader(AbstractApplicationContext继承这个)为参数的PathMatchingResourcePatternResolver的getResources方法。

怎么下载spring源码_spring_03

7.Reource里面包含了什么,以及和recourceloader之间的关系

包含了这个文件的信息。包括文件名,file,inputStream,Description,URL。
recourceloader接口中有一个getResource,不同的实现类不同,也就是根据传进来的路径创建一个resource并返回。DefaultResourceLoader中并没有resource属性。

8. 基于注解方式的注入里面都做了什么

根据书中的思路来,首先,当我们给一个类加入@Autowire之后,就可以自动绑定依赖,而这个依赖就需要在xml文件中声明<bean></bean>,同时,容器需要去读取这个bean,就像之前的bean实例化的时候,在实例化bean,并且设置了对象之后,可以使用BeanPostProccessor来重新设置它的field,而spring提供了AutowiredAnnotationBeanPostProcessor实现,来作为读取Autowire的bean,那么我们只需要在xml中添加<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>即可。然后为了简化这两个东西的配置,我们可以通过<context:annotation-config/>配置来同时把AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor注册到容器,同时还会把PersistenceAnnotationBeanPostProcessor和RequiredAnnotationBeanPostProcessor一并进行注册。但是还是需要在这个xml里面配置最基础的<bean id="" class=""></bean>,为了再次简化,我们可以在需要初始化的bean类上面添加@Component加上之后设置扫描路径,扫描指定路径下面的包,如果带有这个@Component就会提取该类的相关信息,构建对应的BeanDefinition,然后把构建完的BeanDefinition注册到容器。classpath-scanning功能的触发是由<context:component-scan>决定的<context:component-scan base-package="..."/>可以使用这种方式来设置路径,当然这个方法中也会注册各种BeanPostProcessor包。

AOP部分

1. 当ProxyFactory作为weaver的时候,为什么需要使用接口类作为代理对象?

IRequestable proxyObject=(IRequestable)weaver.getProxy(); SpringAop在使用代理模式实现代理对象的时候,一般采用动态代理和CGLIB,分别对实现某个接口的类和没有任何接口的目标类进行代理。如果一个类实现了某个接口,那么这个类就会默认采用动态代理的方式,所以返回的也是一个接口的实现类,因此就需要使用接口类作为代理对象,而没有实现任何类型的就会默认采用CGLIB的方式。

书中解释,在代理对象的场景中,接口的具体实现类和这个具体实现类的代理对象是两个不同的对象,我们可以将接口实现类和它的代理对象都强制转化为接口类型,而无法将代理对象类型强制转化为接口实现类类型。如动态代理示例

当然可以通过weaver.setProxyTargetClass(true)将有实现某个接口的类通过CGLIB生成代理对象。

2. ProxyFactory内部的构造

ProxyFactory继承了ProxyCreatorSupport,提供getProxy()方法,getProxy()调用了ProxyCreatorSupport中的createAopProxy(),ProxyCreatorSupport包含属性AopProxyFactory,这个属性是创建Proxy的工厂类,DefaultAopProxyFactory是它的实现,调用AopProxyFactory的createAopProxy方法后,最后根据目标类的类型获得JdkDynamicAopProxy(动态代理)或ObjenesisCglibAopProxy(CGLIB)