企业级应用框架(五)IOC容器在框架中的应用

前言

  在上一篇我大致的介绍了这个系列所涉及到的知识点,在本篇我打算把IOC这一块单独提取出来讲,因为IOC容器在解除框架层与层之间的耦合有着不可磨灭的作用。当然在本系列前面的三篇中我也提供了一种基于反射的解耦方式,但是始终不是很优雅,运用到项目中显得别扭。目前,我所掌握的IOC容器主要有两个:一个是 unity,另一个则是spring.net,经过慎重的思考我还是决定选择unity 2.0做为本系列的IOC容器,原因主要有两个:第一,他是一个轻量级的容器且师出名门(微软),第二,它提供了简单的拦截机制,在它的基础上实现AOP显得非常的简单,下面开始我们今天的议题......

什么是IOC容器

  IOC容器是对控制反转与依赖注入的一种实现,关于什么是控制反转,什么是依赖注入,网上一搜一大把,我这里就不在多说了,我们需要关注的就是IOC容器到底能够为我们做些什么事情,其实说白了,IOC容器就是通过相应的配置,用来为我们创建实例,使我们摆脱了new的魔咒,这在层与层之间的解耦中有着重要的意义,至于层次间为什么要解耦请参见我的第一篇, 本文着重介绍unity 2.0,您需要在项目中添加对Microsoft.Practices.Unity.dll与Microsoft.Practices.Unity.Configuration.dll的引用,下面我通过简单doom来讲述它的运用,程序如图

IOC项目引用了IService项目,但并未引用service项目,IService项目中定义的是服务接口,Service项目引用了IService项目并实现了里面的服务接口。我们现在要做的事情就是在IOC中采用IService接口标识服务,在调用时采用unity容器读取配置文件帮助我们把接口实例化,其具体的服务来自Service项目(我们的IOC项目没有引用Service项目所以是无法new的),为了很好的运用Unity容器,我做了一下封装,代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using System.Configuration;
using System.Reflection;namespace IOC
{public class ServiceLocator{/// <summary>/// IOC容器/// </summary>private readonly IUnityContainer container;private static readonly ServiceLocator instance = new ServiceLocator();/// <summary>/// 服务定位器单例/// </summary>public static ServiceLocator Instance{get { return instance; }}private ServiceLocator(){//读取容器配置文件UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");//创建容器container = new UnityContainer();//配置容器
            section.Configure(container);}#region   /// <summary>/// 创建构造函数参数/// </summary>/// <param name="overridedArguments"></param>/// <returns></returns>private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments){List<ParameterOverride> overrides = new List<ParameterOverride>();Type argumentsType = overridedArguments.GetType();argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList().ForEach(property =>{var propertyValue = property.GetValue(overridedArguments, null);var propertyName = property.Name;overrides.Add(new ParameterOverride(propertyName, propertyValue));});return overrides;}#endregion#region 公共方法/// <summary>/// 创建指定类型的容器/// </summary>/// <typeparam name="T"></typeparam>/// <returns></returns>public T GetService<T>(){return container.Resolve<T>();}/// <summary>/// 根据指定名称的注册类型/// 创建指定的类型/// </summary>/// <typeparam name="T"></typeparam>/// <param name="name">注册类型配置节点名称</param>/// <returns></returns>public T GetService<T>(string name){return container.Resolve<T>(name);}/// <summary>/// 用指定的构造函数参数/// 创建实体/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="overridedArguments">属性名对应参数名,属性值对应/// 参数值得动态参数实体</param>/// <returns></returns>public T GetService<T>(object overridedArguments){var overrides = GetParameterOverrides(overridedArguments);return container.Resolve<T>(overrides.ToArray());}/// <summary>/// /// </summary>/// <typeparam name="T"></typeparam>/// <param name="name"></param>/// <param name="overridedArguments"></param>/// <returns></returns>public T GetService<T>(string name,object overridedArguments){var overrides = GetParameterOverrides(overridedArguments);return container.Resolve<T>(name, overrides.ToArray());}#endregion}
}
View Code

好了,下面开始我们的测试,我们首先在IService项目创建一个ISayHello服务接口代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace IService
{public interface ISayHello{string hello();}
}
View Code

