MyException - 我的异常网
当前位置:我的异常网» 软件架构设计 » 对Spring IoC器皿实现的结构分析

对Spring IoC器皿实现的结构分析

www.MyException.Cn  网友分享于:2013-07-24  浏览:4次
对Spring IoC容器实现的结构分析
原文:http://www.iteye.com/magazines/71
本文的目标:
从实现的角度来认识SpringIoC容器。

观察的角度:
从外部接口,内部实现,组成部分,执行过程四个方面来认识SpringIoC容器。

本文的风格:
首先列出SpringIoC的外部接口及内部实现所涉及到的组件列表;
其次介绍组件之间的相互关系以对整个执行过程有所把握;
然后针对每一个组件的简单介绍,包括组件的类结构图,核心功能描述,重要接口的重要方法描述;
接下来看SpringIoC容器实现对面向对象基本设计原则的遵守;
最后是后记部分。

术语约定:
组件:本文中的组件是指在功能概念上具有相对独立性的功能单元,物理结构上的特征一般由一组接口、一组抽象类、一组具体实现类、异常类、工具类所组成;
            这里的组件是一种相当狭义的描述,根据上下文的不同,组件可以有不同的表现形式,如:相对于Spring框架,SpringIoC容器就是Spring框架的一个组件,
    相对于系统的整体框架设计,Spring框架就是整体框架的一个组件,这里组件与模块的概念基本等同。
接口方法:一般定义在接口中,提供给外部调用的方法,接口方法最重要的在于接口提供者以清晰、简洁的定义提供了接口使用者所必需的功能特征;
基础方法:首先基础方法也是一个接口方法,但和接口方法的差别在于接口方法的直接实现依赖于基础方法(参见BeanDefintionReader接口中的方法定义);
钩子方法:超类留给子类需要实现或重写的方法,
默认实现钩子方法:超类提供默认实现,子类可以选择是否有必要重写,
默认空实现钩子方法:超类提供一个空实现方法,子类可以选择是否有必要重写,
抽象钩子方法:超类留给子类必须实现的方法,
内部实现方法:对某一相对独立的处理逻辑的封装,以便增强代码的可读性、可修改性、可重用性,达到代码的清晰性、简洁性。
注:
本文的源代码基于Spring2.x。Spring的源代码也处于演变中,但对基础代码的影响并不大。

正文:

Spring IoC容器的外部接口:
ApplicationContext
BeanFactory
WebApplicationContext


BeanFactory是IoC容器的核心组件,其它组件都是在为BeanFactory提供服务.
ConfigurableBeanFactory
AutowireCapableBeanFactory
ListableBeanFactory
HierarchicalBeanFactory
AbstractBeanFactory
AbstractAutowireCapableBeanFactory
DefaultListableBeanFactory
SingletonBeanRegistry接口,
BeanDefintionRegistry接口,

Resource组件,

ResourceLoader组件,

BeanDefintion组件,

BeanDefintionReader组件,

XmlBeanDefinitionParser组件,

BeanDefintionParser组件,

NamespaceHandler组件,

NamespaceHandlerResolver组件,

BeanWrapper组件,
------------------------------------------------
ApplicationContext
ConfigurableApplicationContext
AbstractApplicationContext
AbstractRefreshApplicationContext
AbstractXmlApplicationContext
ClassPathXmlApplicationContext
FileSystemXmlApplicationContext

Lifecycle接口

ApplicationEventPublisher接口
ApplicationEventMulticaster组件

MessageSource组件

MessageSourceResolvable组件

-----------------------------------------------
WebApplicationContext
ConfigurableWebApplicationContext
AbstractRefreshWebApplicationContext
XmlWebApplicationContext

ContextLoader组件
ContextLoaderListener
ContextLoaderServlet
------------------------------------------------------
FactoryBean

一组回调接口,
InitializingBean
DisposableBean
BeanPostProcessor
BeanFactoryPostProcessor

