Autofac基本使用

  Autofac是一款轻量级的IOC框架,使用率上还是挺高的,官方网站http://autofac.org,源码下载地址https://github.com/autofac/Autofac。

  下面以狗的列子来介绍autofac,nuget搜索Autofac进行安装

public interface IDog

{

/// <summary>

/// 品种

/// </summary>

string Breed { get; }

/// <summary>

/// 名称

/// </summary>

string Name { get; }

}

/// <summary>

/// 萨摩耶

/// </summary>

public class Samoyed : IDog

{

/// <summary>

/// 品种

/// </summary>

public string Breed

{

get

{

return "Samoyed(萨摩耶)";

}

}

/// <summary>

/// 名称

/// </summary>

public string Name

{

get

{

return "小黄";

}

}

}

/// <summary>

/// 藏獒

/// </summary>

public class TibetanMastiff : IDog

{

/// <summary>

/// 品种

/// </summary>

public string Breed

{

get

{

return "Mastiff Class(獒犬类)";

}

}

/// <summary>

/// 名称

/// </summary>

public string Name

{

get

{

return "小黑";

}

}

}

1.RegisterType 

public static void Register()

{

var builder = new ContainerBuilder();

//注册Samoyed指定为IDog实现

builder.RegisterType<Samoyed>().As<IDog>();

builder.RegisterType<TibetanMastiff>().As<IDog>();

using (var container = builder.Build())

{

var dogs = container.Resolve<IEnumerable<IDog>>();

foreach (var dog in dogs)

{

Console.WriteLine($"名称:{dog.Name},品种:{dog.Breed}");

}

}

}


2.RegisterAssemblyTypes

public static void RegisterAssemblyTypes()

{

var builder = new ContainerBuilder();

//注册程序集下所有类型

builder.RegisterAssemblyTypes(typeof(Program).Assembly).AsImplementedInterfaces();

using (var container = builder.Build())

{

var dogs = container.Resolve<IEnumerable<IDog>>();

foreach (var dog in dogs)

{

Console.WriteLine($"名称:{dog.Name},品种:{dog.Breed}");

}

}

}

直接注册程序集下的所有类型,AsImplementedInterfaces(让具体实现类型,可以该类型继承的所有接口类型找到该实现类型)

  3.RegisterInstance

TibetanMastiff d = new TibetanMastiff();
builder.RegisterInstance(d).As<IDog>();

  4.RegisterModule

  这种模式需要使用配置文件进行注册,个人更喜欢代码直接注册的方式,毕竟配置文件修改容易遗忘和出错。这里就不介绍该方式了。

  遗留问题:上面的注册代码,自己写写demo的时候没啥问题。但是运用到项目里面就很繁琐了,需要自己一个个类型注册,后面会提供解决方案。

.net core MVC与Autofac

  1.首先nuget下载Autofac和Autofac.Extensions.DependencyInjection引用

  2.替换mvc自带的DI框架

  将Startup.cs中的ConfigureServices返回类型改为IServiceProvider

public IServiceProvider ConfigureServices(IServiceCollection services)

{

services.AddMvc();

var builder = new ContainerBuilder();

builder.Populate(services);

builder.RegisterAssemblyTypes(typeof(Startup).Assembly).AsImplementedInterfaces();

var Container = builder.Build();

return new AutofacServiceProvider(Container);

}

属性注入

Autofac默认是构造函数注入

[Route("api/[controller]")]

public class ValuesController : Controller

{

private readonly IEnumerable<IDog> dogs;

public ValuesController(IEnumerable<IDog> _dogs)

{

dogs = _dogs;

}

// GET api/values

[HttpGet]

public IEnumerable<string> Get()

{

List<string> list = new List<string>();

foreach (var dog in dogs)

{

list.Add($"名称:{dog.Name},品种:{dog.Breed}");

}

return list.ToArray(); ;

}

}


使用过mef的可能更喜欢属性注入的方式,那么使用autofac怎么实现属性注入呢?

1.注册系统所有Controller,由Autofac创建

var IControllerType = typeof(ControllerBase);
builder.RegisterAssemblyTypes(assembly).Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired();

上面这段代码的解释:注册所有程序集下继承ControllerBase的类型,PropertiesAutowired 允许属性注入。

2.替换系统默认Controller创建器

services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());services.AddMvc();

注意:Replace代码放在AddMvc之前

