Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33)
扯淡:
各位:
      命运就算颠沛流离
     命运就算曲折离奇
     命运就算恐吓着你做人没趣味
     别流泪 心酸 更不应舍弃
...
主题:
Spring源码阅读还在继续,上篇文章初始化IoC容器:摸我
事实上前面的IOC容器初始化只是建立了BeanDefinition数据映射,而依赖注入的过程是在向Ioc容器索要bean的时候发生的。
getBean就是触发依赖注入的地方。
1,下面是AbstractBeanFactory的getBean实现。根据scop进行分支处理,比较清晰:
  注意:关于spring中的 bean的scop属性,有两个很常用:singleton和prototype
singleton:这是Spring容器默认的作用域,使用singleton作用域生成的是单实例,在整个Bean容器中仅保留一个实例对象供所有调用者共享引用。单例模式对于那些无会话状态的Bean(如辅助工具类、DAO组件、业务逻辑组件等)是最理想的选择。
prototype: 原型模式,这是多实例作用域,针对每次不同的请求,Bean容器均会生成一个全新的Bean实例以供调用者使用。
    public Object getBean(String name) throws BeansException {
        // 最终调用doGetBean方法
        return doGetBean(name, null, null, false);
    }
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return doGetBean(name, requiredType, null, false);
    }
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, null, args, false);
    }
    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
        return doGetBean(name, requiredType, args, false);
    }
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// 从已经建立的实例bean里找
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 取得本bean的依赖bean组
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 循环调用getBean方法,递归实现本bean的全部依赖
for (String dependsOnBean : dependsOn) {
getBean(dependsOnBean);
registerDependentBean(dependsOnBean, beanName);
}
}
// Create bean instance.
// Singleton 情况
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// Prototype 情况
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// 最终大家都调这个方法来实例化Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
// 其他scop情况
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
2,上面代码会调用到AbstractAutowireCapableBeanFactory 的 createBean方法,这个方法中调用了内部的doCreateBean方法,createBean方法:
protected Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException { if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
// Make sure bean class is actually resolved at this point.
resolveBeanClass(mbd, beanName); // Prepare method overrides.
try {
mbd.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
} try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
// 调用内部方法创建Bean
Object beanInstance = doCreateBean(beanName, mbd, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
doCreateBean方法:
里面也就是调用createBeanInstance创建bean的方法真正出来一个bean后,在调用populateBean方法(链接)来初始化这个bean
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            // 创建bean的方法
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
        Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
        // Allow post-processors to modify the merged bean definition.
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                mbd.postProcessed = true;
            }
        }
        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isDebugEnabled()) {
                logger.debug("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, new ObjectFactory() {
                public Object getObject() throws BeansException {
                    return getEarlyBeanReference(beanName, mbd, bean);
                }
            });
        }
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            // 初始化创建出来的bean
            populateBean(beanName, mbd, instanceWrapper);
            if (exposedObject != null) {
                exposedObject = initializeBean(beanName, exposedObject, mbd);
            }
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }
        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }
        // Register bean as disposable.
        try {
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
        }
        return exposedObject;
    }
如此,我们先看createBeanInstance这个创建bean的方法是如何完成的。
注意:在spring中有三种实例化bean的方式:
一、使用构造器实例化,默认
<bean id="personService" class="cn.mytest.service.impl.PersonServiceBean"></bean>
二、使用静态工厂方法实例化
<bean id="personService1" class="cn.mytest.service.impl.PersonServiceFactory" factory-method="createPersonServiceBean"></bean>
三、使用实例化工厂方法实例化
<bean id="personServiceFactory" class="cn.mytest.service.impl.PersonServiceFactory"></bean>
<bean id="personService2" factory-bean="personServiceFactory" factory-method="createPersonServiceBean1"></bean>
这在createBeanInstance中有体现:
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
        // Make sure bean class is actually resolved at this point.
        Class beanClass = resolveBeanClass(mbd, beanName);
        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }
        // 工厂方法实例化
        if (mbd.getFactoryMethodName() != null)  {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }
        // Shortcut when re-creating the same bean...
        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
        if (resolved) {
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
                return instantiateBean(beanName, mbd);
            }
        }
        // Need to determine the constructor...
        // 构造函数实例化
        Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
            return autowireConstructor(beanName, mbd, ctors, args);
        }
        // No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }
就直接看下简单常用的instantiateBean方法:
    protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return getInstantiationStrategy().instantiate(mbd, beanName, parent);
                    }
                }, getAccessControlContext());
            }
            else {
                // spring默认的实例化策略是使用CGLIB来实例化bean
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }
CGLIB:cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现。
使用CGLB实例化代码:
    public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (beanDefinition.getMethodOverrides().isEmpty()) {
            Constructor<?> constructorToUse;
            synchronized (beanDefinition.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class clazz = beanDefinition.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
                                public Constructor run() throws Exception {
                                    return clazz.getDeclaredConstructor((Class[]) null);
                                }
                            });
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor((Class[]) null);
                        }
                        beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Exception ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            // 使用CGLIB实现实例化bean
            return instantiateWithMethodInjection(beanDefinition, beanName, owner);
        }
    }
    protected Object instantiateWithMethodInjection(
            RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
        // Must generate CGLIB subclass.
        return new CglibSubclassCreator(beanDefinition, owner).instantiate(null, null);
    }
        public Object instantiate(Constructor ctor, Object[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.beanDefinition.getBeanClass());
            enhancer.setCallbackFilter(new CallbackFilterImpl());
            enhancer.setCallbacks(new Callback[] {
                    NoOp.INSTANCE,
                    new LookupOverrideMethodInterceptor(),
                    new ReplaceOverrideMethodInterceptor()
            });
            return (ctor == null) ?
                    enhancer.create() :
                    enhancer.create(ctor.getParameterTypes(), args);
        }
