spring源码7层循环(Spring源码深度解析)
本文目录一览:
- 1、spring循环依赖及解决方式是什么?
- 2、怎么阅读spring源码
- 3、spring工作原理
- 4、spring循环依赖及解决方式
- 5、怎么阅读Spring源码
- 6、看透springmvc源代码分析与实践 怎么样
spring循环依赖及解决方式是什么?
发生在beanA依赖于另一个beanB时,beanB依赖于beanA。
当Spring上下文加载所有bean时,它会尝试按照它们完全工作所需的顺序创建bean。例如,如果我们没有循环依赖,如下例所示:
A-B-C。
Spring将创建beanC,然后创建beanB(并将bean注入其中),然后创建beanA(并将beanB注入其中)。
但是,当具有循环依赖时,Spring无法决定应该首先创建哪个bean,因为它们彼此依赖。在这些情况下,Spring将在加载上下文时引发BeanCurrentlyInCreationException。
使用构造函数注入时,它可能发生在Spring中;如果您使用其他类型的注入,则不应该发现此问题,因为依赖项将在需要时注入,而不是在上下文加载时注入。
简介
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架。
Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。
怎么阅读spring源码
从HttpServletBean的init()进入,再到initWebApplicationContext(),再到refresh(),再到refreshBeanFactory(),再到finishRefresh(),直到服务器启动成功。不知道读了多少遍,
但是源码的东西实在的太多了,想要完全读懂,完全理清头绪,还差很远啊。所以我只重点关注了两块内容,就是bean的定位加载解析注册、bean的实例化两大块内容,其他地方稍作了解,没有太过深入。
整个容器的启动流程,都在AbstractApplicationContext的refresh()的模板方法中了。
复制代码
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 // Prepare this context for refreshing.
4 prepareRefresh();
5
6 // Tell the subclass to refresh the internal bean factory.
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8
9 // Prepare the bean factory for use in this context.
10 prepareBeanFactory(beanFactory);
11
12 try {
13 // Allows post-processing of the bean factory in context subclasses.
14 postProcessBeanFactory(beanFactory);
15
16 // Invoke factory processors registered as beans in the context.
17 invokeBeanFactoryPostProcessors(beanFactory);
18
19 // Register bean processors that intercept bean creation.
20 registerBeanPostProcessors(beanFactory);
21
22 // Initialize message source for this context.
23 initMessageSource();
24
25 // Initialize event multicaster for this context.
26 initApplicationEventMulticaster();
27
28 // Initialize other special beans in specific context subclasses.
29 onRefresh();
30
31 // Check for listener beans and register them.
32 registerListeners();
33
34 // Instantiate all remaining (non-lazy-init) singletons.
35 finishBeanFactoryInitialization(beanFactory);
36
37 // Last step: publish corresponding event.
38 finishRefresh();
39 }
40
41 catch (BeansException ex) {
42 // Destroy already created singletons to avoid dangling resources.
43 destroyBeans();
44
45 // Reset 'active' flag.
46 cancelRefresh(ex);
47
48 // Propagate exception to caller.
49 throw ex;
50 }
51 }
52 }
其实,我并没有上来就看源码,而是先从看书开始,稍微了解,知道了一些关键点,关键流程,自己产生了一堆疑问,然后带着疑问去读源码,读着读着,发现有些疑问就这么解决了。
spring工作原理
Spring的工作原理是让一个对象的创建不用new就可以自动的生产,在运行时与xml Spring的配置文件来动态的创建对象和调用对象,而不需要通过代码来关联。
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。
spring特点是1.方便解耦,简化开发。2.AOP编程的支持。3.声明式事务的支持。4.方便程序的测试。5.方便集成各种优秀框架。6.降低Java EE API的使用难度。7.Java 源码是经典学习范例。
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
spring循环依赖及解决方式
先调用构造函数进行实例化,然后填充属性。
进行其他附加操作和初始化,正是这样的生命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说,三级缓存解决了Bean之间的循环依赖。我们从源码中来说明。
从上面三级缓存的分析,我们可以知道,Spring解决循环依赖的诀窍就在于singletonFactories这个三级cache。这个cache的类型是ObjectFactory。
怎么阅读Spring源码
如何使用jar包以及源码的source包
首先,在工程右键,属性中,添加必要的jar包。
选中必要的jar包,上面给出的源码jar包中,导入spring3.0.5中的所有jar包。
其中lib内的是spring的jar包,用到哪个导入哪个,不知道的话,全部导入就行了。
外面的几个jar包,用于日志以及mysql的驱动。commons-logging jar包是必须的,其他的随意吧。
不确定的话,lib外面的这几个jar包以及lib里面的都导入就行了。
导入jar包后,点开jar包,选中source attachment进行编辑,链接到源码的jar包。
选择相应的source源码包
全部导入后,如下
spring样例
首先是一个必要的POJO类,用于注入属性的值。
1 package com.test.bean;
2
3 public class Person {
4
5 private String name;
6 private int age;
7
8 public String getName() {
9 return name;
10 }
11 public void setName(String name) {
12 this.name = name;
13 }
14 public int getAge() {
15 return age;
16 }
17 public void setAge(int age) {
18 this.age = age;
19 }
20 public void info(){
21 System.out.println("name:"+getName()+" age:"+getAge());
22 }
23 }
主函数,用于读取资源文件,获取bean,调用info方法
1 package test.spring;
2
3 import org.springframework.context.ApplicationContext;
4 import org.springframework.context.support.ClassPathXmlApplicationContext;
5
6 import com.test.bean.Person;
7
8 public class Test {
9 public static void main(String[] args){
10 ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");//读取bean.xml中的内容
11 Person p = ctx.getBean("person",Person.class);//创建bean的引用对象
12 p.info();
13 }
14 }
bean.xml用于配置bean的资源文件
1 ?xml version="1.0" encoding="UTF-8"?
2 beans xmlns:xsi=""
3 xmlns=""
4 xsi:schemaLocation="
5 "
6 bean id="person" class="99a8-f3e2-2db0-4640 com.test.bean.Person"
7 property name="name" value="xingoo"/
8 property name="age" value="12"/
9 /bean
10 /beans
运行结果
阅读源码
首先,有了前面的jar包以及源码包,你就可以通过这个简单的程序,进行但不的调试,阅读源码。
简单的说下调试的快捷键:
1F5:下一步,可以进入下一个函数栈
2F6:当前函数的下一步,不会进入其他的函数。
3F8:下一个断点。
4 也可以通过选中一个变量或者表达式,按ctrl+shift+i来查看内容。或者添加监视的方式,查看。
5 可以通过快捷键F2,来查看一个函数方法的javadoc,即说明
6 快捷键F3或者ctrl+鼠标点击,进入一个函数
7ctrl+shift+G查看当前方法都有谁在使用
8F4查看类的继承关系,可以向上显示出类继承的父类以及接口。
有了调试的方法,接下来,就是如何阅读源码了!
1 参考书籍,推荐《Spring技术内幕》
这本书,基本上很详细的讲述了,spring的实现方式,以及类之间的复杂关系。可以帮助你快速的理清复杂的类之间的关系。
2 使用StarUML画类图
比如你好不容易理清了一个部分的关系,很快就会忘记其中的关系,那么可以通过这个工具,简单的画出其中的复杂关系。
这样,下一次看的时候也会清楚一些,这是我今天刚画好的ClassPathXmlApplicationContext的类图关系:
看透springmvc源代码分析与实践 怎么样
Tomcat 里的Server由org.apache.catalina.startup.Catalina来管理
,Catalina是整个Tomcat的管理类,它里面的三个方法load,start,stop分别用来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建Server并调用
Server的init方法进行初始化,start
方法用于启动服务器,stop方法用于停止服务器,start和stop方法在内部分别调用了Server的start
和stop方法,load方法内部调用了Server的init方法,这三个方法都
会按容器的结构逐层调用相应的方法,比如,Server的start方法中会调用
所有的Service中的start方法,Service中的start方法又会调用所包含的connectors和container的start方法,这样整个服务器就启动了,init和stop方法也一样,这就是tomcat生命周期的管理方式。
Catalina中还有个await方法很重要,此方法直接调用 了Server的await方法,这个方法的作用是进入一个循环,让主线程不会退出。
Tomcat的入口org.apache.catalina.startup.Bootstrap.main(),
Bootstrap的作用类似一个CatalinaAdaptor,具体过程还是使用Catalina来完成,这么做的好处是可以把启动的入口和具体的管理类分开,这样可以就可以方便创建出多种启动方式 ,每种启动方式只需要写一个相应的CatalinaAdaptor就可以了。
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用的Bootstrap的main方法,其代码如下:
// org.apache.catalina.startup.Bootstrap
public static void main(String args[]) {
// 先新建一个Bootstrap
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
//初始化了ClassLoader,并用ClassLoader创建了Catalina实例,赋给catalinaDaemon变量
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
try {
String command = "start";
if (args.length 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
if (t instanceof InvocationTargetException
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
可以看到这里的main非常简单,只有两部分内容:首先新建了Bootstrap,并执行init方法初始化;然后处理main方法传入的命令,如果args参数为空,默认执行start。
在init方法里初始化了ClassLoader,并用ClassLoader创建了Catalina实例,然后赋给catalinaDaemon变量,后面对命令的操作都要使用catalinaDaemon来具体执行。
对start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。这三个方法内部都调用了Catalina的相应方法进行具体执行,只不过是用反射来调用的。start方法(另外两个方法会处理一些参数,调用方法类似)的代码如下:
// org.apache.catalina.startup.Bootstrap
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
这里首先判断catalinaDaemon有没有初始化,如果没有则调用init方法对其进行初始化,然后使用Method进行反射调用Catalina的start方法。Method是java.lang.reflect包里的
类,代表一个具体的方法,可以使用其中的invoke方法来执行所代表的方法,invoke方法有两个参数,第一参数是Method方法所在的实体,第二个参数是可变参数用于Method方法执行时所需要的参数,所以上面的调用相当于((Catalina)catalinaDaemon).start()。setAwait和load也用类似的方法调用了Catalina中的setAwait和load方法。
7.1.3 Catalina的启动过程
从前面的内容可以知道,Catalina的启动主要是调用setAwait、load和start方法来完成的。setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start方法用于启动服务器。下面分别来看一下这三个方法。
首先来看setAwait方法,代码如下:
// org.apache.catalina.startup.Catalina
public void setAwait(boolean b) {
await = b;
}
这个方法非常简单,就是设置await属性的值,await属性会在start方法中的服务器启动完之后使用它来判断是否进入等待状态。
Catalina的load方法根据conf/server.xml创建了Server对象,并赋值给server属性(具体解析操作是通过开源项目Digester完成的),然后调用了server的init方法,代码如下:
// org.apache.catalina.startup.Catalina
public void load() {
long t1 = System.nanoTime();
// 省略创建 server代码,创建过程使用Digester完成
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
//启动过程中,控制台可以看到
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
Catalina的start方法主要调用了server的start方法启动服务器,并根据await属性判断是否让程序进入了等待状态,代码如下:
//org.apache.catalina.startup.Catalina
public void start() {
if (getServer() == null) {
load();
}
long t1 = System.nanoTime();
try {
// 调用Server的start方法启动服务器
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// 此处省略了注册关闭钩子代码
// 进入等待状态
if (await) {
await();
stop();
}
}
这里首先判断Server是否已经存在了,如果不存在则调用load方法来初始化Server,然后调用Server的start方法来启动服务器,最后注册了关闭钩子并根据await属性判断是否进入等待状态,之前我们已将这里的await属性设置为true了,所以需要进入等待状态。进入等待状态会调用await和stop两个方法,await方法直接调用了Server的await方法,Server的await方法内部会执行一个while循环,这样程序就停到了await方法,当await方法里的while循环退出时,就会执行stop方法,从而关闭服务器。
7.1.4 Server的启动过程
Server接口中提供addService(Service service)、removeService(Service
service)来添加和删除Service,Server的init方法和start方法分别循环调用了每个Service的init方法和start方法来启动所有Service。
Server的默认实现是org.apache.catalina.core.StandardServer,StandardServer继承自Lifecycle-MBeanBase,LifecycleMBeanBase又继承自LifecycleBase,init和start方法就定义在了LifecycleBase中,LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法,这两个方法都是模板方法,由子类具体实现,所以调用StandardServer的init和start方法时会执行StandardServer自己的initInternal和startInternal方法,这就是Tomcat生命周期的管理方式,更详细的过程见7.2节。StandardServer中的initInternal和startInternal方法分别循环调用了每一个service的start和init方法,代码如下:
//org.apache.catalina.core.StandardServer
protected void startInternal() throws LifecycleException {
……
synchronized (servicesLock) {
for (int i = 0; i services.length; i++) {
services[i].start();
}
}
}
protected void initInternal() throws LifecycleException {
……
for (int i = 0; i services.length; i++) {
services[i].init();
}
}
除了startInternal和initInternal方法,StandardServer中还实现了await方法,Catalina中就是调用它让服务器进入等待状态的,其核心代码如下:
//org.apache.catalina.core.StandardServer
public void await() {
// 如果端口为-2则不进入循环,直接返回
if( port == -2 ) {