Replace代码的意思:使用ServiceBasedControllerActivator替换DefaultControllerActivator(意味着框架现在会尝试从IServiceProvider中解析控制器实例,也就是return new AutofacServiceProvider(Container);

3.使用属性注入

[Route("api/[controller]")]

public class ValuesController : Controller

{

public IEnumerable<IDog> dogs { get; set; }

[HttpGet]

public IEnumerable<string> Get()

{

List<string> list = new List<string>();

foreach (var dog in dogs)

{

list.Add($"名称:{dog.Name},品种:{dog.Breed}");

}

return list.ToArray(); ;

}

}

至此完成了使用Autofac实现属性注入

Autofac+Castle实现AOP

1.首先nuget下载Autofac.Extras.DynamicProxy引用

2.编写拦截器

public class LogInterceptor : IInterceptor

{

public void Intercept(IInvocation invocation)

{

Console.WriteLine("你正在调用方法 \"{0}\"  参数是 {1}... ",

invocation.Method.Name,

string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));

invocation.Proceed();

if (invocation.ReturnValue != null && invocation.ReturnValue is string)

{

//在返回接口上拼上LogInterceptor

invocation.ReturnValue += " LogInterceptor";

}

Console.WriteLine("方法执行完毕,返回结果:{0}", invocation.ReturnValue);

Console.WriteLine("开始记录日志....");

}

}

3.开启拦截(接口拦截器  类拦截器) 

builder.RegisterType<LogInterceptor>();

builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces()

.EnableInterfaceInterceptors();

var IControllerType = typeof(ControllerBase);

builder.RegisterAssemblyTypes(assembly).Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).PropertiesAutowired()

.EnableClassInterceptors();

var Container = builder.Build();

开启接口拦截器:EnableInterfaceInterceptors  开启类拦截器:EnableClassInterceptors
[Intercept(typeof(LogInterceptor))]
[Route("api/[controller]")]public class ValuesController : Controller
{
}
[Intercept(typeof(LogInterceptor))]public class Samoyed : IDog
{
}

这种使用方式需要自己指定在哪个类上使用,还有一种全局拦截器

builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().EnableInterfaceInterceptors()    .InterceptedBy(typeof(LogInterceptor));

代码封装简单使用

先列出使用过程中遇到的几个问题,然后再给出解决方案

1:如何简单注册代码里面的所有类型

2.如何注册单例和普通对象

3.封装好的代码怎么支持用户特殊化注册需求

为了解决上述问题,这里给出了几个约束

单例对象需继承的接口:ISingletonDependency  普通对象需继承的接口:ITransientDependency 特殊化注册接口:IDependencyRegistrar

通过这几个约束,在初始化时找所有程序集 继承ISingletonDependency ,ITransientDependency 接口的对象进行类型注册

/// <summary>/// 单例接口    /// </summary>public interface ISingletonDependency{}

/// <summary>

/// 所有接口的依赖接口,每次创建新实例

/// </summary>

/// <remarks>

/// 用于Autofac自动注册时,查找所有依赖该接口的实现。

/// 实现自动注册功能

/// </remarks>

public interface ITransientDependency

{

}

/// <summary>

/// 依赖注册接口

/// </summary>

public interface IDependencyRegistrar

{

/// <summary>

/// Register services and interfaces

/// </summary>

/// <param name="builder">Container builder</param>

/// <param name="config">Config</param>

void Register(ContainerBuilder builder,List<Type> listType);

/// <summary>

/// Order of this dependency registrar implementation

/// </summary>

int Order { get; }

}

public interface IIocManager

{

IContainer Container { get; }

bool IsRegistered(Type serviceType, ILifetimeScope scope = null);

object Resolve(Type type, ILifetimeScope scope = null);

T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class;

T Resolve<T>(params Parameter[] parameters) where T : class;

T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null);

object ResolveOptional(Type serviceType, ILifetimeScope scope = null);

object ResolveUnregistered(Type type, ILifetimeScope scope = null);

T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class;

ILifetimeScope Scope();

bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance);

}

/// <summary>

/// Container manager

/// </summary>

public class IocManager : IIocManager

{

private IContainer _container;

public static IocManager Instance { get { return SingletonInstance; } }

private static readonly IocManager SingletonInstance = new IocManager();

/// <summary>

/// Ioc容器初始化

/// </summary>

/// <param name="config"></param>

/// <returns></returns>

public IServiceProvider Initialize(IServiceCollection services)

{

var builder = new ContainerBuilder();

builder.RegisterInstance(Instance).As<IIocManager>().SingleInstance();

//所有程序集 和程序集下类型

var deps = DependencyContext.Default;

var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包

var listAllType = new List<Type>();

foreach (var lib in libs)

{

try

{

var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));

listAllType.AddRange(assembly.GetTypes().Where(type => type != null));

}

catch { }

}