下面我们在Service项目中创建一个ChineseSayHello服务实现ISayHello接口代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;namespace Service
{public class ChineseSayHello : ISayHello{public string hello(){return "你好";}}
}
View Code

下面我们创建一个测试页面Test.aspx,后台代码如下

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;namespace IOC
{public partial class Test : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){ISayHello sayhello = ServiceLocator.Instance.GetService<ISayHello>();showInfo.InnerText = sayhello.hello();}}
}
View Code

好,下面来看一看我们的配置文件

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><register  type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>

<register/>节点是告诉容器我要向容器中注册一个ISayHello接口类型,并且当每次要创建的ISayHello类型的时候都映射到ChineseSayHello实例。我们执行程序,得到的结果为:你好,这说明我们的容器正确的为我们创建ChineseSayHello实例。如果有一天我们觉得ChineseSayHello不好,我们想换一个服务来实现ISayHello,比如:EnglishSayHello,从而替代ChineseSayHello,我们仅需要创建一个EnglishSayHello类型,修改下配置文件,如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;namespace Service
{public class EnglishSayHello : ISayHello{public string hello(){return "hello";}}
}
View Code
<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><!--<register  type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register>--><register  type="IService.ISayHello,IService"  mapTo="Service.EnglishSayHello,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code

程序的运行结果为:hello,实例创建成功。简简单单的一个例证,我们看见了IOC容器在给我们带来的巨大好处,我们IOC层根本不再依赖于具体的服务,我们想要什么实例配置下文件即可,这样极大的增加了程序的灵活性与可扩张性.

    下面,我们来讨论一下容器实例的生命周期,也就是实例在容器中的存活时间。举个例子,我们在同样的配置文件下连续创建多个ISayHello服务实例,很显然,这样的多个实例是来自同样的类型的,现在我们关心的是容器是每一次都会为我们创建该类型的实例,还是仅仅只为我们创建一个,以后所有的ISayHello都引用同一个实例呢?我们测试下,代码如下

using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;namespace IOC
{public partial class Test : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){ISayHello sayhello = ServiceLocator.Instance.GetService<ISayHello>();ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>();showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();}}
}
View Code

我们得到的结果是False,很显然容器每次都为我们去创建了一个实例。事实上Unity容器创建实例的机制是这样的:首先去容器中查找有没有这样的实例还保持在容器中,如果有的话则直接拿出来,如果没有的话则重新去创建一个。现在关键的问题是容器采用什么用的机制去保存这些被创建出来的实例,也就是实例在容器中的生命周期,在默认的情况下,实例被创建出来,容器即不再保存该实例,故在下次创建的时候容器找不到这样的实例,从而重新创建该类型实例,事实上实例的生命周期是可以配置的,我们甚至可以自定义实例的生命周期,下面我们修改下配置文件,设置实例的lifetime类型为singleton,即让实例永远保持在容器中,如下

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><!--<register  type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register>--><register  type="IService.ISayHello,IService"  mapTo="Service.EnglishSayHello,Service"><lifetime type="singleton"/></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code

我们在运行程序,得到的结果是:True,说明我们每次都引用了同一个实例,容器很好的帮我们实现了单例模式,除了singleton外,容器还默认了其他的几种实例生命周期,这里就不在多说了。注:我们所说的实例生命周期不是指实例的创建到销毁,而是指实例在容器中创建,受容器管辖的时间范围。

    Unity容器支持为一个接口或者基类注册多个映射节点,但是每个节点需要采用不同的名称标识,在创建实例的时候,也通过该节点名称来创建指定的映射实例,例如

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><register   type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register><register name="english" type="IService.ISayHello,IService"  mapTo="Service.EnglishSayHello,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code
using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;namespace IOC
{public partial class Test : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){ISayHello sayhello = ServiceLocator.Instance.GetService<ISayHello>();ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>("english");showInfo.InnerText = string.Format("{0}+{1}", sayhello1.hello(), sayhello.hello());//showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();
        }}
}
View Code

结果为:hello+你好,我们成功的通过了配置文件中的注册节点名称来创建我们的具体服务实例。我们知道创建实例是需要调用实例的构造函数的,很显然容器默认的为我们调用了构造函数,倘若构造函数带有参数,则容器则会创建相应的参数实例。现在问题来了,假如我的参数是一个接口或者抽象类型怎么办? 很显然要能创建这样的参数我们就必须知道参数的映射类型, 看如下例子,我们在IService项目中重新创建一个接口

