Orchard提供了依赖注入机制,并且框架的实现也离不开依赖注入如模块管理、日志、事件等。在前一篇中提到在Global.asax中定义的HostInitialization创建了Autofac的IoC容器。
       private static IOrchardHost HostInitialization(HttpApplication application) {
            var host = OrchardStarter.CreateHost(MvcSingletons);
 
            host.Initialize();
 
            // initialize shells to speed up the first dynamic query
            host.BeginRequest();
            host.EndRequest();
 
            return host;
        }

其中OrchardStarter是位于Orchard.Framework项目中Environment目录下的类型

     public static IOrchardHost CreateHost(Action<ContainerBuilder> registrations) {
            var container = CreateHostContainer(registrations);
            return container.Resolve<IOrchardHost>();
        }
以下是容器创建的全量代码:
         public static IContainer CreateHostContainer(Action<ContainerBuilder> registrations) {
            ExtensionLocations extensionLocations = new ExtensionLocations();
 
            var builder = new ContainerBuilder();
            // Application paths and parameters
            builder.RegisterInstance(extensionLocations);
 
            builder.RegisterModule(new CollectionOrderModule());
            builder.RegisterModule(new LoggingModule());
            builder.RegisterModule(new EventsModule());
            builder.RegisterModule(new CacheModule());
 
            // a single default host implementation is needed for bootstrapping a web app domain
            builder.RegisterType<DefaultOrchardEventBus>().As<IEventBus>().SingleInstance();
            builder.RegisterType<DefaultCacheHolder>().As<ICacheHolder>().SingleInstance();
            builder.RegisterType<DefaultCacheContextAccessor>().As<ICacheContextAccessor>().SingleInstance();
            builder.RegisterType<DefaultParallelCacheContext>().As<IParallelCacheContext>().SingleInstance();
            builder.RegisterType<DefaultAsyncTokenProvider>().As<IAsyncTokenProvider>().SingleInstance();
            builder.RegisterType<DefaultHostEnvironment>().As<IHostEnvironment>().SingleInstance();
            builder.RegisterType<DefaultHostLocalRestart>().As<IHostLocalRestart>().Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name).SingleInstance();
            builder.RegisterType<DefaultBuildManager>().As<IBuildManager>().SingleInstance();
            builder.RegisterType<DynamicModuleVirtualPathProvider>().As<ICustomVirtualPathProvider>().SingleInstance();
            builder.RegisterType<AppDataFolderRoot>().As<IAppDataFolderRoot>().SingleInstance();
            builder.RegisterType<DefaultExtensionCompiler>().As<IExtensionCompiler>().SingleInstance();
            builder.RegisterType<DefaultRazorCompilationEvents>().As<IRazorCompilationEvents>().SingleInstance();
            builder.RegisterType<DefaultProjectFileParser>().As<IProjectFileParser>().SingleInstance();
            builder.RegisterType<DefaultAssemblyLoader>().As<IAssemblyLoader>().SingleInstance();
            builder.RegisterType<AppDomainAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
            builder.RegisterType<GacAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
            builder.RegisterType<OrchardFrameworkAssemblyNameResolver>().As<IAssemblyNameResolver>().SingleInstance();
            builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().InstancePerDependency();
            builder.RegisterType<ViewsBackgroundCompilation>().As<IViewsBackgroundCompilation>().SingleInstance();
            builder.RegisterType<DefaultExceptionPolicy>().As<IExceptionPolicy>().SingleInstance();
            builder.RegisterType<DefaultCriticalErrorProvider>().As<ICriticalErrorProvider>().SingleInstance();
            //builder.RegisterType<RazorTemplateCache>().As<IRazorTemplateProvider>().SingleInstance();
 
            RegisterVolatileProvider<WebSiteFolder, IWebSiteFolder>(builder);
            RegisterVolatileProvider<AppDataFolder, IAppDataFolder>(builder);
            RegisterVolatileProvider<DefaultLockFileManager, ILockFileManager>(builder);
            RegisterVolatileProvider<Clock, IClock>(builder);
            RegisterVolatileProvider<DefaultDependenciesFolder, IDependenciesFolder>(builder);
            RegisterVolatileProvider<DefaultExtensionDependenciesManager, IExtensionDependenciesManager>(builder);
            RegisterVolatileProvider<DefaultAssemblyProbingFolder, IAssemblyProbingFolder>(builder);
            RegisterVolatileProvider<DefaultVirtualPathMonitor, IVirtualPathMonitor>(builder);
            RegisterVolatileProvider<DefaultVirtualPathProvider, IVirtualPathProvider>(builder);
           
            builder.RegisterType<DefaultOrchardHost>().As<IOrchardHost>().As<IEventHandler>()
                .Named<IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name)
                .Named<IEventHandler>(typeof(IShellDescriptorManagerEventHandler).Name)
                .SingleInstance();
            {
                builder.RegisterType<ShellSettingsManager>().As<IShellSettingsManager>().SingleInstance();
 
                builder.RegisterType<ShellContextFactory>().As<IShellContextFactory>().SingleInstance();
                {
                    builder.RegisterType<ShellDescriptorCache>().As<IShellDescriptorCache>().SingleInstance();
 
                    builder.RegisterType<CompositionStrategy>().As<ICompositionStrategy>().SingleInstance();
                    {
                        builder.RegisterType<ShellContainerRegistrations>().As<IShellContainerRegistrations>().SingleInstance();
                        builder.RegisterType<ExtensionLoaderCoordinator>().As<IExtensionLoaderCoordinator>().SingleInstance();
                        builder.RegisterType<ExtensionMonitoringCoordinator>().As<IExtensionMonitoringCoordinator>().SingleInstance();
                        builder.RegisterType<ExtensionManager>().As<IExtensionManager>().SingleInstance();
                        {
                            builder.RegisterType<ExtensionHarvester>().As<IExtensionHarvester>().SingleInstance();
                            builder.RegisterType<ModuleFolders>().As<IExtensionFolders>().SingleInstance()
                                .WithParameter(new NamedParameter("paths", extensionLocations.ModuleLocations));
                            builder.RegisterType<CoreModuleFolders>().As<IExtensionFolders>().SingleInstance()
                                .WithParameter(new NamedParameter("paths", extensionLocations.CoreLocations));
                            builder.RegisterType<ThemeFolders>().As<IExtensionFolders>().SingleInstance()
                                .WithParameter(new NamedParameter("paths", extensionLocations.ThemeLocations));
 
                            builder.RegisterType<CoreExtensionLoader>().As<IExtensionLoader>().SingleInstance();
                            builder.RegisterType<ReferencedExtensionLoader>().As<IExtensionLoader>().SingleInstance();
                            builder.RegisterType<PrecompiledExtensionLoader>().As<IExtensionLoader>().SingleInstance();
                            builder.RegisterType<DynamicExtensionLoader>().As<IExtensionLoader>().SingleInstance();
                            builder.RegisterType<RawThemeExtensionLoader>().As<IExtensionLoader>().SingleInstance();
                        }
                    }
 
                    builder.RegisterType<ShellContainerFactory>().As<IShellContainerFactory>().SingleInstance();
                }
 
                builder.RegisterType<DefaultProcessingEngine>().As<IProcessingEngine>().SingleInstance();
            }
 
            builder.RegisterType<RunningShellTable>().As<IRunningShellTable>().SingleInstance();
            builder.RegisterType<DefaultOrchardShell>().As<IOrchardShell>().InstancePerMatchingLifetimeScope("shell");
            builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>().InstancePerMatchingLifetimeScope("shell");
 
            registrations(builder);
 
            var autofacSection = ConfigurationManager.GetSection(ConfigurationSettingsReaderConstants.DefaultSectionName);
            if (autofacSection != null)
                builder.RegisterModule(new ConfigurationSettingsReader());
 
            var optionalHostConfig = HostingEnvironment.MapPath("~/Config/Host.config");
            if (File.Exists(optionalHostConfig))
                builder.RegisterModule(new ConfigurationSettingsReader(ConfigurationSettingsReaderConstants.DefaultSectionName, optionalHostConfig));
 
            var optionalComponentsConfig = HostingEnvironment.MapPath("~/Config/HostComponents.config");
            if (File.Exists(optionalComponentsConfig))
                builder.RegisterModule(new HostComponentsConfigModule(optionalComponentsConfig));
 
            var container = builder.Build();
 
            //
            // Register Virtual Path Providers
            //
            if (HostingEnvironment.IsHosted) {
                foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
                    HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
                }
            }
 
            ControllerBuilder.Current.SetControllerFactory(new OrchardControllerFactory());
            FilterProviders.Providers.Add(new OrchardFilterProvider());
 
            GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new DefaultOrchardWebApiHttpControllerSelector(GlobalConfiguration.Configuration));
            GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new DefaultOrchardWebApiHttpControllerActivator(GlobalConfiguration.Configuration));
            GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
 
            GlobalConfiguration.Configuration.Filters.Add(new OrchardApiActionFilterDispatcher());
            GlobalConfiguration.Configuration.Filters.Add(new OrchardApiExceptionFilterDispatcher());
            GlobalConfiguration.Configuration.Filters.Add(new OrchardApiAuthorizationFilterDispatcher());
 
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new ThemeAwareViewEngineShim());
 
            var hostContainer = new DefaultOrchardHostContainer(container);
            //MvcServiceLocator.SetCurrent(hostContainer);
            OrchardHostContainerRegistry.RegisterHostContainer(hostContainer);
 
            // Register localized data annotations
            ModelValidatorProviders.Providers.Clear();
            ModelValidatorProviders.Providers.Add(new LocalizedModelValidatorProvider());
 
            return container;
        }