//找到所有外部IDependencyRegistrar实现,调用注册

var registrarType = typeof(IDependencyRegistrar);

var arrRegistrarType = listAllType.Where(t => registrarType.IsAssignableFrom(t) && t != registrarType).ToArray();

var listRegistrarInstances = new List<IDependencyRegistrar>();

foreach (var drType in arrRegistrarType)

{

listRegistrarInstances.Add((IDependencyRegistrar)Activator.CreateInstance(drType));

}

//排序

listRegistrarInstances = listRegistrarInstances.OrderBy(t => t.Order).ToList();

foreach (var dependencyRegistrar in listRegistrarInstances)

{

dependencyRegistrar.Register(builder, listAllType);

}

//注册ITransientDependency实现类

var dependencyType = typeof(ITransientDependency);

var arrDependencyType = listAllType.Where(t => dependencyType.IsAssignableFrom(t) && t != dependencyType).ToArray();

builder.RegisterTypes(arrDependencyType)

.AsImplementedInterfaces()

.InstancePerLifetimeScope()

.PropertiesAutowired().EnableInterfaceInterceptors();

foreach (Type type in arrDependencyType)

{

if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))

{

builder.RegisterType(type).As(type.BaseType)

.InstancePerLifetimeScope()

.PropertiesAutowired();

}

}

//注册ISingletonDependency实现类

var singletonDependencyType = typeof(ISingletonDependency);

var arrSingletonDependencyType = listAllType.Where(t => singletonDependencyType.IsAssignableFrom(t) && t != singletonDependencyType).ToArray();

builder.RegisterTypes(arrSingletonDependencyType)

.AsImplementedInterfaces()

.SingleInstance()

.PropertiesAutowired();

foreach (Type type in arrSingletonDependencyType)

{

if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))

{

builder.RegisterType(type).As(type.BaseType)

.SingleInstance()

.PropertiesAutowired();

}

}

builder.Populate(services);

_container = builder.Build();

return new AutofacServiceProvider(_container);

}

/// <summary>

/// Gets a container

/// </summary>

public virtual IContainer Container

{

get

{

return _container;

}

}

/// <summary>

/// Resolve

/// </summary>

/// <typeparam name="T">Type</typeparam>

/// <param name="key">key</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

if (string.IsNullOrEmpty(key))

{

return scope.Resolve<T>();

}

return scope.ResolveKeyed<T>(key);

}

/// <summary>

/// Resolve

/// </summary>

/// <typeparam name="T">Type</typeparam>

/// <param name="key">key</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual T Resolve<T>(params Parameter[] parameters) where T : class

{

var scope = Scope();

return scope.Resolve<T>(parameters);

}

/// <summary>

/// Resolve

/// </summary>

/// <param name="type">Type</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual object Resolve(Type type, ILifetimeScope scope = null)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

return scope.Resolve(type);

}

/// <summary>

/// Resolve all

/// </summary>

/// <typeparam name="T">Type</typeparam>

/// <param name="key">key</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved services</returns>

public virtual T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

if (string.IsNullOrEmpty(key))

{

return scope.Resolve<IEnumerable<T>>().ToArray();

}

return scope.ResolveKeyed<IEnumerable<T>>(key).ToArray();

}

/// <summary>

/// Resolve unregistered service

/// </summary>

/// <typeparam name="T">Type</typeparam>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class

{

return ResolveUnregistered(typeof(T), scope) as T;

}

/// <summary>

/// Resolve unregistered service

/// </summary>

/// <param name="type">Type</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual object ResolveUnregistered(Type type, ILifetimeScope scope = null)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

var constructors = type.GetConstructors();

foreach (var constructor in constructors)

{

try

{

var parameters = constructor.GetParameters();

var parameterInstances = new List<object>();

foreach (var parameter in parameters)

{

var service = Resolve(parameter.ParameterType, scope);

if (service == null) throw new Exception("Unknown dependency");

parameterInstances.Add(service);

}

return Activator.CreateInstance(type, parameterInstances.ToArray());

}

catch (Exception)

{

}

}

throw new Exception("No constructor  was found that had all the dependencies satisfied.");

}

/// <summary>

/// Try to resolve srevice

/// </summary>

/// <param name="serviceType">Type</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <param name="instance">Resolved service</param>

/// <returns>Value indicating whether service has been successfully resolved</returns>

public virtual bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