BeanNameAware
BeanFactoryAware

ResourceLoaderAware
ApplicationContextPublisherAware
MessageSourceAware
ApplicationContextAware
ApplicationContextAwareProcessor

ServletContextAware
ServletConfigAware
ServletContextAwareProcessor
------------------------------------------------------------

对这三个接口(ApplicationContext、BeanFactory、WebApplicationContext)的讨论:
ApplicationContext接口是IoC容器概念的直接对应物,包括容器自身生命周期的管理(容器的启动,容器的初始化,容器的销毁)
一些便利功能的提供如:资源文件的读取,容器级事件的发布。
BeanFactory接口是IoC容器的核心,其它组件都为此组件提供支持,如Resource组件,ResourceLoader组件,BeanDefintionReader组件,
BeanDefintion组件,BeanWrapper组件等。BeanFactory接口相对于容器的概念太过低级,以至于直接使用需要应对较复杂的API。
WebApplicationContext接口提供IoC容器对Web环境的支持,与ServletAPI的集成工作。普通Java应用程序选择IoC容器使用ApplicationContext,
Web环境下的IoC容器使用WebApplicationContext。


下面来关注这两行代码的执行都发生了那些事情,以了解容器的整个执行过程。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Foo foo = (Foo)applicationContext.getBean("foo");

1.首先实例化一个容器对象,
2.然后由ResourceLoader组件对参数"applicationContext.xml"进行解析,将此路径上指定的文件解析为Resource对象。
3.BeanDefinitionReader将Resource资源对象内的bean元素数据封装到BeanDefintion组件中,并通过BeanDefintionRegistry将BeanDefintion注册到
   BeanFactory中,
4.对Resource的解析工作主要包括三个主要部分,
    a:对xml文档的schema验证,
    b:对默认命名空间元素的解析,这部分委托给XmlBeanDefintionParser组件完成,
    c:对客户化命名空间元素的解析,这部分工作委托给BeanDefintionParser完成,NamespaceHadler组件和NamespaceHandlerResolver组件对BeanDefintionParser提供支持工作。
   这里需要提到的是一些特殊元素的解析如:import元素的解析;另外一点是对applicationContext.xml文件中的bean元素的实际解析工作是委托给
   XmlBeanDefintionParserHelper类完成的;上面提到的组件接口列表中与BeanDefinition相关的组件有BeanDefinition组件,BeanDefintionRegistry接口,
   除了这两个最重要的,还有如:BeanDefintionBuilder,BeanDefintionDecorator,BeanDefinitionValueResolver,BeanDefinitionRegistryBuilder等其它与BeanDefintion相关组件,
   都对BeanDefintion的操作提供支持。
   至此,已经完成了阶段性工作,就是已经将类型信息从applicationContext.xml配置文件bean元素中读取到内存对象的BeanDefinition组件中,接下来的工作就是如何将
   BeanDefintion组件中所保存的类型信息实例化为最终的对象。
5.接下来是容器的初始化工作:
   调用BeanFactoryPostProcessor接口,
   注册BeanPostProcessor接口,
   初始化MessageSource组件,
   初始化ApplicationEventMulticaster,
   注册容器级监听器,
   发布容器已刷新的事件,
   ApplicationContext接口对bean对象的初始化采取一种积极初始化策略,这样做容器初始化过程虽然比较慢,但后续的每一次bean访问相对较快,因为可以从singletonCache缓存中直接获取,
6.
   至此下面这行代码的执行过程已结束,
   ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
   接下来看这行代码的执行过程,
   Foo foo = (Foo)applicationContext.getBean("foo");
