上一篇 中将项目的基本骨架搭起来能正常跑通,这一篇将讲到,如何通过autofac将DbContext和model进行解耦,只用添加model,而不用在DbContext中添加DbSet。

在这里就不详细讲autofac是干什么用的了,简单说下autofac。

1.autofac可替换net core自带的DI IOC,用来扩展。

2.autofac可提供Aop,具体实现在博客园有很多示例。

3.autofac的几个生命周期用法:InstancePerDependency 每次都创建一个对象 ,SingleInstance 每次都是同一个对象,InstancePerLifetimeScope 同一个生命周期生成的对象是同一个。

  接下来,我们需要在启动项目上通过nuget安装两个Package:Autofac、Autofac.Extensions.DependencyInjection。

因为autofac是通过接口来进行注入的,因此我们需要创建对应的基层接口用来注入。在basic项目通过nuget安装Autofac.Extensions.DependencyInjection、,

  然后中添加 Dependency 文件夹来存放基层接口,添加IOC容器接口:IIocManager,代码如下:

  1. using System;
  2. using Autofac;
  3. using Autofac.Core;
  4.  
  5. namespace DemoFrame_Basic.Dependency
  6. {
  7. /// <summary>
  8. /// IOC容器接口
  9. /// </summary>
  10. public interface IIocManager
  11. {
  12. IContainer Container { get; }
  13.  
  14. bool IsRegistered(Type serviceType, ILifetimeScope scope = null);
  15. object Resolve(Type type, ILifetimeScope scope = null);
  16. T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class;
  17. T Resolve<T>(params Parameter[] parameters) where T : class;
  18. T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null);
  19. object ResolveOptional(Type serviceType, ILifetimeScope scope = null);
  20. object ResolveUnregistered(Type type, ILifetimeScope scope = null);
  21. T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class;
  22. ILifetimeScope Scope();
  23. bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance);
  24. }
  25. }

-IOC容器接口

  再添加一个数据库基础接口:IEntityBase

  1. /// <summary>
  2. /// 数据库基础接口
  3. /// </summary>
  4. public interface IEntityBase
  5. {
  6. }