return scope.TryResolve(serviceType, out instance);

}

/// <summary>

/// Check whether some service is registered (can be resolved)

/// </summary>

/// <param name="serviceType">Type</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Result</returns>

public virtual bool IsRegistered(Type serviceType, ILifetimeScope scope = null)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

return scope.IsRegistered(serviceType);

}

/// <summary>

/// Resolve optional

/// </summary>

/// <param name="serviceType">Type</param>

/// <param name="scope">Scope; pass null to automatically resolve the current scope</param>

/// <returns>Resolved service</returns>

public virtual object ResolveOptional(Type serviceType, ILifetimeScope scope = null)

{

if (scope == null)

{

//no scope specified

scope = Scope();

}

return scope.ResolveOptional(serviceType);

}

/// <summary>

/// Get current scope

/// </summary>

/// <returns>Scope</returns>

public virtual ILifetimeScope Scope()

{

try

{

//when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)

return Container.BeginLifetimeScope();

}

catch (Exception)

{

//we can get an exception here if RequestLifetimeScope is already disposed

//for example, requested in or after "Application_EndRequest" handler

//but note that usually it should never happen

//when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)

return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);

}

}

}

使用介绍

public IServiceProvider ConfigureServices(IServiceCollection services){services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());services.AddMvc();              return IocManager.Instance.Initialize(services);}

特殊场景介绍

通过上面的封装后,我们可以把Controller的注册单独出来

/// <summary>

///

/// </summary>

public class ControllerRegistrar : IDependencyRegistrar

{

/// <summary>

///

/// </summary>

public int Order

{

get

{

return 0;

}

}

/// <summary>

///

/// </summary>

/// <param name="builder"></param>

/// <param name="listType"></param>

public void Register(ContainerBuilder builder, List<Type> listType)

{

builder.RegisterType(typeof(LogInterceptor));

//注册Controller,实现属性注入

var IControllerType = typeof(ControllerBase);

var arrControllerType = listType.Where(t => IControllerType.IsAssignableFrom(t) && t != IControllerType).ToArray();

builder.RegisterTypes(arrControllerType).PropertiesAutowired().EnableClassInterceptors();

}

}

下面介绍几种特殊使用方式

1.创建实例时给指定参数赋值

builder.RegisterType(typeof(TestDemo)).AsSelf();

public class TestDemo

{

private readonly string _name;

private readonly string _sex;

private readonly int _age;

public TestDemo(string name, string sex, int age)

{

_name = name;

_age = age;

_sex = sex;

}

public string Sex

{

get

{

return _sex;

}

}

public string Name

{

get

{

return _name;

}

}

public int Age

{

get

{

return _age;

}

}

}

使用示例

var iocManager = app.ApplicationServices.GetService<IIocManager>();

List<Parameter> cparams = new List<Parameter>();

cparams.Add(new NamedParameter("name", "张三"));

cparams.Add(new NamedParameter("sex", "男"));

cparams.Add(new TypedParameter(typeof(int), 2));

var testDemo = iocManager.Resolve<TestDemo>(cparams.ToArray());

Console.WriteLine($"姓名:{testDemo.Name},年龄:{testDemo.Age},性别:{testDemo.Sex}");

2.对象激活事件

 Autofac暴露五个事件接口供实例的按如下顺序调用

  1. OnRegistered

  2. OnPreparing

  3. OnActivated

  4. OnActivating

  5. OnRelease

 这些事件会在注册的时候被订阅,或者被附加到IComponentRegistration 的时候。

  builder.RegisterType(typeof(TestDemo)).AsSelf().OnRegistered(e => Console.WriteLine("OnRegistered在注册的时候调用!")).OnPreparing(e => Console.WriteLine("OnPreparing在准备创建的时候调用!")).OnActivating(e => Console.WriteLine("OnActivating在创建之前调用!")).OnActivated(e => Console.WriteLine("OnActivated创建之后调用!")).OnRelease(e => Console.WriteLine("OnRelease在释放占用的资源之前调用!"));

可以在这些事件里面做些特殊场景处理

总结

本篇介绍了Autofac在项目中的使用方式以及几种特殊使用场景。其它未介绍知识如生命周期请参考http://autofac.readthedocs.io/en/latest/getting-started/index.html。

相关文章:

  • ASP.NET Core依赖注入解读&使用Autofac替代实现

  • Autofac+Castle实现AOP事务

原文地址:http://www.cnblogs.com/yanweidie/p/autofac.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