using System;
using System.Collections.Generic;
using System.Linq; using System.Text; namespace IService { public interface ISay { string Say(); } }
View Code

我们写一个服务实现该接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IService;namespace Service
{public class ComSayHello_V2 : ISay{public ISayHello Chinese { get; set; }public ComSayHello_V2(ISayHello chinese){this.Chinese = chinese;}public string Say(){return this.Chinese.hello();}}
}
View Code

配置文件如下

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><register   type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register><register name="english" type="IService.ISayHello,IService"  mapTo="Service.EnglishSayHello,Service"></register><register  type="IService.ISay,IService"  mapTo="Service.ComSayHello_V2,Service"></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code
using IService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;namespace IOC
{public partial class Test : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e){//ISayHello sayhello = ServiceLocator.Instance.GetService<ISayHello>();//ISayHello sayhello1 = ServiceLocator.Instance.GetService<ISayHello>("english");//showInfo.InnerText = string.Format("{0}+{1}", sayhello1.hello(), sayhello.hello());//showInfo.InnerText = sayhello.GetHashCode().Equals(sayhello1.GetHashCode()).ToString();ISay say = ServiceLocator.Instance.GetService<ISay>();showInfo.InnerText=say.Say();}}
}
View Code

我们得到结果:你好。在配置文件中我们添加了两个注册节点,从结果中我们看见,容器默认选择了未命名的节点,倘若我们注释该节点程序将报错,程序没办法自动识别带名称的节点,要想让程序识别带名称的节点我们需要配置构造函数参数,配置如下

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><!--<register   type="IService.ISayHello,IService"  mapTo="Service.ChineseSayHello,Service"></register>--><register name="english" type="IService.ISayHello,IService"  mapTo="Service.EnglishSayHello,Service"><!--<lifetime type="singleton"/>--></register><register  type="IService.ISay,IService"  mapTo="Service.ComSayHello_V2,Service"><constructor><param name="say" dependencyName="english"></param></constructor></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code

结果正确的显示为:hello,在这里顺便提一下如果我们取消english注册节点lifetime的注释,我们会发现每次创建ComSayHello_V2的参数将来自同一个实例的引用,原因请参见,上文的实例生命周期。
    当然我们也可以直接在配置文件的构造函数中指定,参数类型而避免注册其他类型节点,配置文件代码如下,结果一样

<?xml version="1.0" encoding="utf-8"?><!--有关如何配置 ASP.NET 应用程序的详细消息,请访问http://go.microsoft.com/fwlink/?LinkId=169433-->
<configuration><configSections><section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/></configSections><unity xmlns="http://schemas.microsoft.com/practices/2010/unity"><container><register  type="IService.ISay,IService"  mapTo="Service.ComSayHello_V2,Service"><constructor><param  name="say" dependencyType="Service.EnglishSayHello,Service"></param></constructor></register></container></unity><system.web><compilation debug="true" targetFramework="4.0" /></system.web>
</configuration>
View Code

    其实,我们还能够在配置文件中给参数赋值,但是如果参数是一个复杂类型,比如类的时候,我们就需要一个转换器,把字符串类型的值转换为指定的赋值类型,因为在配置文件中我们赋值的类型只能是string。转换器在平时实践中用的少,所以我不打算多说。需要注意的是,如果我们的类中有多个构造函数的话,那么容器默认总会选择参数最多的那个构造函数。
   以上所介绍的归根到底也只是一种构造函数注入。其实Unity还提供能属性注入与方法注入,即在创建实例的时候动态为某个属性赋值或者调用某个方法,其实这个要做到也蛮简单的,我们只需要在相应的属性上面打上[Dependency]特性,在方法上打上[InjectionMethod]特性即可,但是这两种方式对类的侵入性太强,不推荐使用