-数据库基础接口

  IIocManager的实现类:IocManager

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.Loader;
  6. using Autofac;
  7. using Autofac.Core;
  8. using Autofac.Core.Lifetime;
  9. using Autofac.Extensions.DependencyInjection;
  10. using Microsoft.AspNetCore.Mvc;
  11. using Microsoft.Extensions.DependencyInjection;
  12. using Microsoft.Extensions.DependencyModel;
  13.  
  14. namespace DemoFrame_Basic.Dependency
  15. {
  16. /// <summary>
  17. /// Container manager
  18. /// </summary>
  19. public class IocManager : IIocManager
  20. {
  21. private IContainer _container;
  22.  
  23. public static IocManager Instance { get { return SingletonInstance; } }
  24. private static readonly IocManager SingletonInstance = new IocManager();
  25.  
  26. /// <summary>
  27. /// Ioc容器初始化
  28. /// </summary>
  29. /// <param name="config"></param>
  30. /// <returns></returns>
  31. public IServiceProvider Initialize(IServiceCollection services)
  32. {
  33.  
  34. //.InstancePerDependency() //每次都创建一个对象
  35. //.SingleInstance() //每次都是同一个对象
  36. //.InstancePerLifetimeScope() //同一个生命周期生成的对象是同一个
  37.  
  38. var builder = new ContainerBuilder();
  39. builder.RegisterInstance(Instance).As<IIocManager>().SingleInstance();
  40. //所有程序集 和程序集下类型
  41. var deps = DependencyContext.Default;
  42. var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包
  43. var listAllType = new List<Type>();
  44. foreach (var lib in libs)
  45. {
  46. try
  47. {
  48. var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
  49. listAllType.AddRange(assembly.GetTypes().Where(type => type != null));
  50. }
  51. catch { }
  52. }
  53.  
  54. //注册IEntityBase实现类
  55. var entityBaseType = typeof(IEntityBase);
  56. var arrEntityBaseType = listAllType.Where(t => entityBaseType.IsAssignableFrom(t) && t != entityBaseType).ToArray();
  57. builder.RegisterTypes(arrEntityBaseType)
  58. .AsImplementedInterfaces()
  59. .SingleInstance()
  60. .PropertiesAutowired();
  61.  
  62. foreach (var type in arrEntityBaseType)
  63. {
  64. if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
  65. {
  66. builder.RegisterType(type).As(type.BaseType)
  67. .SingleInstance()
  68. .PropertiesAutowired();
  69. }
  70. }
  71.  
  72. //注册controller实现类 让Controller能被找到
  73. var controller = typeof(ControllerBase);
  74. var arrcontrollerType = listAllType.Where(t => controller.IsAssignableFrom(t) && t != controller).ToArray();
  75. builder.RegisterTypes(arrcontrollerType)
  76. .AsImplementedInterfaces()
  77. .SingleInstance()
  78. .PropertiesAutowired();
  79.  
  80. foreach (var type in arrcontrollerType)
  81. {
  82. if (type.IsClass && !type.IsAbstract && !type.BaseType.IsInterface && type.BaseType != typeof(object))
  83. {
  84. builder.RegisterType(type).AsSelf();
  85. }
  86. }
  87.  
  88. builder.Populate(services);
  89. _container = builder.Build();
  90. return new AutofacServiceProvider(_container);
  91. }
  92.  
  93. /// <summary>
  94. /// Gets a container
  95. /// </summary>
  96. public virtual IContainer Container
  97. {
  98. get
  99. {
  100. return _container;
  101. }
  102. }
  103.  
  104. /// <summary>
  105. /// Resolve
  106. /// </summary>
  107. /// <typeparam name="T">Type</typeparam>
  108. /// <param name="key">key</param>
  109. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  110. /// <returns>Resolved service</returns>
  111. public virtual T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class
  112. {
  113. if (scope == null)
  114. {
  115. //no scope specified
  116. scope = Scope();
  117. }
  118. if (string.IsNullOrEmpty(key))
  119. {
  120. return scope.Resolve<T>();
  121. }
  122. return scope.ResolveKeyed<T>(key);
  123. }
  124.  
  125. /// <summary>
  126. /// Resolve
  127. /// </summary>
  128. /// <typeparam name="T">Type</typeparam>
  129. /// <param name="key">key</param>
  130. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  131. /// <returns>Resolved service</returns>
  132. public virtual T Resolve<T>(params Parameter[] parameters) where T : class
  133. {
  134. var scope = Scope();
  135. return scope.Resolve<T>(parameters);
  136. }
  137.  
  138. /// <summary>
  139. /// Resolve
  140. /// </summary>
  141. /// <param name="type">Type</param>
  142. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  143. /// <returns>Resolved service</returns>
  144. public virtual object Resolve(Type type, ILifetimeScope scope = null)
  145. {
  146. if (scope == null)
  147. {
  148. //no scope specified
  149. scope = Scope();
  150. }
  151. return scope.Resolve(type);
  152. }
  153.  
  154. /// <summary>
  155. /// Resolve all
  156. /// </summary>
  157. /// <typeparam name="T">Type</typeparam>
  158. /// <param name="key">key</param>
  159. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  160. /// <returns>Resolved services</returns>
  161. public virtual T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null)
  162. {
  163. if (scope == null)
  164. {
  165. //no scope specified
  166. scope = Scope();
  167. }
  168. if (string.IsNullOrEmpty(key))
  169. {
  170. return scope.Resolve<IEnumerable<T>>().ToArray();
  171. }
  172. return scope.ResolveKeyed<IEnumerable<T>>(key).ToArray();
  173. }
  174.  
  175. /// <summary>
  176. /// Resolve unregistered service
  177. /// </summary>
  178. /// <typeparam name="T">Type</typeparam>
  179. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  180. /// <returns>Resolved service</returns>
  181. public virtual T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class
  182. {
  183. return ResolveUnregistered(typeof(T), scope) as T;
  184. }
  185.  
  186. /// <summary>
  187. /// Resolve unregistered service
  188. /// </summary>
  189. /// <param name="type">Type</param>
  190. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  191. /// <returns>Resolved service</returns>
  192. public virtual object ResolveUnregistered(Type type, ILifetimeScope scope = null)
  193. {
  194. if (scope == null)
  195. {
  196. //no scope specified
  197. scope = Scope();
  198. }
  199. var constructors = type.GetConstructors();
  200. foreach (var constructor in constructors)
  201. {
  202. try
  203. {
  204. var parameters = constructor.GetParameters();
  205. var parameterInstances = new List<object>();
  206. foreach (var parameter in parameters)
  207. {
  208. var service = Resolve(parameter.ParameterType, scope);
  209. if (service == null) throw new Exception("Unknown dependency");
  210. parameterInstances.Add(service);
  211. }
  212. return Activator.CreateInstance(type, parameterInstances.ToArray());
  213. }
  214. catch (Exception)
  215. {
  216.  
  217. }
  218. }
  219. throw new Exception("No constructor was found that had all the dependencies satisfied.");
  220. }
  221.  
  222. /// <summary>
  223. /// Try to resolve srevice
  224. /// </summary>
  225. /// <param name="serviceType">Type</param>
  226. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  227. /// <param name="instance">Resolved service</param>
  228. /// <returns>Value indicating whether service has been successfully resolved</returns>
  229. public virtual bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance)
  230. {
  231. if (scope == null)
  232. {
  233. //no scope specified
  234. scope = Scope();
  235. }
  236. return scope.TryResolve(serviceType, out instance);
  237. }
  238.  
  239. /// <summary>
  240. /// Check whether some service is registered (can be resolved)
  241. /// </summary>
  242. /// <param name="serviceType">Type</param>
  243. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  244. /// <returns>Result</returns>
  245. public virtual bool IsRegistered(Type serviceType, ILifetimeScope scope = null)
  246. {
  247. if (scope == null)
  248. {
  249. //no scope specified
  250. scope = Scope();
  251. }
  252. return scope.IsRegistered(serviceType);
  253. }
  254.  
  255. /// <summary>
  256. /// Resolve optional
  257. /// </summary>
  258. /// <param name="serviceType">Type</param>
  259. /// <param name="scope">Scope; pass null to automatically resolve the current scope</param>
  260. /// <returns>Resolved service</returns>
  261. public virtual object ResolveOptional(Type serviceType, ILifetimeScope scope = null)
  262. {
  263. if (scope == null)
  264. {
  265. //no scope specified
  266. scope = Scope();
  267. }
  268. return scope.ResolveOptional(serviceType);
  269. }
  270.  
  271. /// <summary>
  272. /// Get current scope
  273. /// </summary>
  274. /// <returns>Scope</returns>
  275. public virtual ILifetimeScope Scope()
  276. {
  277. try
  278. {
  279. //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
  280. return Container.BeginLifetimeScope();
  281. }
  282. catch (Exception)
  283. {
  284. //we can get an exception here if RequestLifetimeScope is already disposed
  285. //for example, requested in or after "Application_EndRequest" handler
  286. //but note that usually it should never happen
  287.  
  288. //when such lifetime scope is returned, you should be sure that it'll be disposed once used (e.g. in schedule tasks)
  289. return Container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
  290. }
  291. }
  292. }
  293. }