到这里,创建bean的过程是完成了,关于bean的一些初始化的操作及调用populateBean方法下次再研究吧。
事实上spring提供了jvm的反射实现的代理,和cglib实现的代理,来实例化java对象。
下面就稍微学习下这两种不同的代理方式:
代理模式也是常用的设计模式,分为两种:静态代理和动态代理
静态代理:
/**
* 定义一个账户接口
*
* @author Administrator
*/
public interface Count {
// 查看账户方法
public void queryCount();
// 修改账户方法
public void updateCount();
}
public class CountImpl implements Count {
    @Override
    public void queryCount() {
        System.out.println("查看账户方法...");
    }
    @Override
    public void updateCount() {
        System.out.println("修改账户方法...");
    }
}
/**
* 这是一个代理类(增强CountImpl实现类)
*
* @author Administrator
*
*/
public class CountProxy implements Count {
private CountImpl countImpl; /**
* 覆盖默认构造器
*
* @param countImpl
*/
public CountProxy(CountImpl countImpl) {
this.countImpl = countImpl;
}
@Override
public void queryCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.queryCount();
System.out.println("事务处理之后");
}
@Override
public void updateCount() {
System.out.println("事务处理之前");
// 调用委托类的方法;
countImpl.updateCount();
System.out.println("事务处理之后");
}
}
public class TestCount {
    public static void main(String[] args) {
        CountImpl countImpl = new CountImpl();
        CountProxy countProxy = new CountProxy(countImpl);
        countProxy.updateCount();
        countProxy.queryCount();
    }
}
动态代理:
public interface BookFacade {
    public void addBook();
}
public class BookFacadeImpl implements BookFacade {
    @Override
    public void addBook() {
        System.out.println("增加图书方法。。。");
    }
}
public class BookFacadeProxy implements InvocationHandler{
    private Object target;
    /**
     * 绑定委托对象并返回一个代理类
     *
     * @param target
     * @return
     */
    public Object bind(Object target) {
        this.target = target;
        // 取得代理对象
        // 要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }
    @Override
    /**
     * 调用方法
     * Object proxy:指被代理的对象
     * Method method:要调用的方法
     * Object[] args:方法调用时所需要的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        System.out.println("事物开始");
        // 执行方法
        result = method.invoke(target, args);
        System.out.println("事物结束");
        return result;
    }
}
public class TestProxy {
    public static void main(String[] args) {
        BookFacadeProxy proxy = new BookFacadeProxy();
        BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
        bookProxy.addBook();
    }
}
以上的代理方式需要有接口的类来可以,所以使用cglib弥补了这一缺点,这也是不同之处:
/**
* 这个是没有实现接口的实现类
*/
public class BookFacadeCglibImpl {
public void addBook() {
System.out.println("增加图书的普通方法...");
}
}
public class BookFacadeCglib implements MethodInterceptor{
    private Object target;  
    /**
     * 创建代理对象
     *
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }  
    @Override
    // 回调方法
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("事物开始");
        proxy.invokeSuper(obj, args);
        System.out.println("事物结束");
        return null;
    }
}
public class TestCglib {
    public static void main(String[] args) {
        BookFacadeCglib cglib=new BookFacadeCglib();
        BookFacadeCglibImpl bookCglib=(BookFacadeCglibImpl)cglib.getInstance(new BookFacadeCglibImpl());
        bookCglib.addBook();
    }
}
总结:
学习是没有止境的,当我们学得更多的时候,我们就会发现自己不知道的东西越来越多。
设计模式很难学,今天看一遍明天就忘了,实际编码能想起来更难~
让我们继续前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不会成功。
共勉。
Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)的更多相关文章
- cglib源码主流程源码-我们到底能走多远系列48
		扯淡 祝各位在园里的朋友新年快乐! 辛苦一年,为更好的自己也为更好的世界,很多人要感谢你们,你们也应该有很多人要感谢吧. 看了马斯克的采访视频,又想起兰迪·鲍许的最后一课,时光迁移,唯有梦想可坚持. ... 
- 初始化IoC容器(Spring源码阅读)-我们到底能走多远系列(31)
		我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? 毕竟,对自己的收入的分配差不多体现了自己的 ... 
- Spring mvc源码url路由-我们到底能走多远系列(38)
		我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ... 
- 初始化IoC容器(Spring源码阅读)
		初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ... 
- Spring源码阅读笔记02:IOC基本概念
		上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ... 
- Spring源码阅读 之 配置的读取,解析
		在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ... 
- Spring源码阅读-ApplicationContext体系结构分析
		目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ... 
- Sping学习笔记(一)----Spring源码阅读环境的搭建
		idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ... 
- 搭建 Spring 源码阅读环境
		前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ... 
随机推荐
- JS正则表达式基础
			正则表达式的作用: 测试字符串的某个模式 替换文本 根据模式匹配从字符串中提取一个子字符串.可以用来在文本或输入字段中查找特定文字 [^\d]/g这是一个正则表达式,在JS中 ... 
- 解决extjs grid 不随窗口大小自适应的问题
			解决extjs grid 不随窗口大小自适应的问题 August 30, 2010 zhai Javascript 8,403 viewsGo to comment 最近遇到的问题,在使用grid的时 ... 
- 常见JS(JavaScript)冲突解决方法
			1.一般JS冲突解决办法 a.最容易出现的就是js的命名冲突 ①.变量名冲突 变量有全局变量和局部变量当全局变量变量和局部变量名称一致时,就会js冲突,由于变量传递数值或地址不同就会产生JavaScr ... 
- cssTex
			var head= document.getElementById("head");head.style.cssText="width:200px;height:70px ... 
- 长理ACM 14-星期几(谌海军)
			题目描述:编一个程序,已知今天是星期几,计算出n天后是星期几.要求使用枚举变量. 输入描述:输入为两个正整数,第一个数n(n<=6)表示今天是星期几,第二个数m(m<=1000),表示求m ... 
- websocket++编译过程
			websocket++ 是一个开源 websocket 库,使用websocket++ 能够开发基于websocket 服务. 前一段时间成功编译 websocket++ ,分享一下,编译websoc ... 
- pl/sql乱码
			环境变量增加NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK 
- 常用控件产品官方文档/手册/API列表 c#控件文档API列表 asp.net控件产品技术文档中文版
			.netCHARTING报表图表控件 文档帮助手册Ab3d.PowerToys 文档帮助手册Ab3d.Reader3ds 文档帮助手册ABViewer 文档帮助手册 (工程图纸文档管理系统)Activ ... 
- android开机启动过程
			Android系统开机主要经历三个阶段: bootloader启动 Linux启动 Android启动 启动文件: 对于机器从通电到加载Linux系统一般需要三个文件:bootloader(引导文件) ... 
- mybatis 参数问题
			load方法,往sql语句中传一个参数的那种,似乎parameterType并不是那么重要,不写其实都可以,而且sql语句中的比如username=#{A} 这里面的A可以随便写,只要传过来的是个St ... 