7.
getBean(String)的目标很明确,就是根据bean的名称得到一个bean对象,
对bean对象的不同角度的分类,
首先可以分为普通的非FactoryBean类型的bean对象和FactoryBean类型的bean对象,
其次可以分为singleton类型的bean对象和非singleton类型的bean对象,
但是这些分类是建立在一个已创建的bean对象基础之上。
8.下面来看创建一个bean对象的过程,
    createBean();
    首先容器对BeanDefintion进行整理,根据依赖、继承关系进行合并以得到最终的BeanDefintion,
    然后进行bean对象的实例化、初始化、对需要销毁操作的bean对象进行注册。
    在这一过程中涉及到一组回调接口的调用,包括实例化前后的处理逻辑,初始化前后的处理逻辑,初始化过程的回调逻辑,销毁操作执行逻辑,
    主要的回调接口有
    InitializingBean
    DisposableBean
    BeanPostProcessor
    XXXXXXAware
    配置风格的回调机制(init-method,destroy-method)
    对bean对象的初始化工作依赖于BeanWrapper组件,BeanWrapper组件以反射的方式将BeanDefintion组件中保存的属性信息设置到bean对象中。

组件描述:


Resource组件与ResourceLoader组件一起工作,将字符串格式指示的资源解析为Resource对象。
事实上ResourceLoader是Resource的工厂类,
Java代码
public interface ResourceLoader { 
    public Resource getResource(String location); 


ResourceLoader的核心工作就是解析location,
location示例:"classpath:applicationContext.xml","classpath*:applicationContext-*.xml","file:/some/resource/path/myTemplate.txt","http://myhost.com/resource/path/myTemplate.txt"
ResourceLoader根据所指示的前缀返回特定的Resource对象。


BeanDefintionReader组件,
Java代码
//将Resource中的内容通过BeanDefintionRegistry注册到BeanFactory中。 
public interface BeanDefintionReader { 
    BeanDefinitionRegistry getBeanFactory(); 
    ResourceLoader getResourceLoader(); 
    int loadBeanDefinitions(Resource[] resources) throws BeanDefinitionStoreException; 
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; 
    int loadBeanDefinitions(String[] locations) throws BeanDefinitionStoreException; 
     /*
     *这是一个基础方法,从Resource中加载BeanDefinition;
     *这三个方法loadBeanDefintions(Resources[]),loadBeanDefintions(String[]),loadBeanDefintion(String)的实现
     *依赖于此方法的实现;
     *上面三个方法的实现在AbstractBeanDefinitionReader骨架类中完成,此方法的实现在XmlBeanDefintionReader中完成。
     */ 
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; 




Java代码
//XmlBeanDefinitionParser组件处理配置文件中默认命名空间的元素解析, 
public interface XmlBeanDefinitionParser { 
    //对Document文档的解析,将解析出的内容封装到BeanDefintion中。 
    void registerBeanDefinitions(Document doc, ReaderContext readerContext)throws BeanDefinitionStoreException; 


这三个组件(BeanDefintionParser组件,NamespaceHandler组件,NamespaceHandlerResolver组件)处理客户化的命名空间元素的解析,
此机制使用配置文件易于书写,具有可扩展性。
如spring提供的实现:<util:list>,<aop:config>,<tx:annotation-driven>,<context:annotation-config>,
第三方组件提供的实现:<jaxws:endpoint>,<amq:broker>等其它实现。
Java代码
//对客户化命名空间的bean元素进行解析操作。 
public interface BeanDefintionParser { 
    //对Element的解析。 
    BeanDefinition parse(Element element, ParserContext parserContext); 

//根据元素命名空间得到此元素的BeanDefinitionParser处理程序;此类是BeanDefintionParser的工厂类。 
public interface NamespaceHandler { 
    void init(); 
    BeanDefinitionParser findParserForElement(Element element); 
    BeanDefinitionDecorator findDecoratorForElement(Element element); 

//解析META-INF/spring.handlers中的配置信息;此类是NamespaceHandler 的工厂, 
public interface NamespaceHandlerResolver { 
    //根据命名空间指示符得到指定的命名空间处理器。 
    NamespaceHandler resolve(String namespaceUri); 



BeanWrapper组件,
对java bean对象提供设置属性值、获取属性值操作,并能够将字符串类型值转换为正确的类型,这个工作依赖于PropertyEditor。
操作示例:
beanWrapper.setPropertyValue("name","foo");
beanWrapper.setPropertyValue("address.country","China");
beanWrapper.setPropertyValue("array[2]","arrayValue");
Java代码
//PropertyEditor注册器;提供注册、获取PropertyEditor操用。 
public interface PropertyEditorRegistry { 
    void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor); 
    void registerCustomEditor(Class requiredType, String propertyPath, PropertyEditor propertyEditor); 
    PropertyEditor findCustomEditor(Class requiredType, String propertyPath); 

//bean对象的属性访问器。 
public interface PropertyAccessor { 
    public boolean isReadableProperty(String name); 
    public boolean isWritableProperty(String name); 
    public Class<?> getPropertyType(String name); 
    public Object getPropertyValue(String name); 
    public void setPropertyValues(PropertyValues pvs); 
    public void setPropertyValues(Map<String,Object> pvs); 
    public void setPropertyValue(PropertyValue pv); 
    public void setPropertyValue(String name,Object value); 

//对PropertyEditor增加管理功能。 
public interface ConfigurablePropertyAccessor extends PropertyEditorRegistry,PropertyAccessor { 
    void setExtractOldValueForEditor(boolean extractOldValueForEditor); 
    boolean isExtractOldValueForEditor(); 

//对bean对象进行管理。 
public interface BeanWrapper extends ConfigurablePropertyAccessor { 
     //设置所在包装的object 
    void setWrappedInstance(Object obj); 
     //返回包装对象. 
    Object getWrappedInstance(); 
     //返回包装对象类型 
    Class getWrappedClass(); 
     //返回包装对象属性描述.. 
    PropertyDescriptor[] getPropertyDescriptors() throws BeansException; 
     //根据属性名返回特定的属性描述对象. 
    PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException; 



Java代码
//IoC容器的核心接口,提供访问IoC容器的基本操作。 
public interface BeanFactory { 
    //根据bean名称获取相应的bean对象. 此方法在AbstractBeanFactory骨架类中实现, 
    public Object getBean(String name) throws BeansException; 

//定义分层的BeanFactory容器结构。 
public interface HierarchicalBeanFactory  extends BeanFactory { 
 

//对BeanFactory提供配置信息. 
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory { 
    void setParentBeanFactory(BeanFactory parentBeanFactory); 
    // 注册客户化属性编辑器. 
    void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor); 
     //添加BeanPostProcessor. 
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); 
    //销毁所有singleton类型bean对象. 
    void destroySingletons(); 

//主要逻辑有创建一个bean对象实例的过程,根据不同的WireMode(byName、byType)完成不同的操作。 
public interface AutowireCapableBeanFactory extends BeanFactory { 
    //创建一个bean对象. 
    Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; 
    //配置一个bean对象. 
    Object configureBean(Object existingBean, String beanName) throws BeansException; 
     //初始化bean对象. 
    Object initializeBean(Object existingBean, String beanName) throws BeansException; 
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException; 
    Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException; 

/*
  *提供对BeanDefintion对象的管理操作。
  *对通过BeanDefintionRegistry注册器注册到BeanFactory中的BeanDefintion对象进行管理,但注意此接口
  *并不依赖于BeanDefintion接口API;
  *此接口在操作上与BeanDefintionRegistry接口有重叠部分,但此接口的职责重在管理操作,而BeanDefintionRegistry重在注册操作,并且
  *BeanDefintionRegistry接口直接依赖于BeanDefintion接口API。
  */ 
public interface ListableBeanFactory { 
    boolean containsBeanDefinition(String beanName); 
    int getBeanDefinitionCount(); 
    String[] getBeanDefinitionNames(); 
    String[] getBeanNamesForType(Class type); 
    String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean includeFactoryBeans); 
    Map getBeansOfType(Class type) throws BeansException; 
    Map getBeansOfType(Class type, boolean includePrototypes, boolean includeFactoryBeans) throws BeansException; 

// 
public interface ConfigurableListableBeanFactory 
        extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory,SingletonBeanRegistry { 
    void ignoreDependencyType(Class type); 
    void ignoreDependencyInterface(Class ifc); 
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;  
    //在容器启动过程中实例化singleton类型bean对象。 
    void preInstantiateSingletons() throws BeansException; 

//注册BeanDefintion对象,并进行管理操作。 
public interface BeanDefinitionRegistry { 
    int getBeanDefinitionCount(); 
    String[] getBeanDefinitionNames(); 
    boolean containsBeanDefinition(String beanName); 
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; 
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeansException; 
    String[] getAliases(String beanName) throws NoSuchBeanDefinitionException; 
    void registerAlias(String beanName, String alias) throws BeansException; 

/*
*注册singleton类型对象,并进行管理操作;BeanDefinitionRegistry接口将BeanDefinition注册到BeanFactory中,此接口将singleton类型
*对象注册到BeanFactory中。
*/ 
public interface SingletonBeanRegistry { 
 



ApplicationEventPublisher接口是容器事件发布接口,
ApplicationEventPublisher接口的功能是委托给ApplicationEventMulticaster组件实现的,
ApplicationEventMulticaster组件提供对监听器的完整操作,包括新增监听器、移除单个或全部监听器、通知监听器。
Java代码
//容器事件发布器。 
public interface ApplicationEventPublisher { 
    void publishEvent(ApplicationEvent event); 

//一个完整的事件模型实现。 
public interface ApplicationEventMulticaster { 
    void addApplicationListener(ApplicationListener listener); 
    void removeApplicationListener(ApplicationListener listener); 
    void removeAllListeners(); 
    /*
     *事件发布方法,通知所有监听器;
     *ApplicationEventPublisher.publishEvent(ApplicationEvent)方法的实现委托给此方法完成。
     */ 
    void multicastEvent(ApplicationEvent event); 

//监听器. 
public interface ApplicationListener extends java.util.EventListener { 

//事件对象. 
public class extends ApplicationEvent extends java.util.EventObject { 



MessageSource组件
MessageSourceResolvable组件
这是一个接口复用与组合复用协同工作的好例子,ApplicationContext接口继承了MessageSource接口,对外提供信息源处理操作,但内部实现委托给MessageSource组件完成。


Java代码
//SpringIoC容器的顶级接口. 
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, 
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver { 
    ApplicationContext getParent(); 
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException; 
    String getDisplayName(); 
    long getStartupDate(); 

//对容器对象进行配置化、初始化工作. 
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle { 
    void setParent(ApplicationContext parent); 
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor); 
    //此方法是核心方法,内部生成一个BeanFactory对象,并完成对BeanFactory对象的初始化和容器的初始化工作。 
    void refresh() throws BeansException, IllegalStateException; 
    ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; 
    void close(); 

//生命周期管理接口. 
public interface Lifecycle { 
    void start(); 
    void stop(); 
    boolean isRunning(); 



WebApplicationContext
ConfigurableWebApplicationContext
AbstractRefreshWebApplicationContext
XmlWebApplicationContext
Java代码
//提供SpringIoC容器对Web环境ServletAPI的集成。 
public interface WebApplicationContext extends ApplicationContext { 
    ServletContext getServletContext(); 

//提供WebApplicationContext的配置工作。 
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext { 
    void setServletContext(ServletContext servletContext); 
    void setServletConfig(ServletConfig servletConfig); 
    void setNamespace(String namespace); 
    void setConfigLocations(String[] configLocations); 



Java代码
//ContextLoader组件从ServletContext初始化配置参数中获取Spring的配置文件路径信息,并进行IoC容器的实例化、初始化、销毁操作。 
public class ContextLoader { 
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext){ 
        //code. 
    } 
    public void closeWebApplicationContext(ServletContext servletContext) { 
        //code. 
    } 


ContextLoaderListener和ContextLoaderServlet提供两种方式将IoC容器集成到ServletContext缓存中。

从面向对象基本设计原则角度来看SpringIoC容器的设计:
最基本的两条设计原则--编程到接口、首选组合复用:
编程到接口,
    在SpringIoC容器实现中,interface 关键字随处可见,但是有一点需要注意的就是:并不是使用了interface关键字,就能保证编程到接口,
    但一般来说对编程到接口原则的遵守,inteface关键字的使用是必须的。编程到接口所描述的实质是要将组件的外部接口和内部实现分离
    开来,这将带来一系列的好处:可扩展性,可重用性,可维护性,依赖性,内聚性,耦合性,清晰性,简洁性,可读性,可修改性,
    抽象性,封装性,模块化,层次化,测试性,其它特性。
首选组合复用,
    面向对象的复用方式主要分两种--组合复用、继承复用,继承复用可以细分为两种--接口复用、具体复用,
    这条原则关注的组合复用与具体复用之间的区别,
    事实上这条原则针对复用方式的选择上意义并不大,因为这三种复用方式所处理的是不同的复用问题,一旦能够从has-a、is-like-a、is-a的角度
    区分开,问题就不大了。
    这条原则真正有意义的在于它的教训意义,可以借此了解这条原则形成的原因,全面了解复用的方式,了解每一种复用方式的特点,了解不同
    复用方式之间的差别。
    SpringIoC容器的设计很好的体现了这两条原则,如ApplicationContext接口
Java代码
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, 
ssageSource, ApplicationEventPublisher, ResourcePatternResolver { 


    这是一个基于接口的设计,并且是一个接口复用,
    ApplicationContext接口继承了ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver
    接口,那么就意味着 ApplicationContext可以提供这些接口中定义的所有功能,但是这些功能的实际实现并不是由ApplicationContext的实现类
    提供的,而是以组合复用的方式委托给了各个接口的实际实现类来完成;
    具体复用在接口的实现过程中所使用,以便将接口的设计层次与接口的实现层次分离开来,如:
Java代码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { 


更为全面的设计原则描述:
单一职责、开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、组合复用原则、迪米特法则;
共同重用原则、共同封闭原则,无环依赖原则、稳定依赖原则、稳定抽象原则,


总结:
要全面理解IOC容器,回答下述问题是必须的。
1。IOC容器是什么,
2。IOC容器提供什么样的功能,
3。IOC容器的特征是什么,
4。IOC容器设计的理论依据是什么,
5。IOC容器的设计需要注意的问题是什么,
6。如何实现一个IOC容器,
7。不同容器、不同IOC容器之间的比较, [/size]

文章评论

老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
10个调试和排错的小建议
10个调试和排错的小建议
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
Java程序员必看电影
Java程序员必看电影
为什么程序员都是夜猫子
为什么程序员都是夜猫子
每天工作4小时的程序员
每天工作4小时的程序员
总结2014中国互联网十大段子
总结2014中国互联网十大段子
一个程序员的时间管理
一个程序员的时间管理
我是如何打败拖延症的
我是如何打败拖延症的
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
代码女神横空出世
代码女神横空出世
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
漫画:程序员的工作
漫画:程序员的工作
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
旅行,写作,编程
旅行,写作,编程
那些争议最大的编程观点
那些争议最大的编程观点
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
编程语言是女人
编程语言是女人
程序员都该阅读的书
程序员都该阅读的书
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
 程序员的样子
程序员的样子
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
老程序员的下场
老程序员的下场
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
程序员应该关注的一些事儿
程序员应该关注的一些事儿
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员必看的十大电影
程序员必看的十大电影
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员和编码员之间的区别
程序员和编码员之间的区别
程序员的鄙视链
程序员的鄙视链
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
如何成为一名黑客
如何成为一名黑客
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有