-Container manager

  在这里添加完以后,我们需要将自带的DI容器给替换成现在使用的autofac,

  在启动项目的Startup文件中更改,最终代码如下:

  1. public IServiceProvider ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  4.  
  5. services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  6.  
  7. services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
  8.  
  9. services.AddDbContext<DemoDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection")));
  10.  
  11. return IocManager.Instance.Initialize(services);
  12. }

-ConfigureServices

  为了方便使用,在CoreMvc项目中添加DemoWeb的类来存放一些系统数据:

  1. using DemoFrame_Basic.Dependency;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.Extensions.Caching.Memory;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.Hosting;
  6. using System.Linq;
  7.  
  8. namespace DemoFrame_CoreMvc
  9. {
  10. public class DemoWeb
  11. {
  12. private static IHttpContextAccessor _httpContextAccessor;
  13.  
  14. /// <summary>
  15. /// Configure
  16. /// </summary>
  17. /// <param name="httpContextAccessor"></param>
  18. public static void Configure(IHttpContextAccessor httpContextAccessor)
  19. {
  20. _httpContextAccessor = httpContextAccessor;
  21. }
  22.  
  23. /// <summary>
  24. /// 当前请求HttpContext
  25. /// </summary>
  26. public static HttpContext HttpContext
  27. {
  28. get => _httpContextAccessor.HttpContext;
  29. set => _httpContextAccessor.HttpContext = value;
  30. }
  31.  
  32. /// <summary>
  33. /// IocManager
  34. /// </summary>
  35. public static IIocManager IocManager { get; set; }
  36.  
  37. /// <summary>
  38. /// Environment
  39. /// </summary>
  40. public static IHostingEnvironment Environment { get; set; }
  41.  
  42. /// <summary>
  43. /// Configuration
  44. /// </summary>
  45. public static IConfiguration Configuration { get; set; }
  46.  
  47. /// <summary>
  48. /// MemoryCache
  49. /// </summary>
  50. public static IMemoryCache MemoryCache { get; set; }
  51.  
  52. /// <summary>
  53. /// 获取当前请求客户端IP
  54. /// </summary>
  55. /// <returns></returns>
  56. public static string GetClientIp()
  57. {
  58. var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault()?.Split(',')[].Trim();
  59. if (string.IsNullOrEmpty(ip))
  60. {
  61. ip = HttpContext.Connection.RemoteIpAddress.ToString();
  62. }
  63. return ip;
  64. }
  65. }
  66. }

  Startup的完整代码如下:

  1. public class Startup
  2. {
  3. public Startup(IConfiguration configuration)
  4. {
  5. DemoWeb.Configuration = configuration;
  6. Configuration = configuration;
  7. }
  8.  
  9. public IConfiguration Configuration { get; }
  10.  
  11. // This method gets called by the runtime. Use this method to add services to the container.
  12. public IServiceProvider ConfigureServices(IServiceCollection services)
  13. {
  14. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  15.  
  16. services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
  17.  
  18. services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
  19.  
  20. services.AddDbContext<DemoDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SqlServerConnection")));
  21.  
  22. return IocManager.Instance.Initialize(services);
  23. }
  24.  
  25. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  26. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  27. {
  28. DemoWeb.IocManager = app.ApplicationServices.GetService<IIocManager>();
  29. DemoWeb.Environment = env;
  30. try//注意这里在本地开发允许时会重置数据库,并清空所有数据,如不需要请注释
  31. {
  32. if (env.IsDevelopment())
  33. {
  34. using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
  35. .CreateScope())
  36. {
  37. var dbContent = serviceScope.ServiceProvider.GetService<DemoDbContext>();
  38. //CheckMigrations(dbContent);
  39. var database = serviceScope.ServiceProvider.GetService<DemoDbContext>().Database;
  40. database.EnsureDeleted();
  41. database.EnsureCreated();
  42. }
  43. }
  44. }
  45. catch (Exception ex)
  46. {
  47. //LogHelper.Logger.Error(ex, "Failed to migrate or seed database");
  48. }
  49. DemoWeb.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
  50. if (env.IsDevelopment())
  51. {
  52. app.UseDeveloperExceptionPage();
  53. }
  54. else
  55. {
  56. app.UseHsts();
  57. }
  58. app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials());//允许跨域
  59. app.UseHttpsRedirection();
  60. app.UseMvc();
  61. }
  62.  
  63. }

