作者:zuoxiaolong8810(左潇龙),转载请注明出处。
上一章当中我没有提及具体的搭建环境的步骤,一个是不得不承认有点懒,另外一个我觉得如果上章所述的那些环境都还不会搭建的话,研究spring的源码还有些过早。
如果你有兴趣的话,相信已经搭建好了学习研究的环境,接下来就可以进入正题了。
网上也有很多关于spring源码学习的文章以及帖子,讲的也都不错,但是有些可能高估了读者的能力,该深入的地方反倒一句带过,我现在也是在一步一步研究,和大家的进度一样,所以可能在我的角度来和各位探讨,更加容易。
首先我们来说一下IOC,IOC是spring最核心的理念,包括AOP也要屈居第二,那么IOC到底是什么呢,四个字,控制反转。
网上有不少是这么解释IOC的,说IOC是将对象的创建和依赖关系交给容器,这句话我相信不少人都知道,在我个人的理解,IOC就是让我们的开发变的更简单了。
为什么这么说呢,光说没意思,直接上代码。
public class Person { public void work(){ System.out.println("I am working"); } }
上面这个是Person类,如果我们还有一个Company公司类,公司要开张需要人来工作,所以我们可能需要这样。
public class Company { public Person person; public Company(Person person){ this.person = person; } public void open(){ person.work(); System.out.println("I am opening"); } }
好了,这样可以了,虽说和现实有些区别,毕竟没有一个人的公司,但是就是这么个意思。必须要有人在公司里,公司才能开张。
有了spring上述情况我们是怎么写的呢?Person类不变,Company就可以简单些了。
public class Company { @Autowired public Person person; public void open(){ person.work(); System.out.println("I am opening"); } }
OK了,使用注解后,spring里的写法是这样的,是不是简单很多?或许你可能会说,这才减少了多少代码,但是事实上是,真正的项目中,不可能有这么简单的依赖关系,或许是2层,3层甚至N层。
当然,可能我们有时候用的XML,XML和注解的区别就在于这里,注解可以快速的完成依赖的注入,但是缺点也很明显,那就是比如我公司里不需要人了,我需要的是机器,那么我还要手动改代码,将Person换成机器(这里应该是英文,英语不好,懒得查了,只记得念“磨洗”),而如果是XML配置,那么我们只需要改下配置文件就可以。维护起来会方便很多,当然XML的缺点也很明显,那就是依赖关系复杂的时候,XML文件会比较臃肿,所以我们一般的做法是将XML分离开来。
说到这里,有些扯远了,但是我觉得以上可以足够说明IOC的好处,知道了IOC的好处,我们自然就要知道怎么来实现IOC了。
或许看了spring的源码,第一感觉是很蒙,包太多,我也很蒙,但是研究东西就是得沉下心来,先来关注一下这个接口,BeanFactory,附上代码。
/* * Copyright 2002-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.beans.factory; import org.springframework.beans.BeansException; /** * The root interface for accessing a Spring bean container. * This is the basic client view of a bean container; * further interfaces such as {@link ListableBeanFactory} and * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} * are available for specific purposes. * * <p>This interface is implemented by objects that hold a number of bean definitions, * each uniquely identified by a String name. Depending on the bean definition, * the factory will return either an independent instance of a contained object * (the Prototype design pattern), or a single shared instance (a superior * alternative to the Singleton design pattern, in which the instance is a * singleton in the scope of the factory). Which type of instance will be returned * depends on the bean factory configuration: the API is the same. Since Spring * 2.0, further scopes are available depending on the concrete application * context (e.g. "request" and "session" scopes in a web environment). * * <p>The point of this approach is that the BeanFactory is a central registry * of application components, and centralizes configuration of application * components (no more do individual objects need to read properties files, * for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and * Development" for a discussion of the benefits of this approach. * * <p>Note that it is generally better to rely on Dependency Injection * ("push" configuration) to configure application objects through setters * or constructors, rather than use any form of "pull" configuration like a * BeanFactory lookup. Spring's Dependency Injection functionality is * implemented using this BeanFactory interface and its subinterfaces. * * <p>Normally a BeanFactory will load bean definitions stored in a configuration * source (such as an XML document), and use the <code>org.springframework.beans</code> * package to configure the beans. However, an implementation could simply return * Java objects it creates as necessary directly in Java code. There are no * constraints on how the definitions could be stored: LDAP, RDBMS, XML, * properties file, etc. Implementations are encouraged to support references * amongst beans (Dependency Injection). * * <p>In contrast to the methods in {@link ListableBeanFactory}, all of the * operations in this interface will also check parent factories if this is a * {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance, * the immediate parent factory will be asked. Beans in this factory instance * are supposed to override beans of the same name in any parent factory. * * <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:<br> * 1. BeanNameAware's <code>setBeanName</code><br> * 2. BeanClassLoaderAware's <code>setBeanClassLoader</code><br> * 3. BeanFactoryAware's <code>setBeanFactory</code><br> * 4. ResourceLoaderAware's <code>setResourceLoader</code> * (only applicable when running in an application context)<br> * 5. ApplicationEventPublisherAware's <code>setApplicationEventPublisher</code> * (only applicable when running in an application context)<br> * 6. MessageSourceAware's <code>setMessageSource</code> * (only applicable when running in an application context)<br> * 7. ApplicationContextAware's <code>setApplicationContext</code> * (only applicable when running in an application context)<br> * 8. ServletContextAware's <code>setServletContext</code> * (only applicable when running in a web application context)<br> * 9. <code>postProcessBeforeInitialization</code> methods of BeanPostProcessors<br> * 10. InitializingBean's <code>afterPropertiesSet</code><br> * 11. a custom init-method definition<br> * 12. <code>postProcessAfterInitialization</code> methods of BeanPostProcessors * * <p>On shutdown of a bean factory, the following lifecycle methods apply:<br> * 1. DisposableBean's <code>destroy</code><br> * 2. a custom destroy-method definition * * @author Rod Johnson * @author Juergen Hoeller * @since 13 April 2001 * @see BeanNameAware#setBeanName * @see BeanClassLoaderAware#setBeanClassLoader * @see BeanFactoryAware#setBeanFactory * @see org.springframework.context.ResourceLoaderAware#setResourceLoader * @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher * @see org.springframework.context.MessageSourceAware#setMessageSource * @see org.springframework.context.ApplicationContextAware#setApplicationContext * @see org.springframework.web.context.ServletContextAware#setServletContext * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization * @see InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization * @see DisposableBean#destroy * @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName */ public interface BeanFactory { /** * Used to dereference a {@link FactoryBean} instance and distinguish it from * beans <i>created</i> by the FactoryBean. For example, if the bean named * <code>myJndiObject</code> is a FactoryBean, getting <code>&myJndiObject</code> * will return the factory, not the instance returned by the factory. */ String FACTORY_BEAN_PREFIX = "&"; /** * Return an instance, which may be shared or independent, of the specified bean. * <p>This method allows a Spring BeanFactory to be used as a replacement for the * Singleton or Prototype design pattern. Callers may retain references to * returned objects in the case of Singleton beans. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no bean definition * with the specified name * @throws BeansException if the bean could not be obtained */ Object getBean(String name) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. * <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type * safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the * required type. This means that ClassCastException can't be thrown on casting * the result correctly, as can happen with {@link #getBean(String)}. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to retrieve * @param requiredType type the bean must match. Can be an interface or superclass * of the actual class, or <code>null</code> for any match. For example, if the value * is <code>Object.class</code>, this method will succeed whatever the class of the * returned instance. * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there's no such bean definition * @throws BeanNotOfRequiredTypeException if the bean is not of the required type * @throws BeansException if the bean could not be created */ <T> T getBean(String name, Class<T> requiredType) throws BeansException; /** * Return the bean instance that uniquely matches the given object type, if any. * @param requiredType type the bean must match; can be an interface or superclass. * {@literal null} is disallowed. * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. * @return an instance of the single bean matching the required type * @throws NoSuchBeanDefinitionException if there is not exactly one matching bean found * @since 3.0 * @see ListableBeanFactory */ <T> T getBean(Class<T> requiredType) throws BeansException; /** * Return an instance, which may be shared or independent, of the specified bean. * <p>Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. * @param name the name of the bean to retrieve * @param args arguments to use if creating a prototype using explicit arguments to a * static factory method. It is invalid to use a non-null args value in any other case. * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there's no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but * the affected bean isn't a prototype * @throws BeansException if the bean could not be created * @since 2.5 */ Object getBean(String name, Object... args) throws BeansException; /** * Does this bean factory contain a bean with the given name? More specifically, * is {@link #getBean} able to obtain a bean instance for the given name? * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether a bean with the given name is defined */ boolean containsBean(String name); /** * Is this bean a shared singleton? That is, will {@link #getBean} always * return the same instance? * <p>Note: This method returning <code>false</code> does not clearly indicate * independent instances. It indicates non-singleton instances, which may correspond * to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly * check for independent instances. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean corresponds to a singleton instance * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @see #getBean * @see #isPrototype */ boolean isSingleton(String name) throws NoSuchBeanDefinitionException; /** * Is this bean a prototype? That is, will {@link #getBean} always return * independent instances? * <p>Note: This method returning <code>false</code> does not clearly indicate * a singleton object. It indicates non-independent instances, which may correspond * to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly * check for a shared singleton instance. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return whether this bean will always deliver independent instances * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 2.0.3 * @see #getBean * @see #isSingleton */ boolean isPrototype(String name) throws NoSuchBeanDefinitionException; /** * Check whether the bean with the given name matches the specified type. * More specifically, check whether a {@link #getBean} call for the given name * would return an object that is assignable to the specified target type. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @param targetType the type to match against * @return <code>true</code> if the bean type matches, * <code>false</code> if it doesn't match or cannot be determined yet * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 2.0.1 * @see #getBean * @see #getType */ boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException; /** * Determine the type of the bean with the given name. More specifically, * determine the type of object that {@link #getBean} would return for the given name. * <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates, * as exposed by {@link FactoryBean#getObjectType()}. * <p>Translates aliases back to the corresponding canonical bean name. * Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the name of the bean to query * @return the type of the bean, or <code>null</code> if not determinable * @throws NoSuchBeanDefinitionException if there is no bean with the given name * @since 1.1.2 * @see #getBean * @see #isTypeMatch */ Class<?> getType(String name) throws NoSuchBeanDefinitionException; /** * Return the aliases for the given bean name, if any. * All of those aliases point to the same bean when used in a {@link #getBean} call. * <p>If the given name is an alias, the corresponding original bean name * and other aliases (if any) will be returned, with the original bean name * being the first element in the array. * <p>Will ask the parent factory if the bean cannot be found in this factory instance. * @param name the bean name to check for aliases * @return the aliases, or an empty array if none * @see #getBean */ String[] getAliases(String name); }
这个便是spring核心的Bean工厂定义,上面的author说是2001年写的,已经历史久远了,这个类是spring中所有bean工厂,也就是俗称的IOC容器的祖宗,各种IOC容器都只是它的实现或者为了满足特别需求的扩展实现,包括我们平时用的最多的ApplicationContext。从上面的方法就可以看出,这些工厂的实现最大的作用就是根据bean的名称亦或类型等等,来返回一个bean的实例。
一个工厂如果想拥有这样的功能,那么它一定需要以下几个因素:
1.需要持有各种bean的定义,否则无法正确的完成bean的实例化。
2.需要持有bean之间的依赖关系,否则在bean实例化的过程中也会出现问题。例如上例,如果我们只是各自持有Person和Company,却不知道他们的依赖关系,那么在Company初始化以后,调用open方法时,就会报空指针。这是因为Company其实并没有真正的被正确初始化。
3.以上两种都要依赖于我们所写的依赖关系的定义,暂且认为是XML文件(其实可以是各种各样的),那么我们需要一个工具来完成XML文件的读取。
我目前想到的,只需要满足以上三种条件,便可以创建一个bean工厂,来生产各种bean。当然,spring有更高级的做法,以上只是我们直观的去想如何实现IOC。
其实在上述过程中仍旧有一些问题,比如第一步,我们需要持有bean的定义,如何持有?这是一个问题。我们知道spring的XML配置文件中,有一个属性是lazy-init,这就说明,bean在何时实例化我们是可以控制的。但是这个属性默认为false,也就是说spring容器初始化以后,其实各种bean都还未产生,它们只在需要的时候出现。
所以我们无法直接的创建一个List<Object>来持有这些bean的实例,在这里要注意,我们要储存的是bean的定义,而非实例。
那么接下来,又是一个祖宗级别的接口要出现了,来看BeanDefinition。
/* * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.beans.factory.config; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.MutablePropertyValues; import org.springframework.core.AttributeAccessor; /** * A BeanDefinition describes a bean instance, which has property values, * constructor argument values, and further information supplied by * concrete implementations. * * <p>This is just a minimal interface: The main intention is to allow a * {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer} * to introspect and modify property values and other bean metadata. * * @author Juergen Hoeller * @author Rob Harrop * @since 19.03.2004 * @see ConfigurableListableBeanFactory#getBeanDefinition * @see org.springframework.beans.factory.support.RootBeanDefinition * @see org.springframework.beans.factory.support.ChildBeanDefinition */ public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { /** * Scope identifier for the standard singleton scope: "singleton". * <p>Note that extended bean factories might support further scopes. * @see #setScope */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; /** * Scope identifier for the standard prototype scope: "prototype". * <p>Note that extended bean factories might support further scopes. * @see #setScope */ String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * Role hint indicating that a <code>BeanDefinition</code> is a major part * of the application. Typically corresponds to a user-defined bean. */ int ROLE_APPLICATION = 0; /** * Role hint indicating that a <code>BeanDefinition</code> is a supporting * part of some larger configuration, typically an outer * {@link org.springframework.beans.factory.parsing.ComponentDefinition}. * <code>SUPPORT</code> beans are considered important enough to be aware * of when looking more closely at a particular * {@link org.springframework.beans.factory.parsing.ComponentDefinition}, * but not when looking at the overall configuration of an application. */ int ROLE_SUPPORT = 1; /** * Role hint indicating that a <code>BeanDefinition</code> is providing an * entirely background role and has no relevance to the end-user. This hint is * used when registering beans that are completely part of the internal workings * of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}. */ int ROLE_INFRASTRUCTURE = 2; /** * Return the name of the parent definition of this bean definition, if any. */ String getParentName(); /** * Set the name of the parent definition of this bean definition, if any. */ void setParentName(String parentName); /** * Return the current bean class name of this bean definition. * <p>Note that this does not have to be the actual class name used at runtime, in * case of a child definition overriding/inheriting the class name from its parent. * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but * rather only use it for parsing purposes at the individual bean definition level. */ String getBeanClassName(); /** * Override the bean class name of this bean definition. * <p>The class name can be modified during bean factory post-processing, * typically replacing the original class name with a parsed variant of it. */ void setBeanClassName(String beanClassName); /** * Return the factory bean name, if any. */ String getFactoryBeanName(); /** * Specify the factory bean to use, if any. */ void setFactoryBeanName(String factoryBeanName); /** * Return a factory method, if any. */ String getFactoryMethodName(); /** * Specify a factory method, if any. This method will be invoked with * constructor arguments, or with no arguments if none are specified. * The method will be invoked on the specifed factory bean, if any, * or as static method on the local bean class else. * @param factoryMethodName static factory method name, * or <code>null</code> if normal constructor creation should be used * @see #getBeanClassName() */ void setFactoryMethodName(String factoryMethodName); /** * Return the name of the current target scope for this bean, * or <code>null</code> if not known yet. */ String getScope(); /** * Override the target scope of this bean, specifying a new scope name. * @see #SCOPE_SINGLETON * @see #SCOPE_PROTOTYPE */ void setScope(String scope); /** * Return whether this bean should be lazily initialized, i.e. not * eagerly instantiated on startup. Only applicable to a singleton bean. */ boolean isLazyInit(); /** * Set whether this bean should be lazily initialized. * <p>If <code>false</code>, the bean will get instantiated on startup by bean * factories that perform eager initialization of singletons. */ void setLazyInit(boolean lazyInit); /** * Return the bean names that this bean depends on. */ String[] getDependsOn(); /** * Set the names of the beans that this bean depends on being initialized. * The bean factory will guarantee that these beans get initialized first. */ void setDependsOn(String[] dependsOn); /** * Return whether this bean is a candidate for getting autowired into some other bean. */ boolean isAutowireCandidate(); /** * Set whether this bean is a candidate for getting autowired into some other bean. */ void setAutowireCandidate(boolean autowireCandidate); /** * Return whether this bean is a primary autowire candidate. * If this value is true for exactly one bean among multiple * matching candidates, it will serve as a tie-breaker. */ boolean isPrimary(); /** * Set whether this bean is a primary autowire candidate. * <p>If this value is true for exactly one bean among multiple * matching candidates, it will serve as a tie-breaker. */ void setPrimary(boolean primary); /** * Return the constructor argument values for this bean. * <p>The returned instance can be modified during bean factory post-processing. * @return the ConstructorArgumentValues object (never <code>null</code>) */ ConstructorArgumentValues getConstructorArgumentValues(); /** * Return the property values to be applied to a new instance of the bean. * <p>The returned instance can be modified during bean factory post-processing. * @return the MutablePropertyValues object (never <code>null</code>) */ MutablePropertyValues getPropertyValues(); /** * Return whether this a <b>Singleton</b>, with a single, shared instance * returned on all calls. * @see #SCOPE_SINGLETON */ boolean isSingleton(); /** * Return whether this a <b>Prototype</b>, with an independent instance * returned for each call. * @see #SCOPE_PROTOTYPE */ boolean isPrototype(); /** * Return whether this bean is "abstract", that is, not meant to be instantiated. */ boolean isAbstract(); /** * Get the role hint for this <code>BeanDefinition</code>. The role hint * provides tools with an indication of the importance of a particular * <code>BeanDefinition</code>. * @see #ROLE_APPLICATION * @see #ROLE_INFRASTRUCTURE * @see #ROLE_SUPPORT */ int getRole(); /** * Return a human-readable description of this bean definition. */ String getDescription(); /** * Return a description of the resource that this bean definition * came from (for the purpose of showing context in case of errors). */ String getResourceDescription(); /** * Return the originating BeanDefinition, or <code>null</code> if none. * Allows for retrieving the decorated bean definition, if any. * <p>Note that this method returns the immediate originator. Iterate through the * originator chain to find the original BeanDefinition as defined by the user. */ BeanDefinition getOriginatingBeanDefinition(); }
顾名思义,这个便是spring中的bean定义接口,所以其实我们工厂里持有的bean定义,就是一堆这个玩意,或者是他的实现类和子接口。这个接口并非直接的祖宗接口,他所继承的两个接口一个是core下面的AttributeAccessor,继承这个接口就以为这我们的bean定义接口同样具有处理属性的能力,而另外一个是beans下面的BeanMetadataElement,字面翻译这个接口就是bean的元数据元素,它可以获得bean的配置定义的一个元素。在XML文件中来说,就是会持有一个bean标签。
仔细观看,能发现beanDefinition中有两个方法分别是String[] getDependsOn()和void setDependsOn(String[] dependsOn),这两个方法就是获取依赖的beanName和设置依赖的beanName,这样就好办了,只要我们有一个BeanDefinition,就可以完全的产生一个完整的bean实例。
今天就先到这里吧,我也是边看边写的,与其说跟我一起学,其实这个系列的应该叫源码学习笔记,更多的还是看到哪里,写到哪里,谈不上跟我一起学。
如果文中有不周到的地方,希望各位尽管指出。