1、Spring源码学习
到spring github上下载spring的包下来,并解压
如果用gradlew.bat运行下载的gradle的版本,就会一直报错;我自己换了gradle5就没有问题了。
在源码路径下进入cmd界面,输入gradle cleanidea eclipse执行
2、bean的加载
AbstractBeanFactory.java类的doGetBean方法获取bean。
过程:final String beanName = transformedBeanName(name);获取beanName;
Object sharedInstance = getSingleton(beanName);获取beanName的实例
获取实例的过程:调用了DefaultSingletonBeanRegistry.java类,singletonObjects存储单例;singletonFactories(bean name to ObjectFactory);earlySingletonObjects(存储单例)实例生成前;registeredSingletons注册的单例
ObjectFactory
FactoryBean是spring框架的重要的接口。
BeanPostProcessor接口有两个方法:postProcessBeforeInitialization;postProcessAfterInitialization用于初始化执行
DestructionAwareBeanPostProcessor接口继承BeanPostProcessor接口,并新增了postProcessBeforeDestruction方法用于在bean销毁前执行。
DisposableBean接口destroy方法,用来释放类的资源,销毁类。
InitializingBean 初始化类的接口
BeanFactory接口的结构:
BeanDefinitionRegistry抽象出bean的注册逻辑,而BeanFactory抽象出bean的管理逻辑;BeanDefinitionRegistry依赖于BeanDefinition。
ConfigurationMetaData加载配置;BeanDefinitionReader会对加载的ConfigurationMetaData进行解析和分析,并将分析后的信息组装为相应的BeanDefinition。
三、功能扩展
ApplicationContextAwareProcessor类实现了BeanPostProcessor接口
PropertyPlaceholderConfigurer类解析配置文件中的${}
BeanFactoryPostProcessor任何实现了这个接口的bean都会在加载bean的配置后执行postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法
MessageSource消息处理的接口
ApplicationContext实现的默认行为是在启动时将所有的单例bean提前进行实例化。
实例化的过程就是在方法finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)中完成的。
LifecycleProcessor接口控制bean的生命周期
ApplicationContext包含BeanFactory的所有功能,并提供了扩展功能。
ApplicationContext context = new ClassPathXmlApplicationContext(“com/abc/person.xml”);
setConfigLocations(configLocations);设置配置文件的路径
refresh();解析及功能实现
refresh()方法的功能包括:
prepareRefresh();准备刷新的上下文的环境
obtainFreshBeanFactory();初始化BeanFactory并读取xml文件
prepareBeanFactory(beanFactory);对BeanFactory进行填充
postProcessBeanFactory(beanFactory);子类覆盖方法做额外的处理
invokeBeanFactoryPostProcessors(beanFactory);激活BeanFactory的处理器
registerBeanPostProcessors(beanFactory);注册拦截Bean的处理器
initMessageSource();初始化Message源
initApplicationEventMulticaster();初始化消息广播器
onRefresh();留给子类初始化其他的bean
registerListeners();注册监听器
finishBeanFactoryInitialization(beanFactory);初始化剩余的单实例
finishRefresh();完成刷新,并通知
resetCommonCaches(); 清除缓存
第七章、AOP
AopNamespaceHandler类注册了对应的解析器
BeanDefinitionParser解析接口
jdk和cglib方式的总结:1、如果目标对象实现了接口,使用jdk动态代理实现aop;
2、如果目标对象实现了接口,可以强制使用cglib;
3、如果目标对象没有实现接口,必须使用cglib,spring会自动在jdk和cglib之间切换
第八章、JDBC
execute方法是基础的操作,update、query只是传入不同的PreparedStatementCallback参数来执行不同的逻辑。
PreparedStatement继承了Statement 的所有功能,PreparedStatement 对象已预编译过,安全性及执行速度都高于Statement ,不建议使用Statement 。
Bean的生命周期:对象的创建(Construct)、对象的初始化前(BeforeInit)、对象创建之后(PostConstruct)、对象的初始化(Init)、对象的初始化后(AfterInit)、对象销毁前(PreDestroy)、对象的销毁(Destroy)
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。
Application contexts实现了MessageSource接口,该接口的实现以可插拔的方式提供获取本地化消息的方法。
WebApplicationContext 继承了ApplicationContext 并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext ,因为它能处理主题,并找到被关联的servlet。
Spring 提供了以下 5 中标准的事件:
1.上下文更新事件(ContextRefreshedEvent):该事件会在 ApplicationContext 被初始化或者更新时发布。也可以在调用 ConfigurableApplicationContext 接口中的 refresh()方法时被触发。
2.上下文开始事件(ContextStartedEvent):当容器调用 ConfigurableApplicationContext的 Start()方法开始/重新开始容器时触发该事件。
3.上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext的 Stop()方法停止容器时触发该事件。
4.上下文关闭事件(ContextClosedEvent):当 ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。
5.请求处理事件(RequestHandledEvent):在 Web 应用中,当一个 http 请求(request)结束触发该事件。
Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的:
1、代理模式:在 AOP 和 remoting 中被用的比较多。
2、单例模式:在 spring 配置文件中定义的 bean 默认为单例模式。
3、模板模式:用来解决代码重复的问题。
比如. RestTemplate, JmsTemplate, JpaTemplate。
4、委派模式:Spring 提供了 DispatcherServlet 来对请求进行分发。
5、工厂模式:BeanFactory 用来创建对象的实例,贯穿于 BeanFactory / ApplicationContext
接口的核心理念。
IOC实现原理:
1、加载容器创建resource对象:首先加载一个spring的容器beanfactory,beanfactory的构造方法会创建一个实现了resource接口实例对象。resource对象通过setconfiguration()方法设置spring配置文件位置。resource对象加载完成后,通过一个super关键字创建容器。
2、提取验证模式:容器创建完成后,开始加载配置文件。beanfactory通过一个getinputstream方法拿到配置文件的输入流。在加载之前,需要验证xml的正确性和验证模式。
3、提取信息:将配置文件的信息转化为document对象。document对象负责将类信息转化为spring的特殊结构beandefintion.beandefination会保存bean的属性,是否懒加载,是否单例,是否抽象类,是否私有类等
4、注册:将收集到的所有bean存到一个currenthashmap中,用beanname做key,beandefintion作为value。 如果有相同的key值,并且spring不允许重名。就抛出异常。否则覆盖原来的对象
tomcat在启动web容器的时候会启动一个叫ServletContextListener的监听器,每当在web容器中有ServletContextListener这个接口被实例化的时候,web容器会通知ServletContextListener被实例的对象去执行其contextInitialized()的方法进行相应的业务处理,而spring框架在设计的过程中ContextLoadListener这个类实现了ServletContextListener这个接口,因此每当有ContextLoadListener这个类被实例化的时候,web容器会通知他执行contextInitialized()这个方法。因此在其实例化的过程中,contextInitialized()会被调用,从而进行spring容器的启动与创建的过程中。
2、SpringBoot源码解析
springboot的启动入口SpringApplication.run(Application.class, args);
StopWatch类记录任务开始、结束的时间。
run()方法的流程过程:stopWatch.start();
this.configureHeadlessProperty(); //(设置系统属性)
this.getRunListeners(args); //(生成监听器)
listeners.starting(); //(启动监听器)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //(生成参数)
this.prepareEnvironment(listeners, applicationArguments);//(生成配置的环境对象ConfigurableEnvironment)
this.configureIgnoreBeanInfo(environment); //(设置需要忽略的bean)
this.printBanner(environment); //(生成横幅Banner)
this.createApplicationContext(); //(生成上下文对象ConfigurableApplicationContext)
this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //异常报告集合
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); //预备加载监听等信息(准备容器)
this.refreshContext(context); //与spring的refresh()方法及功能一致(刷新容器)
this.afterRefresh(context, applicationArguments); //刷新容器后的扩展接口
stopWatch.stop(); //统计任务时间
listeners.started(context); //监听启动
this.callRunners(context, applicationArguments);
listeners.running(context); //监听运行
3、SpringCloud组件源码解析
Eureka源码解析
@EnableDiscoveryClient注解包含@Import(EnableDiscoveryClientImportSelector.class)注解,进入EnableDiscoveryClientImportSelector类。
EnableDiscoveryClientImportSelector类的selectImports(AnnotationMetadata metadata)方法。
EurekaClientConfigServerAutoConfiguration类
通过该类@ConditionalOnClass注解可知,EurekaClientConfigServerAutoConfiguration类的产生需要一些条件,需要EurekaInstanceConfigBean.class, EurekaClient.class,ConfigServerProperties.class这三个类先行产生。
消费者和提供者的主要功能点有:服务注册、服务续约、服务下线、服务调用
com.netflix.discovery.DiscoveryClient中:register()服务注册;renew()//服务续约(就是发送当前应用的心跳请求到注册中心);getInstancesByVipAddress(String vipAddress, boolean secure)//服务调用(本质就是获取调用服务名所对应的服务提供者实例信息,包括IP、port等);unregister()//服务下线(发送取消注册的HTTP请求到注册中心)
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider backupRegistryProvider)服务发现的初始化方法
scheduler = Executors.newScheduledThreadPool(2,
new ThreadFactoryBuilder()
.setNameFormat(“DiscoveryClient-%d”)
.setDaemon(true)
.build());
heartbeatExecutor = new ThreadPoolExecutor( //心跳线程池
1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue(),
new ThreadFactoryBuilder()
.setNameFormat(“DiscoveryClient-HeartbeatExecutor-%d”)
.setDaemon(true)
.build());
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue(),
new ThreadFactoryBuilder()
.setNameFormat(“DiscoveryClient-CacheRefreshExecutor-%d”)
.setDaemon(true)
.build()
);
initScheduledTasks()方法执行刷新任务的定时器、客户端默认注册到eureka、执行心跳任务的定时器、将当前服务注册到注册中心
CacheRefreshThread类获取注册信息,客户端定时去刷新服务信息
HeartbeatThread发送HTTP请求到注册中心,如果请求响应失败,则重新调用注册方法
在DiscoveryClient被创建的时候,在其构造方法中,启动了三个线程池,然后分别启动了三个定时器任务:注册当前服务到注册中心;持续发送心跳进行续约任务;定时刷新注册中心注册细信息到本地。
@EnableEurekaServer注解
@Import(EurekaServerMarkerConfiguration.class)// 主要就是注册该类到SpringBoot中
很多的类并不是被显示的加载到容器中,而是通过配置的方式,最经典的方式就是放到META-INF/spring.factories文件中去加载。org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
EnableAutoConfiguration对应的value值列表中的类会在SpringBoot项目启动的时候注册到Spring容器中,那么EurekaServerAutoConfiguration会被默认加载到Spring中,真正的动作应该都在这个类里。
EurekaServerAutoConfiguration中
eurekaServerConfig(EurekaClientConfig clientConfig):创建并加载EurekaServerConfig的实现类,主要是Eureka-server的配置信息
eurekaController():Eureka-server的可视化界面就是通过EurekaController提供的
peerAwareInstanceRegistry(ServerCodecs serverCodecs):接收客户端的注册等请求就是通过InstanceRegistry来处理
eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext):初始化Eureka-server,会同步其他注册中心的数据到当前注册中心
InstanceRegistry类
register(final InstanceInfo info, final boolean isReplication):接收客户端注册请求
cancel(String appName, String serverId, boolean isReplication):接收客户端下线请求
renew(final String appName, final String serverId, boolean isReplication):接收客户端续约请求
服务的注册实际上是将服务信息添加到一个map中,map的key是服务名称,value也是一个map,是提供该服务的所有客户端信息;
服务的续约实际上是获取map中该服务的客户端信息,然后修改其最新更新时间
服务的下线实际上是删除该map中该服务信息,然后修改服务状态
@FeignClient解析
包含的注解:name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现;
url: url一般用于调试,可以手动指定@FeignClient调用的地址;
decode404:当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException;
configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract;
fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口;
fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码;
path: 定义当前FeignClient的统一前缀;
@FeignClient(name = “github-client”,
url = “https://api.github.com”,
configuration = GitHubExampleConfig.class,
fallback = GitHubClient.DefaultFallback.class)
public interface GitHubClient {
@RequestMapping(value = “/search/repositories”, method = RequestMethod.GET)
String searchRepo(@RequestParam(“q”) String queryStr);
/**
* 容错处理类,当调用失败时,简单返回空字符串
*/
@Component
public class DefaultFallback implements GitHubClient {
@Override
public String searchRepo(@RequestParam("q") String queryStr) {
return "";
}
}
}
@EnableFeignClients和@FeignClient两个注解就实现了Feign的功能。
@EnableFeignClients注入FeignClientsRegistrar类
registerDefaultConfiguration(metadata, registry);针对那些在@EnableFeignClients中添加了defaultConfiguration属性的进行操作将这些类定义的bean添加到容器中
registerFeignClients(metadata, registry);注册那些添加了@FeignClient的类或接口
我们最终是向Spring中注册了一个bean,bean的名称就是类或接口的名称(也就是本例中的FeignService),bean的实现类是FeignClientFactoryBean,其属性设置就是我们在@FeignClient中定义的属性。
1)@EnableFeignClients注解将类FeignClientsRegistrar注册到Spring中
2)FeignClientsRegistrar类主要是扫描包路径下的所有类,将带有@FeignClient注解的类或接口注册到Spring中
3)如何注册带有@FeignClient的类或接口呢?就是生成一个BeanDefinitionHolder类,beanName为@FeignClient所在接口的名称,beanDefinition为FeignClientFactoryBean,并将@FeignClient的属性添加到FeignClientFactoryBean中
4)至此,我们已经把带有@FeignClient注解的类或接口注册到Spring中
FeignClientFactoryBean源码结构分析,其实现了FactoryBean接口,那么当从ApplicationContext中获取该bean的时候,实际调用的是其getObject()方法。
getObject():获取容器中的FeignContext实现,默认实现在FeignAutoConfiguration类中;主要使用构造者模式来构建一个Feign(构造者模式构建Feign.Builder);获取负载均衡后的对象;
1.@EnableFeignClients注解将所有带有@FeignClient的类或接口注册到Spring中,注册类为FeignClientFactoryBean
2.FeignClientFactoryBean.getObject()方法返回的是一个代理类,InvocationHandler中包含类中每个方法对应的MethodHandler,也就是SynchronousMethodHandler,方法真正执行就是SynchronousMethodHandler.invoke()方法
3.LoadBalancerFeignClient.execute()方法进行业务的处理,在这一步操作中就用到了ribbon和Hystrix功能
4、tomcat解析
tomcat目录结构及主要文件:bin用于存放tomcat的启动、停止等批处理脚步和shell脚步;conf存放tomcat的相关配置文件;lib服务器依赖库目录;logs默认的日志存放路径;webapps默认的web应用部署目录;work应用jsp代码生成和编译临时目录。
tomcat针对所有拥有生命周期管理特性的组件抽象了一个Lifecycle通用接口,该接口定义了生命周期管理的核心方法。
Lifecycle接口状态图
tomcat定义了pipeline(管道)和Valve(阀门)两个接口。前者用于构造职责链,后者代表职责链上的每个处理器。
Pipeline中维护了一个基础的Valve,始终位于Pipeline的末端,封装了具体的请求处理和输出响应的过程。
修改后的应用服务器
connector要完成如下几项功能:监听服务器端口,读取来自客户端的请求;将请求数据按照指定协议进行解析;根据请求地址匹配正确的容器进行处理;将响应返回客户端。
解决并发问题,加入Executor接口。
服务器的完整设计如图
Server表示整个Servlet容器,因此只有一个Server实例,一个Server包含多个Service;
Service表示一个或多个Connector的集合,这些Connector共同享用Container来处理其请求。
Connector用于监听并转化Socket请求,读取Socket请求交由Container处理,支持不同协议以及不同的I/O方式。
Container能够执行客户端请求并返回响应的一类对象。不同级别的容器Engine、Host、Context、Wrapper。
Engine表示整个Servlet引擎。
Host表示Servlet引擎中的虚拟机,与服务器的网络名有关。
Context表示ServletContext,一个ServletContext表示一个独立的Web应用
Wrapper表示Web应用中Servlet
Executor表示Tomcat组件之间可以共享的线程池
服务器启动时序图
Tomcat加载器
应用服务器通常会自行创建类加载器以实现更灵活的控制,一方面对规范的实现,另一方面也有架构层面的考虑。
1、隔离性:web应用类库相互隔离,避免依赖库或者应用包相互影响。
2、灵活性:应用之间的类加载器相互独立,针对一个应用进行重新部署,此时该应用的类加载器将会重新创建,而且不会影响其他应用。
3、性能:应用在加载类时,不会搜索其他web应用包含的jar包,性能自然高于 应用服务器只有一个类加载器的情况。
Common:以System为父类加载器,默认指向catalina_home/lib下的包,负责加载服务器内部和web应用可见的类。
Catalina:以Common类为父类加载器,负责加载只有tomcat应用服务器内部可见的类。
Shared:以Common类为父类加载器,负责加载web应用共享的类,这些类tomcat服务器不会依赖
Web应用:以Shared类为父类加载器,加载/web-inf/classes目录下的class和资源文件以及jar包。该类加载器只对当前Web应用可见,对其他Web应用均不可见。
spring的传播特性: PROPAGATION_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY, PROPAGATION_REQUIRES_NEW, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER, PROPAGATION_NESTED
事务隔离级别: ISOLATION_DEFAULT,ISOLATION_READ_UNCOMMITTED,ISOLATION_READ_COMMITTED,ISOLATION_REPEATABLE_READ,ISOLATION_SERIALIZABLE
事务包spring-tx.jar
事务的定义接口:TransactionDefinition
TransactionAttribute接口继承TransactionDefinition
TransactionDefinition 的一个实现类:DefaultTransactionDefinition
TransactionAttribute的实现类:DefaultTransactionAttribute
事务模板类TransactionTemplate核心是里面有PlatformTransactionManager 这个事务管理类,用它来对事务提交和回滚。我们的业务逻辑只要写在TransactionCallback.doInTransaction()方法里面既可以,每次执行这个方法前,先会transactionManager.getTransaction(this)开启一 个事务,执行TransactionCallback.doInTransaction()异常的话会调用transactionManager.rollback(status)来回滚事务,正确的话就会调用transactionManager.commit(status)提交事务
AOP核心概念如下:
通知(Advice):定义了切面(各处业务代码中都需要的逻辑提炼成的一个切面)做什么what+when何时使用。例如:前置通知Before、后置通知After、返回通知After-returning、异常通知After-throwing、环绕通知Around.
连接点(Joint point):程序执行过程中能够插入切面的点,一般有多个。比如调用方式时、抛出异常时。
切点(Pointcut):切点定义了连接点,切点包含多个连接点,即where哪里使用通知.通常指定类+方法 或者 正则表达式来匹配 类和方法名称。
切面(Aspect):切面=通知+切点,即when+where+what何时何地做什么。
引入(Introduction):允许我们向现有的类添加新方法或属性。
织入(Weaving):织入是把切面应用到目标对象并创建新的代理对象的过程。
AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,复写registerBeanDefinitions方法
申明式事务,核心流程如下:
1.createTransactionIfNecessary():如果有必要,创建事务
2.InvocationCallback的proceedWithInvocation():InvocationCallback是父类的内部回调接口,子类中实现该接口供父类调用,子类TransactionInterceptor中invocation.proceed()。回调方法执行
3.异常回滚completeTransactionAfterThrowing()
AbstractPlatformTransactionManager实现了getTransaction()方法
TransactionSynchronizationManager事务同步管理器,该类维护了多个线程本地变量ThreadLocal
SqlSessionSynchronization是SqlSessionUtils的一个内部类,继承自TransactionSynchronizationAdapter抽象类,实现了事务同步接口TransactionSynchronization。
TransactionSynchronization接口定义了事务操作时的对应资源的(JDBC事务那么就是SqlSessionSynchronization)
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
JDBC 事务是用 Connection 对象控制的。JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。 JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。