源码分析
结构图
配置文件读取图
资源文件处理
BeanDefinition
- 使用idea生成的通过ctrl+shift+alt生成一个类的结构图,再在本页面中右击添加类

- BeanDefinition和
中的属性一一对应的。 - RootBeanDefinition是最常用的实现类
- GenericBeanDefinition是一站式服务类。2.5后的版本加入的
- ChildBeanDefinition是子bean对应rootBeanDefinition
首先将
转换为BeanDefinition再注册到BeanDefinitionRegistry中 理论知识
重要的类
DefaultListableBeanFactory:主要是对注册后的类进行处理,主要继承AbstractAutowireCapableBeanFactory综合AbstractBeanFactory并对Autowire CapableBeanFactory进行实现,实现了BeanDefinitionRegistry定义各种对Definiton的增删改查操作,
- DefaultBeanDefinitionDocumentReader:完成解析和处理的任务。最后将处理得到的BeanDefinitionHolder交给了BeanDefinitionReaderUtils进行注册。
- XMLBeanDefinitionReader:主要是的任务是把XML文件加载到内存中以Document对象的形式存在。
BeanDefinitionReaderUtils:BeanDefinitionHolder有了,Bean工厂也有了,它就负责把BeanDefinitionHolder中的BeanDefinition和BeanName等取出来,然后注册到Bean工厂中。
其他类
ConfigurableListableBeanFactory:是beanfactory的配置清单指定忽略类型和指定接口。
- Resource:UrlResource,ClassPathResource,FileSystemResource,DescriptiveResource,ByteArrayResource
手写部分结构代码
spring的使用的设计模式
代码分析
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("BeanFactoryTest.xml"));//通过入参的路径 实例化一个Resource;初始化path,classLoader,clazz//001 调用入参为Sting的构造函数new ClassPathResource("BeanFactoryTest.xml")//002 实际调用入参是path和ClassLoader的构造函数 作用生成Resourcethis(path, (ClassLoader) null)-->ClassPathResource(String path, @Nullable ClassLoader classLoader)//解析resource为inputSource//001 调用入参为Resource的构造方法XmlBeanFactory(Resource resource) 将上面的生成Resource当作入参//002 实际调用入参是Resource和BeanFactory(BeanFactory为空)this(resource, null);-->XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)//003调用父类DefaultListableBeanFactory的入参是BeanFactory的构造方法super(parentBeanFactory);-->DefaultListableBeanFactory//0031 调用父类AbstractAutowireCapableBeanFactory的入参是BeanFactory的构造方法super(parentBeanFactory);-->AbstractAutowireCapableBeanFactory//00311this();-->AbstractAutowireCapableBeanFactory()//003111 调用AbstractBeanFactory的空参构造函数,会初始化很多属性parentBeanFactory,beanClassLoader,scopessuper();--> AbstractBeanFactory()//003112 自动装配时忽略给定的依赖接口ignoreDependencyInterface(BeanNameAware.class);//003113ignoreDependencyInterface(BeanFactoryAware.class);//003114ignoreDependencyInterface(BeanClassLoaderAware.class);//00312 设置parentBeanFactory的值setParentBeanFactory(parentBeanFactory);//004 调用XmlBeanDefinitionReader的loadBeanDefinitions方法 返回值为int?this.reader.loadBeanDefinitions(resource);-->XmlBeanDefinitionReader.loadBeanDefinitionsload//封装资源文件,将Resource变成EncodedResource然后调用实际的构造函数EncodedResource encodedResource=new EncodedResource(resource)//(new EncodedResource(resource)实际调用的是this(resource, null, null)构造方法 设置resource,encoding,charsetEncodedResource(Resource resource, @Nullable String encoding, @Nullable Charset charset)//调用object的构造方法?super();//005 实际调用入参是EncodedResource的构造方法loadBeanDefinitions(EncodedResource encodedResource)-->XmlBeanDefinitionReader.loadBeanDefinitions//获得已经加载过的资源,用于判断资源是否已经加载过,若加载过抛出BeanDefinitionStoreExceptionSet<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();//从encodedResource获得已经封装的Resource再获得里面的InputStream流InputStream inputStream = encodedResource.getResource().getInputStream();//封装成InputSourceInputSource inputSource = new InputSource(inputStream);//实际调用了InputSource的setByteStream (InputStream byteStream)this.byteStream = byteStream// 核心方法 另起一行doLoadBeanDefinitions(inputSource, encodedResource.getResource())//核心方法doLoadBeanDefinitions(inputSource, encodedResource.getResource())-->XmlBeanDefinitionReader.doLoadBeanDefinitions//解析xmlDocument doc = doLoadDocument(inputSource, resource);//这几个方法是为Document设置属性的//如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向sax驱动器注册一个实例getEntityResolver()//确定要使用的默认EntityResolver。ResourceLoader resourceLoader = getResourceLoader()//如果未指定,则生成默认ResourceEntityResolverreturn new ResourceEntityResolver(resourceLoader)//获得验证xml的模式 返回值为int 代码逻辑:如果手动指定了验证模式,就返回,如果没有指定调用detectValidationMode(resource)getValidationModeForResource(resource)//自动检测验证模式 将resource转换为inputStream 请记住resource基本干不了啥,如果要解析里面的内存,还是要变为inputStreamdetectValidationMode(resource)//真正的获取模式的方法 while ((content = reader.readLine()) != null)读文件如果文件有DOCTYPE就是dtd否则就是xsdthis.validationModeDetector.detectValidationMode(inputStream)//指示是否将工厂配置为生成具有感知名称空间功能的解析器。isNamespaceAware()//返回document对象this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());//注册 返回注册的bean个数 为什么要resource?有些多余。因为doc就是resource转换过来的,猜测需要方便的使用resource的一些属性? 将环境变量设置其中在哪一步?registerBeanDefinitions(doc, resource)//实例化documentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//通过BeanUtils实例化documentReaderBeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass))//记载及注册beandocumentReader.registerBeanDefinitions(doc, createReaderContext(resource));-->DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()//获得root标签Element root = doc.getDocumentElement();//使用root标签进行注册doRegisterBeanDefinitions(root);//处理profile属性 这个属性是用于配置生产环境和开发环境或者测试环境,使用这个可以方便的切换环境<beans profile="dev">String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);//this.delegate = createDelegate(getReaderContext(), root, parent);//解析前处理 留给子类实现preProcessXml(root);//解析 注册 rootparseBeanDefinitions(root, this.delegate);//解析并注册BeanDefinitions 分为两类一个是默认的bean配置 另外一类是<tx:annotation-driven>parseBeanDefinitions(root, this.delegate);//对beans的特殊处理delegate.isDefaultNamespace(root)//对xml的读取parseDefaultElement(ele,delegate)if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT))//对import标签的处理//获取resource属性所表示的路径//解析路径中的系统属性,如"${user.dir}"//判定location是结对路径还是相对路径//如果是绝对路径则递归调用bean的解析过程,进行另一次的解析//如果是相对路径则计算出绝对路径并进行解析//通知监听器解析完成importBeanDefinitionResource(ele);else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT))//对alias标签的处理processAliasRegistration(ele);else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT))//对bean标签的处理 另起一行 核心逻辑processBeanDefinition(ele, delegate);else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT))// 如果bean中有bean,进行递归调用doRegisterBeanDefinitions(ele);//解析后处理 留给子类实现postProcessXml(root);//对bean标签的处理processBeanDefinition(ele, delegate);-->DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)//首先委托BeanDefinitionParserDelegate类进行元素的解析,返回BeanDefinitionHolder类型的实例bdHolder,通过这个方法,bdHolder包括了class,name,id,alias的属性BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)//解析id属性,解析name属性,分割name属性,如果不存在beanName那么根据spring中提供的命名规则为当前bean生成对应的beannameparseBeanDefinitionElement(ele, null)-->BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele,BeanDefinition containingBean)//(1)提前运算的id以及name属性String id = ele.getAttribute(ID_ATTRIBUTE);//别名 nameAttr转List<String> aliases = new ArrayList<>();-->aliases.add(beanClassName);-->String[] aliasesArray = StringUtils.toStringArray(aliases);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//(2)进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);//解析class属性className = ele.getAttribute(CLASS_ATTRIBUTE).trim()//解析parent属性parent = ele.getAttribute(PARENT_ATTRIBUTE);//创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition,代码是创建GenericBeanDefinition设置setBeanClass,setBeanClassNameAbstractBeanDefinition bd = createBeanDefinition(className, parent);//硬编码1解析默认bean的各种属性,主要是解析scope,singlegton,在嵌入beanDifinnition情况下且没有单独指用scope属性则使用父类默认属性,abstract属性,//解析lazy-init,autowire,depency-check,depends-on,autowire-candidate,primary,init-method,destoy-method,factory-method,factory-bean属性parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);//提取descriptionbd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));//解析元数据 <meta key="testStr" value="aaaaa">parseMetaElements(ele, bd);//解析lookup-method属性 <lookup-method name="getBean" bean="teacher"> 动态的将teacher所代表的bean作为getBean的返回值parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析replaced-method属性 动态替换原来的方法parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析构造函数参数 如果指定了index属性和没有指定index属性 解析constructor-arg,封装,将type,name,index一同封装到ConstructorArgumentValues.ValueHolder//类型中并添加到当前BeanDefinition的constructorArgumenValues的indexedArgumenValues中。(当指定indexs时)parseConstructorArgElements(ele, bd);//解析property子元素 对<prooerty name="testStr" value="aaa">提取parsePropertyElements(ele, bd);//解析qualifier子元素 <qualifier type="org.spring.Qualifier" value="qf">通过这个指定注入的Bean的名称,消除歧义。spring容器中匹配的Bean的数量只有一个parseQualifierElements(ele, bd);//(3)如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanNamebeanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);//(4)将获取到的信息封装到BeanDefinitionHolder的实例中return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);//当bdHolder不为空的情况下若存在默认标签下再有自定义属性,还需要再次对自定义标签进行解析bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)//首先获取属性或者元素的命名空间,判断该元素是否适用于自定义标签的解析条件,找出自定义类型所对应的namespacehandler并进一步解析。待具体分析return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);//对bdHolder进行注册BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());//通过1.beanName做唯一标识注册registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());registerBeanDefinition(String beanName, BeanDefinition beanDefinition)-->DefaultListableBeanFactory类的实现//注册前的最后一次验证,这里的验证不同于xml文件验证//主要是对于AbstractBeanFinition属性中的methodOverrides校验//校验methodOverrides是否于工厂方法并称或者methodOverrides对应的方法根本不存在((AbstractBeanDefinition) beanDefinition).validate();//获得已经注册过的bean 主要是判断新的bean是否已经被注册过oldBeanDefinition = this.beanDefinitionMap.get(beanName);//判断对应的beanName已经注册且配置中配置了bean不允许覆盖,则抛出异常isAllowBeanDefinitionOverriding()//因为是全局变量可能有并发现象synchronized (this.beanDefinitionMap)//将要注册bean加入beanDefinitionNames中updatedDefinitions.addAll(this.beanDefinitionNames)+updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;//清除缓存updatedSingletons.remove(beanName);//2.通别名进行注册 String[] aliases = definitionHolder.getAliases();//alisa与beanName相同情况处理,若alias与beanName并名称相同则不需要处理并删除原有的alias//alias覆盖处理,若aliasName已经使用并已经指向了另一个beanName则需要用户的设置处理//alias循环检查。当a->b存在时,若再次出现a->c->b时候会抛出异常//注册aliasregistry.registerAlias(beanName, alias);//最后发出响应事件getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder))
spring的配置
构造注入
<bean id ="injectionService" class="com.imooc.ioc.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
普通注入
<bean id="injectionService" class="com.imooc.service.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
bean的配置项
- id
- Class
- Scope
- Construction
- Properties
- AutoWiring mode
- lazy-inirialization mode
-
bean的自动装配AutoWring
no:不做任何操作
- byName:根据属性名自动装配。此选项将检查容器名称并根据名字查找与属相完全一致的bean,并将其与属相自动装配
- byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不会发生
Construction:与byType方式类似,不同之处在于它应用与构造参数。如果容器没有找到与构造器参数类型一致的bean,那么抛出异常
Resource针对于资源文件的统一接口
UrlResource:URL对应的资源,根据一个URL地址即可构建
- ClassPathResponse:获取类路径下的资源文件
- FileSystemResource:获取文件系统下的资源
- ServletContextResource;ServletContext封装资源,用于访问ServletContext环境下的资源
- InputStreamResource:针对于输入流封装的资源
-
Bean的作用域
singleton:默认的scope,每个scope为singleton的bean都会被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)。
- prototype:bean被定义为在每次注入时都会创建一个新的对象。
- request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。
- session:bean被定义为在一个session的生命周期内创建一个单例对象。
- application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
- websocket:bean被定义为在websocket的生命周期中复用一个单例对象。
常见注解
- @Component,@Repository,@Service,@Controller
- @required
- @Autowired
- @Qualifier
- @Resource
- Transactional当一个方法要有两个事务的话,一个需要事务处理,一个不需要事务处理,需要:.使用public访求;2. 写在外部类中,可被调用; 3. 使用注入的方式进行该方法的执行。
AOP的相关概念
Aspect Oriented programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。
动态代理
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。
- JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。
功能
日志记录
- 性能统计,
- 安全控制,
- 事务处理,
-
IOC
相关概念
切面Aspect 一个关注点的模块化,这个关注点可能会横切多个对象
- 连接点Joinpoint 程序执行过程中的某个特定的点
- 通知Advice 在切面的某个特定点上执行的动作
- 目标对象Target Object 被一个或者多个切面所通知的对象
- AOP代理aop proxy AOP框架创建的对象,用来实现切面契约aspect contract包括通知方法执行等功能
织入Weaving 把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入
Advice的类型
前置通知before advice 在某连接点之前执行的通知,但不能阻止连接点前的执行除非他抛出一个异常
- 返回后通知after returning advice 在某连接点join point正常完成后执行的通知
- 抛出异常后通知after throwing advice 在方法抛出异常退出时执行的通知
- 后通知after(finally)advice 当某连接点退出的时候执行的通知无论是正常返回还是异常退出
环绕通知around adivce 包围一个连接点join point的通知
SpringMVC生成输出的两种方式
resuful ResponseBody
- view视图
SpringMVC输出格式判定
- 通过请求URL后缀:http://myserver/myapp/accounts/list.html 返回html格式
- 通过请求的参数:http://myserver/myapp/accounts/list?format=xls 该设置默认不开启,默认key是format。
- 通过HTTP Header的Accept:Accept:application/xml 优先级由上至下
IOC容器的依赖注入
1、依赖注入发生的时间
2、AbstractBeanFactory通过getBean向IoC容器获取被管理的Bean:
3、AbstractAutowireCapableBeanFactory创建Bean实例对象:
4、createBeanInstance方法创建Bean的java实例对象:
5、SimpleInstantiationStrategy类使用默认的无参构造方法创建Bean实例化对象:
6、populateBean方法对Bean属性的依赖注入:
7、BeanDefinitionValueResolver解析属性值:
8、BeanWrapperImpl对Bean属性的依赖注入:bean的生命周期
-
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
1、实例化一个Bean--也就是我们常说的new;
2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。Bean 加载解析处理注册流程
首先加载resource变为document
- 使用registerBeanDefinitions(doc, resource)进行记录注册的个数
- 使用documentReader.registerBeanDefinitions(doc, createReaderContext(resource))进行注册
- 将doc转换为Element,即获取出配置文件中的beans标签
- 使用doRegisterBeanDefinitions(Element root) 对root进行处理
- 调用parseBeanDefinitions(Element root, BeanDefinitionParserDelegate this.delegate)
- DefaultBeanDefinitionDocumentReader使用for循环遍历beans获得bean
- Node node = root.getChildNodes().item(i);
- ele = (Element) node;
- parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate)解析bean
- importBeanDefinitionResource(ele)
- processAliasRegistration(ele)
- processBeanDefinition(ele,delegate)将的Element对象转化成了BeanDefinitionHolder对象
- BeanDefinitionHolder的创建 bdHolder
- BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());获得beanName
- doRegisterBeanDefinitions(Element root) 判断是不是bean中嵌套bean,进行递归调用
- registry.registerBeanDefintion(beanName,definitionHolder.getBeanDefinition());注册
- oldBeanDefinition = this.beanDefinitionMap.get(beanName);
- this.beanDefinitionMap.put(beanName, beanDefinition);