总结

    本文简单的演示了Unity IOC的一些使用方法,因为在我的框架中,Unity在层次解耦中充当了重要的作用,除此之外Unity其实还能实现的AOP拦截,但是由于篇幅的原因不再多讲,在这里要提醒大家务必理解实体的生命周期,因为这对实现单元工作模式有着重要的意义。在我的系列前三篇中,我都是采用了是反射来解耦,有兴趣的朋友可以尝试下用Unity取代它。我目前写的案例与前面系列的版本框架有很大的差异,所以有些知识点必须和大家说明,相信在接下来的一到两篇中,就能与大伙见面,祝大伙周末愉快。本篇测试源码请点击这里

 

转载于:https://www.cnblogs.com/shaoshun/p/3868968.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/260197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端开发需要学什么_都2020年了,还在纠结学什么语言?| 后端篇

几个礼拜前&#xff0c;一个学弟问我&#xff1a;“Ray&#xff0c;我打算之后要找工作了&#xff0c;不过现在自己没有特别深入的语言&#xff0c;最近想找一门好好学一下&#xff0c;你觉得学什么语言好呀&#xff1f;”我表示&#xff1a;“这个要看你求职方向、个人喜好、市…

10个非常有用的CSS hack和技术

转自&#xff1a;http://www.qianduan.net/10-useful-css-hacks-and-technique.html 1 – 跨浏览器的inline-block <style>li {width: 200px;min-height: 250px;border: 1px solid #000;display: -moz-inline-stack;display: inline-block;margin: 5px;zoom: 1;*display:…

Java的递归算法

递归算法设计的基本思想是&#xff1a;对于一个复杂的问题&#xff0c;把原问题分解为若干个相对简单类同的子问题&#xff0c;继续下去直到子问题简单到可以直接求解&#xff0c;也就是说到了递推的出口&#xff0c;这样原问题就有递推得解。 关键要抓住的是&#xff1a; &…

发现Java程序中的Bug

昨天在CSDN上阅读 "Java中十个常见的违规编码"这篇文章时&#xff0c;无意中找到了3个 "发现Java程序中的Bug"工具。 文章地址&#xff1a;http://www.csdn.net/article/2012-09-11/2809829-common-code-violations-in-java其中&#xff0c; FindBugs™ - …

shiro前后端分离_为什么要前后端分离?前后端分离的优点是什么?

随着互联网的高速发展以及IT开发技术的升级&#xff0c;前后端分离已成为互联网项目开发的业界标准使用方式。在实际工作中&#xff0c;前后端的接口联调对接工作量占HTML5大前端人员日常工作的30%-50%&#xff0c;甚至会更高。接下来千锋小编分享的广州HTML5大前端学习就给大家…

hdu--1075--字典树||map

做这题的时候 我完全没想到 字典树 就直接用map来做了 - 我是有 多不 敏感啊~~ 然后去 discuss 一看 很多都是说 字典树的问题.... 字典树 给我感觉 它的各个操作的意思都很清晰明了 直接手写 不那么容易啊。。 晚些 时候 试下来写------用map写是真心方便 只要注意下那么\n的吸…

php的类装载的步骤,设计PHP自动类装载功能

在使用面向对象方法做PHP开发时&#xff0c;可能会经常使用到各个路径中的类文件&#xff0c;这就需要大量的 include 或 require&#xff0c;而 PHP 提供了一个比较快捷的方式&#xff0c;就是利用函数 __autoload 可以编程实现动态的类装载功能&#xff0c;这样就不需要手动的…

网站首页幻灯片

