Spring Ioc源码深入剖析预备知识
Posted 赵广陆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Ioc源码深入剖析预备知识相关的知识,希望对你有一定的参考价值。
目录
1 Spring源码环境编译与构建
工欲善其事 必先利其器
源码调试搭建步骤:
七大步骤
1.1 自动化构建gradle介绍
引言:
为什么要讲解gradle?
因为从Sping5开始,官方就开始使用gradle来构建环境了
接下来,我们所有的环境都要基于gradle
什么是gradle
Gradle是一个项目自动化构建工具。
是Apache的一个基于Ant 和Maven的软件,用于项目的依赖管理。
项目的构建经历了三个时代:
引言:
为什么要讲解gradle?
因为从Sping5开始,官方就开始使用gradle来构建环境了
接下来,我们所有的环境都要基于gradle
Apache Ant(2000 年左右)
Maven(2004年)
Gradle(2012 年左右)
Spring(5.0开始) 等优秀的开源项目都将自己的项目从 Maven 迁移到了 Gradle
Google 官方 android 开发的 IDE Android Studio 也默认使用了 Gradle 进行构建。
1.2 源码环境搭建与编译
四大步骤
1)安装gralde环境
下载gradle,建议不要下载太高的版本,版本太高,后面在idea中编译的时候有些spring的组件下载不
了
https://services.gradle.org/distributions/
https://gradle.org/releases/
gralde的安装非常简单,就和配置jdk一样的道理,将主目录配置到环境变量即可
设置JAVA_HONME和GRADLE_HOME
然后cmd打开窗口,测试一下是否安装成功
gradle -V
tips:常见问题
如果不能正常查看版本号,说明jdk版本太高,需要将JAVA_HOME修改为8
2)下载Spring源码
进入Spring官网,然后从下面的窗口进入到github
https://spring.io/projects/spring-framework#learn
直接从github下载
https://github.com/spring-projects/spring-framework
3)源码环境编译
gradlew.bat
执行项目下的gradlew.bat, 等待构建完成…
4)IDEA导入源码
注意:这里必须使用import Project,不能使用open形式打开
然后点击 Finish 就会进入项目的自动构建阶段了。
Spring 构建完成(耗时比较长),如下图所示
正在编译
关键步骤
如果不运行当前的compileTestJava,会有很多类找不到
手工删除上面的 build;如果遇到找不的class依赖,右键run
5)常见错误
如果构建非常慢,可以在C:\\用户\\下创建【init.gradle】文件
allprojects
repositories
maven
name "ALIYUN_CENTRAL_URL" // name 可以不需要
url 'https://maven.aliyun.com/nexus/content/repositories/central'
maven
name "ALIYUN_JCENTER_URL"
url 'https://maven.aliyun.com/nexus/content/repositories/jcenter'
maven
name "ALIYUN_GOOGLE_URL"
url 'https://maven.aliyun.com/nexus/content/repositories/google'
2 构建gradle源码测试项目
2.1 开始搭建测试项目
四步:
1、新建测试module项目
首先我们在 Spring 源码项目中新增一个测试项目,点击 New -> Module… 创建一个 Gradle 的 Java 项目
2、详细信息
3、设置gradle
4、完善信息
在 build.gradle 中添加对 Spring 源码的依赖:
compile(project(‘:spring-context’))
spring-context 会自动将 spring-core、spring-beans、spring-aop、spring-expression 这几个
基础 jar 包带进来。
接着,我们需要在项目中创建一个 bean 和配置文件(application.xml)及启动文件(Main.java)
接口如下:
package com.spring.test.service;
public interface UserService
public String getName();
实现类
package com.spring.test.impl;
import com.spring.test.service.UserService;
public class UserServiceImpl implements UserService
@Override
public String getName()
return "Hello World";
Main代码如下
public class Main
public static void main(String[] args)
ApplicationContext context =newClassPathXmlApplicationContext("classpath*:application.xml");
UserService userService = context.getBean(UserService.class);
System.out.println(userService);
// 这句将输出: hello world
System.out.println(userService.getName());
配置文件 application.xml(在 resources 中)配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.spring.test.impl.UserServiceImpl"/>
</beans>
运行
输出如下
com.spring.test.impl.UserServiceImpl@2aa5fe93
Hello World
3 Spring基本概念回顾
3.1 Spring中的基本概念
<bean />
标签中可以定义哪些属性
Property | |
---|---|
class | 类的全限定名 |
name | 可指定 id、name(用逗号、分号、空格分隔) |
scope | 作用域 |
constructor arguments | 指定构造参数 |
properties | 设置属性的值 |
autowiring mode | no(默认值)、byName、byType、 constructor |
lazy-initialization mode | 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了) |
initialization method | bean 属性设置完成后,会调用这个方法 |
destruction method | bean 销毁后的回调方法 |
与xml对应
<bean id="exampleBean" name="name1, name2, name3" class="com.spring.test"
scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup">
<!-- 可以用下面三种形式指定构造参数 -->
<constructor-arg type="int" value="7500000"/>
<constructor-arg name="years" value="7500000"/>
<constructor-arg index="0" value="7500000"/>
<!-- property 的几种情况 -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
作用域扩展
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。 |
application | 限定一个Bean的作用域为ServletContext 的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。 |
tips:
单例模式的bean,在初始化容器时就会创建此对象。
而多例即原型的bean则在使用时创建
BeanFactory与FactoryBean区别和联系?
1、BeanFactory是ioc容器的顶级接口
2、FactoryBean是一个普通的接口
3、BeanFactory能够生产和管理Bean
4、FactoryBean只能生产bean(实现Factory接口)
为什么会有FactorBean
BeanFactory只能通过工厂生产标准的bean
FactoryBean可以自定义复杂的bean
虽然spring的对象都是有ioc进行管理,开发人员不会new,此处是个例外
@Component
public class FactoryBeanTest implements FactoryBean
@Override
public Object getObject() throws Exception
//这个Bean是我们自己new的,这里我们就可以控制Bean的创建过程了
return new FactoryBeanServiceImpl();
....略
Bean生命周期
spring的生命周期其实是一个很简单的过程,从配置文件一开始,它就会针对你的这个bean做一系列的操作
(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
(3)处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
③如果这个Bean已经实现了ApplicationContextAware接口,会调setApplicationContext(ApplicationContext)方法,传入Spring上下文;
(4)BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
(5)InitializingBean 与 init-method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
(8)destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
org.springframework.beans.factory.BeanFactory
以下是完整的调用链(对应3-8步骤)
* 下面描述了详细的生命周期
* <p>Bean factory implementations should support the standard bean lifecycle interfaces
* as far as possible. The full set of initialization methods and their standard order is:
* <ol>
* <li>BeanNameAware's @code setBeanName
* <li>BeanClassLoaderAware's @code setBeanClassLoader
* <li>BeanFactoryAware's @code setBeanFactory
* <li>EnvironmentAware's @code setEnvironment
* <li>EmbeddedValueResolverAware's @code setEmbeddedValueResolver
* <li>ResourceLoaderAware's @code setResourceLoader
* (only applicable when running in an application context)
* <li>ApplicationEventPublisherAware's @code setApplicationEventPublisher
* (only applicable when running in an application context)
* <li>MessageSourceAware's @code setMessageSource
* (only applicable when running in an application context)
* <li>ApplicationContextAware's @code setApplicationContext
* (only applicable when running in an application context)
* <li>ServletContextAware's @code setServletContext
* (only applicable when running in a web application context)
* <li>@code postProcessBeforeInitialization methods of BeanPostProcessors
* <li>InitializingBean's @code afterPropertiesSet
* <li>a custom init-method definition
* <li>@code postProcessAfterInitialization methods of BeanPostProcessors
* </ol>
Aware系列接口的共性
- 都以“Aware”结尾
- 都是Aware接口的子接口,即都继承了Aware接口
- 接口内均定义了一个set方法
Aware系列接口,主要用于辅助Spring bean访问Spring容器
3.2 Spring AOP和AspectJ
十个人有九 个人弄不懂的关系
引言:
为什么要讲解Spring AOP和AspectJ?
首先,我们先要搞明白,在spring里面的动态代理有几种实现?
他们之间的关系是什么?比如;能否单独使用?能否两者结合使用?
什么情况下单独使用?什么情况下结合使用
不同的代理方式织入时机(编译、类加载、运行期)和代理机制(jdk、cglib)是什么
弄明白这些;我们才能知道我们的接口
或者我们的类他的代理机制到底是通过的jdk还是cglib
只有这样,我们才能追本溯源
找到各自的代理对象是如何生成的
Spring AOP:
Spring AOP旨在通过Spring IoC提供一个简单的AOP实现,以解决编码人员面临的最常出现的问题。这并不是完整的AOP解决方案,它只能用于Spring容器管理的beans。
AspectJ:
AspectJ是最原始的AOP实现技术,提供了完整的AOP解决方案。AspectJ更为健壮,相对于Spring AOP也显得更为复杂
区别:AspectJ使用的是编译期和类加载时进行织入,Spring AOP利用的是运行时织入
概念总结
AOP是面向切面的一个思想
他有两种实现
1、Spring AOP
2、Aspectj
Spring AOP的实现没有AspectJ强大
所以,Spring把Aspectj给集成(需要单独引jar)进来了
但是;spring aop已能满足我们的需求
在进行开发时候,这两个框架是完全兼容的
说白了,就是两个框架能一起使用,就看你项目需求用到的哪种程度了,简单的;spirng aop够用了,顶多借助aspectj的注解功能,在高级点,比如切面很多,上万个,这是就要用到aspectj的高级功能了
tips
1)如果使用xml方式,不需要任何额外的jar包。
2)如果使用@Aspect方式,你就可以在类上直接一个@Aspect,然后在xml里加上<aop:aspectj-autoproxy />。但是这需要额外的jar包( aspectjweaver.jar)。因为spring直接使用AspectJ的注解功能,注意只是使用了它 的注解功能而已。并不是核心功能 !无论是使用spring aop还是 aspectj都需要aspectjweaver.jar spring-aop.jar这两个jar包
运行时织入,在使用目标对象的代理执行应用程序时,编译这些切面(使用JDK动态代理或者CGLIB代理
Spring AOP 是一个基于代理的AOP框架。这意味着,要实现目标对象的切面,将会创建目标对象的代理类。这可以通过下面两种方式实现:
- JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。
- CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理
4.3 设计模式之责任链
责任链(Chain of Responsibility):
为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
tips
注意:责任链模式也叫职责链模式
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
职责链模式主要包含以下角色:
- 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
抽象处理者角色
//抽象处理者角色
public abstract class Handler
//下一个处理者
private Handler next;
//设定下一个处理者
public void setNext(Handler next)
this.next = next;
//获取下一个处理者
public Handler getNext()
return next;
//每个处理者都必须实现的处理任务
public abstract void handleRequest(int request);
具体处理者角色–Leader
//具体处理者角色--Leader
public class LeaderApproval extends Handler
public void handleRequest(int request)
if (request == 1)
System.out.println("Leader处理请假请求");
else
//下一处理者不为空
if (getNext() != null)
getNext().handleRequest(request);
else
System.out.println("没有人处理该请求!");
//具体处理者角色–部门经理
//具体处理者角色--部门经理
public class DepartmentManagerApproval extends Handler
public void handleRequest(int request)
if (request > 1 && request <= 5)
System.out.println("部门经理处理请假请求");
else
//下一处理者不为空
if (getNext() != null)
getNext().handleRequest(request);
else
System.out.println("没有人处理该请求!");
//具体处理者角色–技术总监
//具体处理者角色--技术总监
public class CTOApproval extends Handler
public void handleRequest(int request)
if (request>5&&request<=15)
System.out.println("CTO处理请假请求");
else
//下一处理者不为空
if (getNext() != null)
getNext().handleRequest(request);
else
System.out.println("没有人处理该请求!");
//具体处理者角色–总经理
//具体处理者角色--总经理
public class CEOApproval extends Handler
public void handleRequest(int request)
if (request>15)
System.out.println("CEO处理请假请求");
else
//下一处理者不为空
if (getNext() != null)
getNext().handleRequest(request);
else
System.out.println("没有人处理该请求!");
责任链测试
//责任链测试
public class Test
public static void main(String[] args)
//1天
Handler handler1 = new LeaderApproval();
//request>1&&request<=5
Handler handler2 = new DepartmentManagerApproval();
//request>5&&request<=15
Handler handler3 = new CTOApproval();
//request>15
Handler handler4 = new CEOApproval();
//此处可以无限扩展控制类型
//创建职责链
handler1.setNext(handler2);
handler2.setNext(handler3);
handler3.setNext(handler4);
//发起一次审批请求
handler1.handleRequest(4);
输出
4 ioC用到的那些设计模式
引言:
为什么要讲设计模式(不是源码课程吗?)
1、Spring中使用了大量的设计模式
2、如果不懂设计模式,你肯定读不懂(部分)Spring的源码,原因就是你打断点跟到某一行,值就初始化
完了,究竟在哪里初始化完的?你找不到,为什么,因为你不懂设计模式,找不到那个地方设计模式怎么讲?
剖析spring源码过程中; 遇到一个设计模式,就讲一个设计模式
4.1 设计模式之工厂
工厂模式(Factory Pattern)提供了一种创建对象的最佳方式。
工厂模式(Factory Pattern)分为三种
1、简单工厂
2、工厂方法
3、抽象工厂
4.1.1 简单工厂模式
ApplicationContext context =new ClassPathXmlApplicationContext("classpath*:application.xml");\\
UserService userService = context.getBean(UserService.class);
简单工厂模式对对象创建管理方式最为简单,因为其仅仅简单的对不同类对象的创建进行了一层简单的
封装
定义接口IPhone
public interface Phone
void make();
实现类
public class IPhone implements Phone
public IPhone()
this.make();
@Override
public void make()
// TODO Auto-generated method stub
System.out.println("生产苹果手机!");
实现类
public class MiPhone implements Phone
public MiPhone()
this以上是关于Spring Ioc源码深入剖析预备知识的主要内容,如果未能解决你的问题,请参考以下文章
深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析IOC容器核心流程以及运作原理