-Startup

  在这么多的配置都完成了的情况下,我们该去实现mode与DbContext的解耦操作了。那么该如何做呢?

  废话不多说了,直接上代码,在数据库上下文DemoDbContext中将之前的DbSet删掉,更改如下:

  !!!先将之前的model都继承 IEntityBase 接口。这样在模型生成时才能生成到数据库!!!

  1. /// <summary>
  2. /// 数据库上下文
  3. /// </summary>
  4. public class DemoDbContext : DbContext
  5. {
  6. public DemoDbContext(DbContextOptions<DemoDbContext> options)
  7. : base(options)
  8. { }
  9.  
  10. #region IOC得到所有实体
  11. private readonly IEntityBase[] _entitys = DemoWeb.IocManager.ResolveAll<IEntityBase>();
  12. #endregion
  13.  
  14. protected override void OnModelCreating(ModelBuilder modelBuilder)
  15. {
  16. if (_entitys == null)
  17. {
  18. return;
  19. }
  20. foreach (var entity in _entitys)
  21. {
  22. modelBuilder.Model.AddEntityType(entity.GetType());
  23. }
  24. }
  25. }

-数据库上下文

  在使用数据库上下文是,使用Set<T>方法设置需要使用的的类

  在下一篇中将介绍如何使用基类controller来统一前后端交互数据,统一使用一个模型进行返回。

  有需要源码的可通过此 GitHub链接拉取 觉得还可以的给个 start 哦,谢谢!