代码很长,但是可以分为以下几个部分:
  • 基础组件:日志、事件、缓存
  • 环境相关:路径、程序集、编译器,Http上下文
  • 异常处理:异常策略
  • 拓展相关:拓展目录、拓展依赖、拓展管理。
  • 多租户相关:Shell表、Shell启动。
  • 拓展的依赖注入:拓展模块中的自定义注入、Host及HostComponents配置文件的注入。
  • MVC相关:ControllerFactory替换,过滤器加载
  • WebAPI相关:Selector、Activator替换,DependencyResolver替换、过滤器加载
  • ViewEngine:使用ThemeAwareViewEngineShim。
  • Model相关:使用LocalizedModelValidatorProvider作为Model验证器。
 
相对特殊的地方有:
  1. 日志的自动属性注入:
Orchard使用Autofac实现了日志的属性自动注入,具体代码如下:
         protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
            var implementationType = registration.Activator.LimitType;
 
            // build an array of actions on this type to assign loggers to member properties
            var injectors = BuildLoggerInjectors(implementationType).ToArray();
 
            // if there are no logger properties, there's no reason to hook the activated event
            if (!injectors.Any())
                return;
 
            // otherwise, whan an instance of this component is activated, inject the loggers on the instance
            registration.Activated += (s, e) => {
                foreach (var injector in injectors)
                    injector(e.Context, e.Instance);
            };
        }
 
        private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType) {
            // Look for settable properties of type "ILogger"
            var loggerProperties = componentType
                .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
                .Select(p => new {
                    PropertyInfo = p,
                    p.PropertyType,
                    IndexParameters = p.GetIndexParameters(),
                    Accessors = p.GetAccessors(false)
                })
                .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger
                .Where(x => x.IndexParameters.Count() == ) // must not be an indexer
                .Where(x => x.Accessors.Length != || x.Accessors[].ReturnType == typeof(void)); //must have get/set, or only set
 
            // Return an array of actions that resolve a logger and assign the property
            foreach (var entry in loggerProperties) {
                var propertyInfo = entry.PropertyInfo;
 
                yield return (ctx, instance) => {
                    string component = componentType.ToString();
                    if (component != instance.GetType().ToString()) {
                        return;
                    }
                    var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<ILogger>(new TypedParameter(typeof(Type), componentType)));
                    propertyInfo.SetValue(instance, logger, null);
                };
            }
        }

