b2c信息网

您现在的位置是:首页 > 热点事件 > 正文

热点事件

spring源码7层循环(Spring源码深度解析)

hacker2022-06-09 01:13:25热点事件92
本文目录一览:1、spring循环依赖及解决方式是什么?

本文目录一览:

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 ) {

发表评论

评论列表

  • 余安僚兮(2022-06-09 02:03:52)回复取消回复

    命周期,才有了Spring的解决循环依赖,这样的解决机制是根据Spring框架内定义的三级缓存来实现的,也就是说,三级缓存解决了Bean之间的循环依赖。我们从源码中来说明。