从零开始搭建前后端分离的NetCore(EF Core CodeFirst+Au)+Vue的项目框架之二autofac解耦的更多相关文章

  1. 【手摸手,带你搭建前后端分离商城系统】02 VUE-CLI 脚手架生成基本项目,axios配置请求、解决跨域问题

    [手摸手,带你搭建前后端分离商城系统]02 VUE-CLI 脚手架生成基本项目,axios配置请求.解决跨域问题. 回顾一下上一节我们学习到的内容.已经将一个 usm_admin 后台用户 表的基本增 ...

  2. Z从壹开始前后端分离【 .NET Core2.0/3.0 +Vue2.0 】框架之二 || 后端项目搭建

    本文梯子 前言 1..net core 框架性能测试 2..net core 执行过程 3.中间件执行过程 4.AOP切面 5.整体框架结构与数据库表UML 一.创建第一个Core 1.SDK 安装 ...

  3. 从零开始搭建前后端分离的NetCore2.2(EF Core CodeFirst+Autofac)+Vue的项目框架之四Nlog记录日志至数据库

    为什么要进行日志记录呢?为什么要存至数据库呢?只能说日志记录是每个系统都应当有的. 好的日志记录方式可以提供我们足够多定位问题的依据.查找系统或软件或项目的错误或异常记录.程序在运行时就像一个机器人, ...

  4. 利用grunt-contrib-connect和grunt-connect-proxy搭建前后端分离的开发环境

    前后端分离这个词一点都不新鲜,完全的前后端分离在岗位协作方面,前端不写任何后台,后台不写任何页面,双方通过接口传递数据完成软件的各个功能实现.此种情况下,前后端的项目都独立开发和独立部署,在开发期间有 ...

  5. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  6. 【手摸手,带你搭建前后端分离商城系统】01 搭建基本代码框架、生成一个基本API

    [手摸手,带你搭建前后端分离商城系统]01 搭建基本代码框架.生成一个基本API 通过本教程的学习,将带你从零搭建一个商城系统. 当然,这个商城涵盖了很多流行的知识点和技术核心 我可以学习到什么? S ...

  7. 【手摸手,带你搭建前后端分离商城系统】03 整合Spring Security token 实现方案,完成主业务登录

    [手摸手,带你搭建前后端分离商城系统]03 整合Spring Security token 实现方案,完成主业务登录 上节里面,我们已经将基本的前端 VUE + Element UI 整合到了一起.并 ...

  8. Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之七 || API项目整体搭建 6.2 轻量级ORM

    本文梯子 本文3.0版本文章 前言 零.今天完成的蓝色部分 0.创建实体模型与数据库 1.实体模型 2.创建数据库 一.在 IRepository 层设计接口 二.在 Repository 层实现相应 ...

  9. Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储+服务+抽象接口模式

    本文梯子 本文3.0版本文章 前言 零.完成图中的粉色部分 2019-08-30:关于仓储的相关话题 一.创建实体Model数据层 二.设计仓储接口与其实现类 三.设计服务接口与其实现类 四.创建 C ...