其实它的核心就是注册时通过对象类型查找是否有一个类型为Ilogger的可读写属性,拿出来赋值。

 

  2. 通过动态代理实现的事件机制:

  在.Net中提到事件就会想到委托,但是Orchard的事件比较特殊,它将事件定义为一个接口(实现IEventHandler的接口),对于生产者(事件发布者)它仅需要通过Orchard的容器注入一个相应接口类型即可,而观察者(事件的真实处理器)则仅需要实现相应接口即可(其余框架会自动处理)。
对.Net的委托不了解的可参考张子阳老师的博客:http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html
Orchard事件的使用如下(使用张子阳老师例子改了一下):
         public interface IBoilHandler : IEventHandler
        {
            void OnTemperatureChanged(int temperature);
        }
 
        // 热水器
        public class Heater
        {
            private int temperature;
            private IBoilHandler _boilhandler;
 
            public Heater(IBoilHandler boilhandler)
            {
                _boilhandler = boilhandler;
            }
 
            // 烧水
            public void BoilWater()
            {
                for (int i = ; i <= ; i++)
                {
                    temperature = i;
 
                    if (temperature > )
                    {
                        if (_boilhandler != null)
                        { //如果有对象注册
                            _boilhandler.OnTemperatureChanged(temperature);  //调用所有注册对象的方法
                        }
                    }
                }
            }
        }
 
        // 警报器
        public class Alarm : IBoilHandler
        {
            public void OnTemperatureChanged(int temperature)
            {
                Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", temperature);
            }
        }
 
        // 显示器
        public class Display : IBoilHandler
        {
            public void OnTemperatureChanged(int temperature)
            {
                Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", temperature);
            }
        }
 