Js页面: View Code /** * 大眼睛广告轮播 */ var indexEye {autoTime: 0,init: function () {var eyeObj $("#dyj_pics a:eq(0) img:eq(0)");eyeObj.attr("src", eyeObj.attr("data-imgSrc"));eyeObj.load(function () {indexEye.autoTime se…

Qt之QAbstractItemView视图项拖拽(二)

一、需求说明 上一篇文章Qt之QAbstractItemView视图项拖拽(一)讲述了实现QAbstractItemView视图项拖拽的一种方式&#xff0c;是基于QDrag实现的&#xff0c;这个类是qt自己封装好了的&#xff0c;所以可定制性也就没有了那么强&#xff0c;最明显的是&#xff0c;这个类在执…

电脑控制苹果手机_必备神器,电脑控制手机

序一款电脑端的神器&#xff0c;它可以任意的操纵你的手机。****QtScrcpy可以通过USB(或通过TCP/IP)连接Android设备&#xff0c;并进行显示和控制。不需要root权限。单个应用程序最多支持16个安卓设备同时连接。同时支持GNU/Linux&#xff0c;Windows和MacOS三大主流桌面平台。…

生活大爆炸版石头剪刀布

题目描述 Description石头剪刀布是常见的猜拳游戏&#xff1a;石头胜剪刀&#xff0c;剪刀胜布&#xff0c;布胜石头。如果两个人出拳一样&#xff0c;则不分胜负。在《生活大爆炸》第二季第8集中出现了一种石头剪刀布的升级版游戏。升级版游戏在传统的石头剪刀布游戏的基础上&…

jquery事件 on(),live(),delegate(),blind()

jQuery推出on()的目的有2个&#xff0c;一是为了统一接口&#xff0c;二是为了提高性能&#xff0c; 所以从现在开始用on()替换bind(), live(), delegate吧。 尤其是不要再用live()了&#xff0c;因为它已经处于不推荐使用列表了[1.7已经被删除]。 如果只绑定一次事件&#xff…

用python庆祝生日_生日到底该过阴历还是阳历好呢?不是迷信,都怪我们大意!...

过生日到底该过阴历还是阳历&#xff1f;答案说出来你可能都不信在我们国家&#xff0c;过生日有两种不同的方式&#xff0c;因为有两种不同的日子的计算方式&#xff0c;分为阴历和阳历。一般来说&#xff0c;在农村和一些比较落后的地方&#xff0c;人们习惯于用阴历来计算生…

WSS3.0自带数据库可以使用SQL 2005 Server Management Studio来管理

默认情况下&#xff0c;安装完WSS3.0后&#xff0c;会自动安装一个自带的SQL Server 2005 Embedded Edition数据库&#xff0c;但是此数据库却没有管理工具,不像安装SQL 2005其它版本会有管理工具。如果你要管理数据库&#xff0c;这时怎么办呢。经过俺试了一上午了&#xff0c…

CPU的高速缓存存储器知识整理

基于缓存的存储器层次结构 基于缓存的存储器层次结构行之有效&#xff0c;是因为较慢的存储设备比较快的存储设备更便宜&#xff0c;还因为程序往往展示局部性&#xff1a; 时间局部性&#xff1a;被引用过一次的存储器的位置很可能在不远的将来被再次引用。 空间局部性&#x…

uniapp光标自动定义到文本框_word技巧自动生成毕业论文目录

一篇word文档&#xff0c;内容有大的章&#xff0c;小的节。如何把章节抽出来生成目录&#xff1f;WORD →点击需要插入的地方 → 插入菜单 → 索引和目录 → 目录 → 确定。1 创建标题目录Word 一般是利用标题或者大纲级别来创建目录的。因此&#xff0c;在创建目录之前&#…

JSTL

2019独角兽企业重金招聘Python工程师标准>>> 说明 JSTL 是 JAVA 中的一个定制标记库集。实现了JSP页面中代码的复用&#xff0c;增加了JSP页面的可读性&#xff0c;方便查看和参与开发 使用 1.下载地址 http://archive.apache.org/dist/jakarta/taglibs/standard/bi…

自然语言0_nltk中文使用和学习资料汇总

sklearn实战-乳腺癌细胞数据挖掘&#xff08;博主亲自录制视频教程&#xff09; https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare http://blog.csdn.net/huyoo/article/…

学习关于时间在sql里面的对比,用if语句(这个有点特别)

需求&#xff1a;假如当前时间是大于starttime且小于endtime的则按starttime倒序排列显示一条记录&#xff1b;假如当前时间小于starttime且小于endtime则按starttime正序排列显示一条记录。要在一次sql查询中完成。 这个是论坛里面的&#xff0c;人家给出答案了&#xff0c;我…

超简单的JNI——NDK开发教程

不好意思各位&#xff0c;我按照网上一些教程进行JNI开发&#xff0c;折腾了半天也没成功&#xff0c;最后自己瞎搞搞定了&#xff0c;其实超简单的&#xff0c;网上的教程应该过时了&#xff0c;最新版的AS就包含了NDK编译的功能&#xff0c;完全不用手动javah&#xff0c;各种…