.net core2.0下Ioc容器Autofac使用相关推荐

  1. IoC容器Autofac(1) -- 什么是IoC以及理解为什么要使用Ioc

    阅读目录: 一. 一个没有使用IoC的例子 二. 当需求发生变动时,非IoC遭遇到的困境 三. 使用IoC彻底解决问题 四. 总结 一.一个没有使用IoC的例子 IoC的全称是Inversion of ...

  2. IoC容器Autofac(2) - 一个简单示例(附demo源码)

    上篇文章中(IoC容器Autofac(1) -- 什么是IoC以及理解为什么要使用Ioc),我们用自己的方式实现了一个简陋的工厂类来实现IoC. 这里我们尝试使用Auotfac来替换我们的工厂类Mov ...

  3. IoC容器Autofac

    一.一个没有使用IoC的例子 IoC的全称是Inversion of Control,中文叫控制反转.要理解控制反转,可以看看非控制反转的一个例子. public class MPGMovieList ...

  4. IoC容器Autofac(3) - 理解Autofac原理,我实现的部分Autofac功能(附源码)

    上篇文章中,举了一个Autofac的简单例子,园友eflay反应: "虽然对autofac架构原理还是完全不懂,不过基本是明白ioc的用途了,会从已注册的类中找出符合接口的类来调用构造函数进 ...

  5. .net core2.0下使用Identity改用dapper存储数据

    前言. 已经好多天没写博客了,鉴于空闲无聊之时又兴起想写写博客,也当是给自己做个笔记.过了这么些天,我的文笔还是依然那么烂就请多多谅解了.今天主要是分享一下在使用.net core2.0下的实际遇到的 ...

  6. Ioc容器Autofac介绍

    Autofac是轻量级的开源Ioc容器,在这里可以下载http://code.google.com/p/autofac/.如果你用过其他的Ioc容器,那么学习Autofac使用也会比较容易,下面将通过 ...

  7. .Net Core2.0下使用Dapper遇到的问题

    今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和大家分享一下.下面我会还原迁移的每一个过程 ...

  8. ASP.NET MVC IOC 之AutoFac攻略

    转于:http://www.cnblogs.com/WeiGe/p/3871451.html 一.为什么使用AutoFac? 之前介绍了Unity和Ninject两个IOC容器,但是发现园子里用Aut ...

  9. Spring IoC容器初始化源码(1)—容器初始化入口以及setConfigLocations设置容器配置信息【一万字】

      基于最新Spring 5.x,对于基于XML的Spring IoC容器初始化过程中的setConfigLocations设置容器配置信息方法的源码进行了详细分析,最后给出了比较详细的方法调用时序图 ...

最新文章

  1. python tqdm_Python基础 | 一个被忽视的神器tqdm
  2. Wireshark数据包分析之数据包信息解读
  3. vsftp客户连接常见故障现象
  4. NDT 算法和一些常见配准算法
  5. “程序已停止工作”问题的解决方法,停止解决方法
  6. 增量式编码器工作原理以及使用
  7. 100天精通Andriod逆向——第5天:app逆向流程简介
  8. 团队协同办公管理工具横向对比:Worktile VS Teambition VS Tita VS Tapd
  9. Excel如何给单元格加斜线
  10. 利用 cv2 给图片添加文字
  11. Centos7.5 升级openssh到9.0p1
  12. 2019 HZNU Winter Training Day 14 Comprehensive Training
  13. java8 collections_初识Java8新特性Lambda(二) 之collections
  14. 计算机常见的应用系统,计算机常见的系统软件
  15. 基克的聚合 机器人_LOL:基克的聚合是否只适合硬辅们,软辅们能否借它发挥?...
  16. 蚂蚁市场教程 电视上怎么安装看快手TV版
  17. 阿里短信发送报错 InvalidTimeStamp.Expired
  18. 干货 | 以模型为中心,携程契约系统的演进
  19. vmware workstation 各个版本官网下载链接
  20. 你的微信昵称正在暴露你的性格和状态,你知道吗?

热门文章

  1. SQL Server Update 所有表的某一列(列名相同,类型相同)数值
  2. MySQL5.5多实例编译安装——mysqld_multi
  3. 阿里云MaxCompute香港开服 将引入更多人工智能服务
  4. python基础知识纵览(下)
  5. WebService学习笔记---CXF入门
  6. PHP截取IE浏览器并缩小原图的方法
  7. 使用 Daynamic 动态添加属性
  8. 给定两个二叉树T和S,判断S是否为T的子树
  9. Windows Phone 知识锦(12月版)
  10. 网管心得:优化网络性能给局域网提速[好文章]