那么这是如何实现的呢?
两个要点:
  1. 在注册服务时将所有通过继承IEventhandler的子接口(如上面代码的IBoilHandler)通过动态代理,将相应接口的方法执行全部拦截并转到EventBus上,调用Notify方法,其参数为当前接口名称+方法名称和相应的参数。--生产者
   public class EventsRegistrationSource : IRegistrationSource {
        private readonly DefaultProxyBuilder _proxyBuilder;
 
        public EventsRegistrationSource() {
            _proxyBuilder = new DefaultProxyBuilder();
        }
 
        public bool IsAdapterForIndividualComponents {
            get { return false; }
        }
 
        public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor) {
            var serviceWithType = service as IServiceWithType;
            if (serviceWithType == null)
                yield break;
 
            var serviceType = serviceWithType.ServiceType;
            if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
                yield break;
 
            var interfaceProxyType = _proxyBuilder.CreateInterfaceProxyTypeWithoutTarget(
                serviceType,
                new Type[],
                ProxyGenerationOptions.Default);
 
 
            var rb = RegistrationBuilder
                .ForDelegate((ctx, parameters) => {
                    var interceptors = new IInterceptor[] { new EventsInterceptor(ctx.Resolve<IEventBus>()) };
                    var args = new object[] { interceptors, null };
                    return Activator.CreateInstance(interfaceProxyType, args);
                })
                .As(service);
 
            yield return rb.CreateRegistration();
        }
    }
 
    public class EventsInterceptor : IInterceptor {
        private readonly IEventBus _eventBus;
        private static readonly ConcurrentDictionary<Type,MethodInfo> _enumerableOfTypeTDictionary = new ConcurrentDictionary<Type, MethodInfo>();
 
        public EventsInterceptor(IEventBus eventBus) {
            _eventBus = eventBus;
        }
 
        public void Intercept(IInvocation invocation) {
            var interfaceName = invocation.Method.DeclaringType.Name;
            var methodName = invocation.Method.Name;
 
            var data = invocation.Method.GetParameters()
                .Select((parameter, index) => new { parameter.Name, Value = invocation.Arguments[index] })
                .ToDictionary(kv => kv.Name, kv => kv.Value);
 
            var results = _eventBus.Notify(interfaceName + "." + methodName, data);
 
            invocation.ReturnValue = Adjust(results, invocation.Method.ReturnType);
        }
 
        public static object Adjust(IEnumerable results, Type returnType) {
            if (returnType == typeof(void) ||
                results == null ||
                results.GetType() == returnType) {
                return results;
            }
 
            // acquire method:
            // static IEnumerable<T> IEnumerable.OfType<T>(this IEnumerable source)
            // where T is from returnType's IEnumerable<T>
            var enumerableOfTypeT = _enumerableOfTypeTDictionary.GetOrAdd( returnType, type => typeof(Enumerable).GetGenericMethod("OfType", type.GetGenericArguments(), new[] { typeof(IEnumerable) }, typeof(IEnumerable<>)));
            return enumerableOfTypeT.Invoke(null, new[] { results });
        }
    }

  上面代码有个重点就是:

  if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler))