随机推荐

  1. R035---偷个懒,用UiPath录制电脑操作过程,迅速实现流程自动化

    ​一.缘起 UiPath可以录制你操作电脑的过程,从而实现自动化. 目前有点鸡肋,因为有些操作过程无法录制,例如: 键盘快捷键 修改键 右键点击 鼠标悬停 即便如此,录制功能有时候还是可以用一下,特别 ...

  2. go 格式化 int,位数不够0补齐

    n := 32 sInt := fmt.Sprintf("%07d", n)

  3. Excel催化剂开源第46波-按行列排列多个图形技术要点

    此篇对应功能出自:第10波-快速排列工作表图形对象 - 简书 https://www.jianshu.com/p/eab71f2969a6 在Excel的对象模型中,列的宽度不是一般所期待的和行高一样 ...

  4. [PTA] 数据结构与算法题目集 6-7 在一个数组中实现两个堆栈

    //如果堆栈已满,Push函数必须输出"Stack Full"并且返回false:如果某堆栈是空的,则Pop函数必须输出"Stack Tag Empty"(其中 ...

  5. MySql(Windows)

    百度云:链接:http://pan.baidu.com/s/1nvlSzMh      密码:o1cw 官网下载网址:http://dev.mysql.com/downloads/mysql/

  6. CentOS 下配置JDK

    从官网上下载jdk到系统中,并解压好 tar –axvf jdk.tr.gz 1. PATH环境变量.作用是指定命令搜索路径,在shell下面执行命令时,它会到PATH变量所指定的路径中查找看是否能找 ...

  7. 基于 HTML5 Canvas 的可交互旋钮组件

    前言 此次的 Demo 效果如下: Demo 链接:https://hightopo.com/demo/comp-knob/ 整体思路 组件参数 绘制旋钮 绘制刻度 绘制指针 绘制标尺 绘制文本 1. ...

  8. 基于SpringBoot从零构建博客网站 - 新增创建、修改、删除专栏功能

    守望博客是支持创建专栏的功能,即可以将一系列相关的文章归档到专栏中,方便用户管理和查阅文章.这里主要讲解专栏的创建.修改和删除功能,至于专栏还涉及其它的功能,例如关注专栏等后续会穿插着介绍. 1.创建 ...

  9. webgl图库研究(包括BabylonJS、Threejs、LayaboxJS、SceneJS、ThingJS等框架的特性、适用范围、支持格式、优缺点、相关网址)

    3D图库框架范围与示例 摘要: 为实现企业80%以上的生产数据进行智能转化,在烟草.造纸.能源.电力.机床.化肥等行业,赢得领袖企业青睐,助力企业构建AI赋能中心,实现智能化转型升级.“远舢文龙数据处 ...

  10. 移动端开发用touch事件还是click事件

    前端开发现在包含了跨浏览器,跨平台(不同操作系统)和跨设备(不同尺寸的设备)开发. 在移动开发的过程中,到底选取touch事件还是click事件?对了,请不要鄙视click,click在移动端开发用着 ...