yield break;

  如果不是接口、如果类型不是IEventHandler的子类型(子接口)且当前类型就是IEventHandler,满足这三个接口就跳出。正过来说就是:

  •  类型一定是接口
  •  一定是继承IEventHandler的接口
  •  一定不是IEventHandler本身
  2. 把所有实现了IEventHandler的实现全部解析到EventBus的_eventHandlers字段中。 --消费者
换句话说就是,当应用程序初始化的时候EventBus里面的处理器都已经被加载了,但真正触发事件时,仅仅是通过拦截器将相应接口的方法转到了Notify方法上,进而去通知相应的Handlers执行。
下面代码位于ShellContainerFactory.cs文件中
               if (typeof(IEventHandler).IsAssignableFrom(item.Type)) {
                            var interfaces = item.Type.GetInterfaces();
                            foreach (var interfaceType in interfaces) {
 
                                // register named instance for each interface, for efficient filtering inside event bus
                                // IEventHandler derived classes only
                                if (interfaceType.GetInterface(typeof (IEventHandler).Name) != null) {
                                    registration = registration.Named<IEventHandler>(interfaceType.Name);
                                }
                            }
                        }
 
  3. ICacheManager的注入:
ICacheManager的特殊之处就在于依赖它的对象在通过构造注入ICacheManager会将当前对象作为参数来实例化ICacheManager。
         protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) {
            var needsCacheManager = registration.Activator.LimitType
                .GetConstructors()
                .Any(x => x.GetParameters()
                    .Any(xx => xx.ParameterType == typeof(ICacheManager)));
 
            if (needsCacheManager) {
                registration.Preparing += (sender, e) => {
                    var parameter = new TypedParameter(
                        typeof(ICacheManager),
                        e.Context.Resolve<ICacheManager>(new TypedParameter(typeof(Type), registration.Activator.LimitType)));
                    e.Parameters = e.Parameters.Concat(new[] { parameter });
                };
            }
        }
 
小结:
本章主要是分析了Orchard中IoC容器的创建以及基础设施的注册,其中对Log、Event和CacheManager进行了特殊处理,并且处理方式均是使用Autofac提供的一系列特性,动态的对注册的组件进行额外操作,另外在Orchard中它还针对Shell级别创建了一个子容器,所以后续将对Orchard的依赖注入进一步分析。
 
参考:

Orchard详解--第三篇 依赖注入之基础设施的更多相关文章

  1. Java程序员从笨鸟到菜鸟之(一百零二)sql注入攻击详解(三)sql注入解决办法

    sql注入攻击详解(二)sql注入过程详解 sql注入攻击详解(一)sql注入原理详解 我们了解了sql注入原理和sql注入过程,今天我们就来了解一下sql注入的解决办法.怎么来解决和防范sql注入, ...

  2. 详解AngularJS中的依赖注入

    点击查看AngularJS系列目录 依赖注入 一般来说,一个对象只能通过三种方法来得到它的依赖项目: 我们可以在对象内部创建依赖项目 我们可以将依赖作为一个全局变量来进行查找或引用 我们可以将依赖传递 ...

  3. 详解Java Spring各种依赖注入注解的区别

    注解注入顾名思义就是通过注解来实现注入,Spring和注入相关的常见注解有Autowired.Resource.Qualifier.Service.Controller.Repository.Comp ...

  4. ASP.NET Core 学习笔记 第三篇 依赖注入框架的使用

    前言 首先感谢小可爱门的支持,写了这个系列的第二篇后,得到了好多人的鼓励,也更加坚定我把这个系列写完的决心,也能更好的督促自己的学习,分享自己的学习成果.还记得上篇文章中最后提及到,假如服务越来越多怎 ...

  5. 【laravel5.*】详解laravel中的依赖注入

    1.下面这个是自定义的类,钉钉扫码登录web 网页授权OAuth2.0,是一个典型的依赖注入参考示例:

  6. Orchard详解--第八篇 拓展模块及引用的预处理

    从上一篇可以看出Orchard在处理拓展模块时主要有两个组件,一个是Folder另一个是Loader,前者用于搜索后者用于加载. 其中Folder一共有三个:Module Folder.Core Fo ...

  7. Orchard详解--第七篇 拓展模块(译)

    Orchard作为一个组件化的CMS,它能够在运行时加载任意模块. Orchard和其它ASP.NET MVC应用一样,支持通过Visual Studio来加载已经编译为程序集的模块,且它还提供了自定 ...

  8. Orchard详解--第六篇 CacheManager 2

    接上一篇,关于ICacheContextAccessor先看一下默认实现,用于保存一个获取上下文,且这个上下文是线程静态的: public class DefaultCacheContextAcces ...

  9. Orchard详解--第五篇 CacheManager

    上一篇文章介绍了Orchard中的缓存,本篇主要针对CacheManager进行分析,CacheManager在Orchard中用于存储应用程序的配置信息以及框架内部的一些功能支持,包括整个拓展及拓展 ...

随机推荐

  1. Android--UI之GridView

    前言 这篇博客介绍一下Android平台下,GridView控件的开发.针对GridView控件的一些常用属性.方法,以及注意事项进行讲解,最后将以一个Demo展示GridView控件的使用. Gri ...

  2. 使用docker搭建数据分析环境

    注:早在学习<云计算>这门课之前就已经知道docker,学习这门课时老师还鼓励我们自己尝试一下:但是直到去年年底才有机会尝试,用过之后感觉确实很好用.最近需要部署几个shiny应用,又回顾 ...

  3. solr(五): centos中, 整合 tomcat&solr

    前言 虽然windows下, tomcat和solr整合起来灰常的方便, 但是, 一般像这种东西, 都很少部署在windows中, 更多的是部署到linux中去. 其实, 步骤是一样的, 这里, 我在 ...

  4. OnlineJudge难度与正确度的相关性检验

    本着做题的心态,上了东莞理工学院的 oj 网:在选择难度的时候发现有些题目通过率和难度可能存在着某些关系,于是决定爬下这些数据简单查看一下是否存在关系. 一.新建项目 我是用 Scrapy 框架爬取的 ...

  5. XML概念定义以及如何定义xml文件编写约束条件java解析xml DTD XML Schema JAXP java xml解析 dom4j 解析 xpath dom sax

    本文主要涉及:xml概念描述,xml的约束文件,dtd,xsd文件的定义使用,如何在xml中引用xsd文件,如何使用java解析xml,解析xml方式dom sax,dom4j解析xml文件 XML来 ...

  6. SpringBoot学习(一)-->Spring的发展

    一.Spring的发展 1.Spring1.x 时代 在Spring1.x时代,都是通过xml文件配置bean,随着项目的不断扩大,需要将xml配置分放到不同的配置文件中,需要频繁的在java类和xm ...

  7. JS_正则表达式_使用字符串创建的正则表达式_反斜杠也需要添加转义符

    备注:   使用字符串创建的正则表达式:"\"也需要加转义符: var reg1=new RegExp("\\w+");       这和 直接使用:var r ...

  8. 【转载】ASP.NET实现文件下载的功能

    文件下载是很多网站中含有的常用功能,在ASP.NET中可以使用FileStream类.HttpRequest对象.HttpResponse对象相互结合,实现输出硬盘文件的功能.该方法支持大文件.续传. ...

  9. asp.net core 2.1 配置管理

    1. 直接读取配置 StartUp类中使用 Configuration["ConnectionString"], Configuration["AliyunAkSk:Ak ...

  10. [android] 内容提供者实现

    [android] 内容提供者实现 上一节的主机名类似网络上的域名,协议是content://,可以定义一下规则 content://主机名/insert 添加操作 content://主机名/del ...