首页 > 编程语言 >AspNetCore8.0实战

AspNetCore8.0实战

时间:2024-03-15 17:12:42浏览次数:33  
标签:实战 string builder AspNetCore8.0 static var new public

前言

想变优秀的第N天。
学习张老师的Blog.Core。

1.创建Asp.NetCore API

1.1创建项目

启用OpenAPI:sawgger

不适用顶级语句:使用main函数

使用控制器:controller

image-20240304230411871

1.2配置说明

iisSettings:iis配置。

http:kestrl启动配置。

IIS Express:iis启动配置。

image-20240306215443679

2.仓储+服务

创建以下公共类库和API,他们分别是:

Peng.Net8:WebApi。

Peng.Net8.Common:公共帮助类。

Peng.Net8.Model:实体层。

Peng.Net8.Repository:仓储层。

Peng.Net8.IService:服务接口层。

Peng.Net8.Service:服务层。

image-20240306221548968

3.泛型基类

3.1仓储基类

IBaseRepository:需要对传入TEntity(实体模型)进行数据操作。

image-20240306222307554

BaseRepository:实现IBaseRepository。

image-20240306222446728

3.2服务基类

IBaseServices:对传入的TEntity(实体模型)进行操作,但是不能返回TEntity,需要返回TVo(视图模型),不能将实体字段暴露给WebAPI层。

image-20240306222600955

BaseServices:实现IBaseServices。

image-20240306222754601

4.AutoMapper(对象映射)

使用AutoMapper是为了将视图模型和实体模型进行相互转换。

4.1安装

在Peng.Net8.Common层安装AutoMapper的俩个包,这里直接粘贴保存就能自动安装。

<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />

image-20240306223742861

4.2使用

将UserInfo和UserInfoVo的字段进行配置。

image-20240306225451692

AutoMapperConfig:

/// <summary>
///  静态全局 AutoMapper 配置文件
/// </summary>
public class AutoMapperConfig
{
    public static MapperConfiguration RegisterMappings()
    {
        return new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new CustomProfile());
        });
    }
}

image-20240306225253523

注入:

 builder.Services.AddAutoMapper(typeof(AutoMapperConfig));
 AutoMapperConfig.RegisterMappings();

image-20240306225204196

5.原生依赖注入

在NetCore开发中,减少new的使用,尽量使用依赖注入。(不应该使用new,比如复杂的链路、GC回收、内存泄露等)

AddSington:单例模式。

AddScope:会话模式。一个http请求。

AddTrasient:瞬时模式。

5.1仓储服务代码

仓储层:

image-20240306234227162

image-20240306234309779

服务层:

image-20240306234339781

image-20240306234359121

5.2依赖注入

注入:

image-20240306234502236

image-20240306234600341

6.自定义项目框架模版

官网地址:https://learn.microsoft.com/zh-cn/dotnet/core/tools/custom-templates

6.1template.json

{
  "$schema": "https://json.schemastore.org/template.json",
  "author": "peng", //  模板作者  必须
  "classifications": [ "Web/WebAPI" ], //必须,这个对应模板的Tags 模板特征标识。上文举例的配置是因为我自定义的模板包括了console和webapi
  "name": "Peng.Net8 Dotnet", //必须,这个对应模板的Templates 用户看到的模板名称
  "identity": "Peng.Net8.Template", //可选,模板的唯一名称
  "shortName": "PNetTpl", //必须,这个对应模板的Short Name  短名称。当使用CLI命令创建模板项目时,使用短名称将利于使用。
  "tags": {
    "language": "C#",
    "type": "project"
  },
  "sourceName": "Peng.Net8", // 可选,要替换的名字 
  "preferNameDirectory": true // 可选,添加目录
}

image-20240307222935800

6.2安装模板

安装模板 (绝对路径)

dotnet new install 绝对路径\Peng.Net8  --force

如果需要卸载重来的话,卸载模版命令

dotnet new uninstall 绝对路径\Peng.Net8

image-20240307212642337

查看命令

donet new list

image-20240307213039566

查看模版支持选项,使用的名称是template.json中的shortName

dotnet new 名称 -h

image-20240307215351802

6.3新模版创建项目

使用新模版创建项目(记得关闭vs,要不会报错The process cannot access the file ‘ ’ because it is being used by another process.)。

dotnet new 模版名称 -n 项目名称
dotnet new PNetTpl -n PengPeng.Net8
  • -n:项目名称
  • -o:生成项目路径
  • -E:/--EnableFramework 自定义命令 (生成项目模式)

image-20240307215901134

创建成功

image-20240307215925305

image-20240307223035249

VS2022创建

image-20240307223620819

6.4nuget

下载nuget.exe文件

https://www.nuget.org/downloads

image-20240307214916236

打包模板,并生成.nupkg文件

nuget.exe pack Peng.Net8/peng.net8.template.nuspec

image-20240309193010099

image-20240307221021494

发布到nuget

nuget push Peng.Net8.Template.1.0.0.nupkg -Source "你的nuget 服务 url" -ApiKey "你的nuget api key"

7.Autofac

7.1安装Autofac

<ItemGroup>
	<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
	<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />	
</ItemGroup>

image-20240307230710065

7.2注入Autofac

构造函数注入

 public class AutofacModuleRegister : Autofac.Module
    {
        /*
        1、看是哪个容器起的作用,报错是什么
        2、三步走导入autofac容器
        3、生命周期,hashcode对比,为什么controller里没变化
        4、属性注入
        */
        protected override void Load(ContainerBuilder builder)
        {
            var basePath = AppContext.BaseDirectory;

            var servicesDllFile = Path.Combine(basePath, "Peng.Net8.Service.dll");
            var repositoryDllFile = Path.Combine(basePath, "Peng.Net8.Repository.dll");

            builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)).InstancePerDependency(); //注册仓储
            builder.RegisterGeneric(typeof(BaseServices<,>)).As(typeof(IBaseServices<,>)).InstancePerDependency(); //注册服务

            // 获取 Service.dll 程序集服务,并注册
            var assemblysServices = Assembly.LoadFrom(servicesDllFile);
            builder.RegisterAssemblyTypes(assemblysServices)
                .AsImplementedInterfaces()
                .InstancePerDependency()
                .PropertiesAutowired();

            // 获取 Repository.dll 程序集服务,并注册
            var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
            builder.RegisterAssemblyTypes(assemblysRepository)
                .AsImplementedInterfaces()
                .PropertiesAutowired()
                .InstancePerDependency();
        }
    }

属性注入:

 public class AutofacPropertityModuleReg : Module
 {
     protected override void Load(ContainerBuilder builder)
     {
         var controllerBaseType = typeof(ControllerBase);
         builder.RegisterAssemblyTypes(typeof(Program).Assembly)
             .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
             .PropertiesAutowired();
     }
 }

把autofac注入到ServiceCollection

var builder = WebApplication.CreateBuilder(args);
builder.Host
   .UseServiceProviderFactory(new AutofacServiceProviderFactory())
   .ConfigureContainer<ContainerBuilder>(builder =>
   {
       builder.RegisterModule<AutofacModuleRegister>();
       builder.RegisterModule<AutofacPropertityModuleReg>();
   });

// 属性注入
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
// Add services to the container.
builder.Services.AddControllers();

7.3构造函数注入

在构造函数中赋值:

image-20240307232716650

7.4属性注入

ASP.NET Core默认不使用DI获取Controller,是因为DI容器构建完成后就不能变更了,但是Controller是可能有动态加载的需求的。

需要使用IControllerActivator开启Controller的属性注入,默认不开启。

image-20240307232750159

必须使用public修饰属性

image-20240307232855819

8.AOP(Log)

8.1安装

<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />	

image-20240309192036214

8.2动态代理实现ServiceAOP

实现IInterceptor,执行完成后打出日志

image-20240309192322499

   public class AOPLogInfo
{
    /// <summary>
    /// 请求时间
    /// </summary>
    public string RequestTime { get; set; } = string.Empty;
    /// <summary>
    /// 操作人员
    /// </summary>
    public string OpUserName { get; set; } = string.Empty;
    /// <summary>
    /// 请求方法名
    /// </summary>
    public string RequestMethodName { get; set; } = string.Empty;
    /// <summary>
    /// 请求参数名
    /// </summary>
    public string RequestParamsName { get; set; } = string.Empty;
    /// <summary>
    /// 请求参数数据JSON
    /// </summary>
    public string RequestParamsData { get; set; } = string.Empty;
    /// <summary>
    /// 请求响应间隔时间
    /// </summary>
    public string ResponseIntervalTime { get; set; } = string.Empty;
    /// <summary>
    /// 响应时间
    /// </summary>
    public string ResponseTime { get; set; } = string.Empty;
    /// <summary>
    /// 响应结果
    /// </summary>
    public string ResponseJsonData { get; set; } = string.Empty;
}
     
   /// <summary>
    /// 拦截器AOP 继承IInterceptor接口
    /// </summary>
    public class ServiceAOP : IInterceptor
    {
        /// <summary>
        /// 实例化IInterceptor唯一方法 
        /// </summary>
        /// <param name="invocation">包含被拦截方法的信息</param>
        public void Intercept(IInvocation invocation)
        {
            string json;
            try
            {
                json = JsonConvert.SerializeObject(invocation.Arguments);
            }
            catch (Exception ex)
            {
                json = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString();
            }

            DateTime startTime = DateTime.Now;
            AOPLogInfo apiLogAopInfo = new AOPLogInfo
            {
                RequestTime = startTime.ToString("yyyy-MM-dd hh:mm:ss fff"),
                OpUserName = "",
                RequestMethodName = invocation.Method.Name,
                RequestParamsName = string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()),
                ResponseJsonData = json
            };

            try
            {
                //在被拦截的方法执行完毕后 继续执行当前方法,注意是被拦截的是异步的
                invocation.Proceed();


                // 异步获取异常,先执行
                if (IsAsyncMethod(invocation.Method))
                {

                    //Wait task execution and modify return value
                    if (invocation.Method.ReturnType == typeof(Task))
                    {
                        invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(
                            (Task)invocation.ReturnValue,
                            async () => await SuccessAction(invocation, apiLogAopInfo, startTime), /*成功时执行*/
                            ex =>
                            {
                                LogEx(ex, apiLogAopInfo);
                            });
                    }
                    //Task<TResult>
                    else
                    {
                        invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
                            invocation.Method.ReturnType.GenericTypeArguments[0],
                            invocation.ReturnValue,
                            async (o) => await SuccessAction(invocation, apiLogAopInfo, startTime, o), /*成功时执行*/
                            ex =>
                            {
                                LogEx(ex, apiLogAopInfo);
                            });
                    }

                }
                else
                {
                    // 同步1
                    string jsonResult;
                    try
                    {
                        jsonResult = JsonConvert.SerializeObject(invocation.ReturnValue);
                    }
                    catch (Exception ex)
                    {
                        jsonResult = "无法序列化,可能是兰姆达表达式等原因造成,按照框架优化代码" + ex.ToString();
                    }

                    DateTime endTime = DateTime.Now;
                    string ResponseTime = (endTime - startTime).Milliseconds.ToString();
                    apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff");
                    apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms";
                    apiLogAopInfo.ResponseJsonData = jsonResult;
                    Console.WriteLine(JsonConvert.SerializeObject(apiLogAopInfo));
                }
            }
            catch (Exception ex)
            {
                LogEx(ex, apiLogAopInfo);
                throw;
            }
        }

        private async Task SuccessAction(IInvocation invocation, AOPLogInfo apiLogAopInfo, DateTime startTime, object o = null)
        {
            DateTime endTime = DateTime.Now;
            string ResponseTime = (endTime - startTime).Milliseconds.ToString();
            apiLogAopInfo.ResponseTime = endTime.ToString("yyyy-MM-dd hh:mm:ss fff");
            apiLogAopInfo.ResponseIntervalTime = ResponseTime + "ms";
            apiLogAopInfo.ResponseJsonData = JsonConvert.SerializeObject(o);

            await Task.Run(() =>
            {
                Console.WriteLine("执行成功-->" + JsonConvert.SerializeObject(apiLogAopInfo));
            });
        }

        private void LogEx(Exception ex, AOPLogInfo dataIntercept)
        {
            if (ex != null)
            {
                Console.WriteLine("error!!!:" + ex.Message + JsonConvert.SerializeObject(dataIntercept));
            }
        }


        public static bool IsAsyncMethod(MethodInfo method)
        {
            return
                method.ReturnType == typeof(Task) ||
                method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>);
        }
    }
    internal static class InternalAsyncHelper
    {
        public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
        {
            Exception exception = null;

            try
            {
                await actualReturnValue;
                await postAction();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<object, Task> postAction,
        Action<Exception> finalAction)
        {
            Exception exception = null;
            try
            {
                var result = await actualReturnValue;
                await postAction(result);
                return result;
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue,
             Func<object, Task> action, Action<Exception> finalAction)
        {
            return typeof(InternalAsyncHelper)
                .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static)
                .MakeGenericMethod(taskReturnType)
                .Invoke(null, new object[] { actualReturnValue, action, finalAction });
        }
    }

8.3Autofac注入

只在服务层(Service)注入日志。

image-20240309192532970

  var aopTypes = new List<Type>() { typeof(ServiceAOP) };
  builder.RegisterType<ServiceAOP>();

  builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>))
      .InstancePerDependency(); //注册仓储
  builder.RegisterGeneric(typeof(BaseServices<,>)).As(typeof(IBaseServices<,>))
      .EnableInterfaceInterceptors()
      .InterceptedBy(aopTypes.ToArray())
      .InstancePerDependency(); //注册服务

  // 获取 Service.dll 程序集服务,并注册
  var assemblysServices = Assembly.LoadFrom(servicesDllFile);
  builder.RegisterAssemblyTypes(assemblysServices)
      .AsImplementedInterfaces()
      .InstancePerDependency()
      .PropertiesAutowired()
      .EnableInterfaceInterceptors()
      .InterceptedBy(aopTypes.ToArray());

8.4实现

日志成功打出。

image-20240315145515808

9.Appseting单例类获取配置

AspNetCore五大接口对象:

  • ILogger:日志。
  • IServiceCollection:IOC。
  • IOptions:选项。
  • IConfiguration:配置。
  • Middleware:中间件。

弊端:硬编码,重构会有很大影响。比如大小写。

9.1安装

<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />

9.2实现AppSettings

 public class AppSettings
 {
     public static IConfiguration Configuration { get; set; }
     static string contentPath { get; set; }

     public AppSettings(string contentPath)
     {
         string Path = "appsettings.json";

         //如果你把配置文件 是 根据环境变量来分开了,可以这样写
         //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";

         Configuration = new ConfigurationBuilder()
             .SetBasePath(contentPath)
             .Add(new JsonConfigurationSource
             {
                 Path = Path,
                 Optional = false,
                 ReloadOnChange = true
             }) //这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性
             .Build();
     }

     public AppSettings(IConfiguration configuration)
     {
         Configuration = configuration;
     }

     /// <summary>
     /// 封装要操作的字符
     /// </summary>
     /// <param name="sections">节点配置</param>
     /// <returns></returns>
     public static string app(params string[] sections)
     {
         try
         {
             if (sections.Any())
             {
                 return Configuration[string.Join(":", sections)];
             }
         }
         catch (Exception)
         {
         }

         return "";
     }

     /// <summary>
     /// 递归获取配置信息数组
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="sections"></param>
     /// <returns></returns>
     public static List<T> app<T>(params string[] sections)
     {
         List<T> list = new List<T>();
         // 引用 Microsoft.Extensions.Configuration.Binder 包
         Configuration.Bind(string.Join(":", sections), list);
         return list;
     }


     /// <summary>
     /// 根据路径  configuration["App:Name"];
     /// </summary>
     /// <param name="sectionsPath"></param>
     /// <returns></returns>
     public static string GetValue(string sectionsPath)
     {
         try
         {
             return Configuration[sectionsPath];
         }
         catch (Exception)
         {
         }

         return "";
     }
 }

9.3注入

builder.Services.AddSingleton(new AppSettings(builder.Configuration));

image-20240308230257272

9.4使用

var redisEnable = AppSettings.app(new string[] { "Redis", "Enable" });
var redisConnectionString = AppSettings.GetValue("Redis:ConnectionString");
Console.WriteLine($"Enable: {redisEnable} ,  ConnectionString: {redisConnectionString}");

image-20240315145908176

配置文件:

image-20240308230555213

10.IOptions

10.1安装

<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />

10.2配置类

IConfigurableOptions是为了约束,只有继承IConfigurableOptions才被注入到ServiceCollection中。

public interface IConfigurableOptions
{
}
/// <summary>
/// Redis缓存配置选项
/// </summary>
public sealed class RedisOptions : IConfigurableOptions
{
    /// <summary>
    /// 是否启用
    /// </summary>
    public bool Enable { get; set; }

    /// <summary>
    /// Redis连接
    /// </summary>
    public string ConnectionString { get; set; }

    /// <summary>
    /// 键值前缀
    /// </summary>
    public string InstanceName { get; set; }
}

10.3实现

将实现IConfigurableOptions接口的配置类注入到ServiceCollection中。

public static class AllOptionRegister
{
    public static void AddAllOptionRegister(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));

        foreach (var optionType in typeof(ConfigurableOptions).Assembly.GetTypes().Where(s =>
                     !s.IsInterface && typeof(IConfigurableOptions).IsAssignableFrom(s)))
        {
            services.AddConfigurableOptions(optionType);
        }
    }
}
public static class ConfigurableOptions
{
    internal static IConfiguration Configuration;
    public static void ConfigureApplication(this IConfiguration configuration)
    {
        Configuration = configuration;
    }


    /// <summary>添加选项配置</summary>
    /// <typeparam name="TOptions">选项类型</typeparam>
    /// <param name="services">服务集合</param>
    /// <returns>服务集合</returns>
    public static IServiceCollection AddConfigurableOptions<TOptions>(this IServiceCollection services)
        where TOptions : class, IConfigurableOptions
    {
        Type optionsType = typeof(TOptions);
        string path = GetConfigurationPath(optionsType);
        services.Configure<TOptions>(Configuration.GetSection(path));

        return services;
    }

    public static IServiceCollection AddConfigurableOptions(this IServiceCollection services, Type type)
    {
        string path = GetConfigurationPath(type);
        var config = Configuration.GetSection(path);

        Type iOptionsChangeTokenSource = typeof(IOptionsChangeTokenSource<>);
        Type iConfigureOptions = typeof(IConfigureOptions<>);
        Type configurationChangeTokenSource = typeof(ConfigurationChangeTokenSource<>);
        Type namedConfigureFromConfigurationOptions = typeof(NamedConfigureFromConfigurationOptions<>);
        iOptionsChangeTokenSource = iOptionsChangeTokenSource.MakeGenericType(type);
        iConfigureOptions = iConfigureOptions.MakeGenericType(type);
        configurationChangeTokenSource = configurationChangeTokenSource.MakeGenericType(type);
        namedConfigureFromConfigurationOptions = namedConfigureFromConfigurationOptions.MakeGenericType(type);

        services.AddOptions();
        services.AddSingleton(iOptionsChangeTokenSource,
            Activator.CreateInstance(configurationChangeTokenSource, Options.DefaultName, config) ?? throw new InvalidOperationException());
        return services.AddSingleton(iConfigureOptions,
            Activator.CreateInstance(namedConfigureFromConfigurationOptions, Options.DefaultName, config) ?? throw new InvalidOperationException());
    }

    /// <summary>获取配置路径</summary>
    /// <param name="optionsType">选项类型</param>
    /// <returns></returns>
    public static string GetConfigurationPath(Type optionsType)
    {
        var endPath = new[] { "Option", "Options" };
        var configurationPath = optionsType.Name;
        foreach (var s in endPath)
        {
            if (configurationPath.EndsWith(s))
            {
                return configurationPath[..^s.Length];
            }
        }

        return configurationPath;
    }
}

10.4注入

这俩种方式都可以

var builder = WebApplication.CreateBuilder(args);
builder.Host
    .UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureContainer<ContainerBuilder>(builder =>
    {
        builder.RegisterModule<AutofacModuleRegister>();
        builder.RegisterModule<AutofacPropertityModuleReg>();
    })
    .ConfigureAppConfiguration((hostingContext, config) =>
    {
        hostingContext.Configuration.ConfigureApplication();
    });
    
  // 配置
//ConfigurableOptions.ConfigureApplication(builder.Configuration);
builder.Services.AddAllOptionRegister();  

image-20240308232544721

10.5使用

var redisOptions = _redisOptions.Value;

image-20240315154145877

11.非依赖注入管道中获取所有服务

11.1.安装Serilog

<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />

11.2实现

RuntimeExtension:获取项目程序集

public static class RuntimeExtension
{
    /// <summary>
    /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包
    /// </summary>
    /// <returns></returns>
    public static IList<Assembly> GetAllAssemblies()
    {
        var list = new List<Assembly>();
        var deps = DependencyContext.Default;
        //只加载项目中的程序集
        var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type == "project"); //排除所有的系统程序集、Nuget下载包
        foreach (var lib in libs)
        {
            try
            {
                var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));
                list.Add(assembly);
            }
            catch (Exception e)
            {
                Log.Debug(e, "GetAllAssemblies Exception:{ex}", e.Message);
            }
        }

        return list;
    }

    public static Assembly GetAssembly(string assemblyName)
    {
        return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName));
    }

    public static IList<Type> GetAllTypes()
    {
        var list = new List<Type>();
        foreach (var assembly in GetAllAssemblies())
        {
            var typeInfos = assembly.DefinedTypes;
            foreach (var typeInfo in typeInfos)
            {
                list.Add(typeInfo.AsType());
            }
        }

        return list;
    }

    public static IList<Type> GetTypesByAssembly(string assemblyName)
    {
        var list = new List<Type>();
        var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName));
        var typeInfos = assembly.DefinedTypes;
        foreach (var typeInfo in typeInfos)
        {
            list.Add(typeInfo.AsType());
        }

        return list;
    }

    public static Type GetImplementType(string typeName, Type baseInterfaceType)
    {
        return GetAllTypes().FirstOrDefault(t =>
        {
            if (t.Name == typeName &&
                t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name))
            {
                var typeInfo = t.GetTypeInfo();
                return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType;
            }

            return false;
        });
    }
}

InternalApp:内部只用于初始化使用,获取IServiceCollection、IServiceProvider、IConfiguration等几个重要的内部对象

public static class InternalApp
{
    internal static IServiceCollection InternalServices;

    /// <summary>根服务</summary>
    internal static IServiceProvider RootServices;

    /// <summary>获取Web主机环境</summary>
    internal static IWebHostEnvironment WebHostEnvironment;

    /// <summary>获取泛型主机环境</summary>
    internal static IHostEnvironment HostEnvironment;

    /// <summary>配置对象</summary>
    internal static IConfiguration Configuration;

    public static void ConfigureApplication(this WebApplicationBuilder wab)
    {
        HostEnvironment = wab.Environment;
        WebHostEnvironment = wab.Environment;
        InternalServices = wab.Services;
    }

    public static void ConfigureApplication(this IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public static void ConfigureApplication(this IHost app)
    {
        RootServices = app.Services;
    }
}

App:实现非注入形式获取Service、Options

public class App
{
    static App()
    {
        EffectiveTypes = Assemblies.SelectMany(GetTypes);
    }

    private static bool _isRun;

    /// <summary>是否正在运行</summary>
    public static bool IsBuild { get; set; }

    public static bool IsRun
    {
        get => _isRun;
        set => _isRun = IsBuild = value;
    }

    /// <summary>应用有效程序集</summary>
    public static readonly IEnumerable<Assembly> Assemblies = RuntimeExtension.GetAllAssemblies();

    /// <summary>有效程序集类型</summary>
    public static readonly IEnumerable<Type> EffectiveTypes;

    /// <summary>优先使用App.GetService()手动获取服务</summary>
    public static IServiceProvider RootServices => IsRun || IsBuild ? InternalApp.RootServices : null;

    /// <summary>获取Web主机环境,如,是否是开发环境,生产环境等</summary>
    public static IWebHostEnvironment WebHostEnvironment => InternalApp.WebHostEnvironment;

    /// <summary>获取泛型主机环境,如,是否是开发环境,生产环境等</summary>
    public static IHostEnvironment HostEnvironment => InternalApp.HostEnvironment;

    /// <summary>全局配置选项</summary>
    public static IConfiguration Configuration => InternalApp.Configuration;

    /// <summary>
    /// 获取请求上下文
    /// </summary>
    public static HttpContext HttpContext => RootServices?.GetService<IHttpContextAccessor>()?.HttpContext;

    //public static IUser User => GetService<IUser>();

    #region Service

    /// <summary>解析服务提供器</summary>
    /// <param name="serviceType"></param>
    /// <param name="mustBuild"></param>
    /// <param name="throwException"></param>
    /// <returns></returns>
    public static IServiceProvider GetServiceProvider(Type serviceType, bool mustBuild = false, bool throwException = true)
    {
        if (HostEnvironment == null || RootServices != null &&
            InternalApp.InternalServices
                .Where(u =>
                    u.ServiceType ==
                    (serviceType.IsGenericType ? serviceType.GetGenericTypeDefinition() : serviceType))
                .Any(u => u.Lifetime == ServiceLifetime.Singleton))
            return RootServices;

        //获取请求生存周期的服务
        if (HttpContext?.RequestServices != null)
            return HttpContext.RequestServices;

        if (RootServices != null)
        {
            IServiceScope scope = RootServices.CreateScope();
            return scope.ServiceProvider;
        }

        if (mustBuild)
        {
            if (throwException)
            {
                throw new ApplicationException("当前不可用,必须要等到 WebApplication Build后");
            }

            return default;
        }

        ServiceProvider serviceProvider = InternalApp.InternalServices.BuildServiceProvider();
        return serviceProvider;
    }

    public static TService GetService<TService>(bool mustBuild = true) where TService : class =>
        GetService(typeof(TService), null, mustBuild) as TService;

    /// <summary>获取请求生存周期的服务</summary>
    /// <typeparam name="TService"></typeparam>
    /// <param name="serviceProvider"></param>
    /// <param name="mustBuild"></param>
    /// <returns></returns>
    public static TService GetService<TService>(IServiceProvider serviceProvider, bool mustBuild = true)
        where TService : class => (serviceProvider ?? GetServiceProvider(typeof(TService), mustBuild, false))?.GetService<TService>();

    /// <summary>获取请求生存周期的服务</summary>
    /// <param name="type"></param>
    /// <param name="serviceProvider"></param>
    /// <param name="mustBuild"></param>
    /// <returns></returns>
    public static object GetService(Type type, IServiceProvider serviceProvider = null, bool mustBuild = true) =>
        (serviceProvider ?? GetServiceProvider(type, mustBuild, false))?.GetService(type);

    #endregion

    #region private

    /// <summary>加载程序集中的所有类型</summary>
    /// <param name="ass"></param>
    /// <returns></returns>
    private static IEnumerable<Type> GetTypes(Assembly ass)
    {
        Type[] source = Array.Empty<Type>();
        try
        {
            source = ass.GetTypes();
        }
        catch
        {
            Console.WriteLine($@"Error load `{ass.FullName}` assembly.");
        }

        return source.Where(u => u.IsPublic);
    }

    #endregion

    #region Options

    /// <summary>获取配置</summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <returns>TOptions</returns>
    public static TOptions GetConfig<TOptions>()
        where TOptions : class, IConfigurableOptions
    {
        TOptions instance = Configuration
            .GetSection(ConfigurableOptions.GetConfigurationPath(typeof(TOptions)))
            .Get<TOptions>();
        return instance;
    }

    /// <summary>获取选项</summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <param name="serviceProvider"></param>
    /// <returns>TOptions</returns>
    public static TOptions GetOptions<TOptions>(IServiceProvider serviceProvider = null) where TOptions : class, new()
    {
        IOptions<TOptions> service = GetService<IOptions<TOptions>>(serviceProvider ?? RootServices, false);
        return service?.Value;
    }

    /// <summary>获取选项</summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <param name="serviceProvider"></param>
    /// <returns>TOptions</returns>
    public static TOptions GetOptionsMonitor<TOptions>(IServiceProvider serviceProvider = null)
        where TOptions : class, new()
    {
        IOptionsMonitor<TOptions> service =
            GetService<IOptionsMonitor<TOptions>>(serviceProvider ?? RootServices, false);
        return service?.CurrentValue;
    }

    /// <summary>获取选项</summary>
    /// <typeparam name="TOptions">强类型选项类</typeparam>
    /// <param name="serviceProvider"></param>
    /// <returns>TOptions</returns>
    public static TOptions GetOptionsSnapshot<TOptions>(IServiceProvider serviceProvider = null)
        where TOptions : class, new()
    {
        IOptionsSnapshot<TOptions> service = GetService<IOptionsSnapshot<TOptions>>(serviceProvider, false);
        return service?.Value;
    }

    #endregion
}

IConfiguration对象从App中获取

image-20240309181519681

ApplicationSetup:通过事件获取WebApplication的状态。

public static class ApplicationSetup
{
    public static void UseApplicationSetup(this WebApplication app)
    {
        app.Lifetime.ApplicationStarted.Register(() =>
        {
            App.IsRun = true;
        });

        app.Lifetime.ApplicationStopped.Register(() =>
        {
            App.IsRun = false;

            //清除日志
            Log.CloseAndFlush();
        });
    }
}

image-20240309181931782

11.3注入

var builder = WebApplication.CreateBuilder(args);
builder.Host
   .UseServiceProviderFactory(new AutofacServiceProviderFactory())
   .ConfigureContainer<ContainerBuilder>(builder =>
   {
       builder.RegisterModule<AutofacModuleRegister>();
       builder.RegisterModule<AutofacPropertityModuleReg>();
   })
   .ConfigureAppConfiguration((hostingContext, config) =>
   {
       hostingContext.Configuration.ConfigureApplication();
   });

//配置
builder.ConfigureApplication();

image-20240309182003197

app.ConfigureApplication();
app.UseApplicationSetup();

image-20240309182040002

11.4使用

 [HttpGet(Name = "GetUserInfo")]
 public async Task<object> GetUserInfo()
 {
     var userServiceObjNew = App.GetService<IBaseServices<UserInfo, UserInfoVo>>(false);
     var redisOptions = App.GetOptions<RedisOptions>();
     await Console.Out.WriteLineAsync(JsonConvert.SerializeObject(redisOptions));
     return await userServiceObjNew.Query();
 }

image-20240315154111822

12.ControllerAsServices属性注入

IControllerActivator的默认实现不是ServiceBasedControllerActivator,而是DefaultControllerActivator。

控制器本身不是由依赖注入容器生成的,只不过是构造函数里的依赖是从容器里拿出来的,控制器不是容器生成的,所以他的属性也不是容器生成的。为了改变默认实现DefaultControllerActivator,所以使用ServiceBasedControllerActivator。

IControllerActivator源码地址:https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/Controllers/IControllerActivator.cs

IControllerActivator 就俩个方法Create和Release。

image-20240309184511885

查看DefaultControllerActivator 和ServiceBasedControllerActivator源码发现:

DefaultControllerActivator是由ITypeActivatorCache.CreateInstance创建对象。

ServiceBasedControllerActivator是由actionContext.HttpContext.RequestServices创建对象。

image-20240309184911906

通过改变Controllers的创建方式来实现属性注入,将Controller的创建都由容器容器创建。以下俩种方式都是由容器创建Controller。

image-20240309185138521

// 属性注入
//builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
// Add services to the container.
builder.Services.AddControllers().AddControllersAsServices();

Controller由容器创建完成,所以他的属性也是容器创建的,就可以实现属性注入。

属性修饰词必须是public

image-20240309185300054

13.Redis分布式缓存

13.1安装Redis包

<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0" />
<PackageReference Include="StackExchange.Redis" Version="2.7.10" />

13.2Redis实现

这里不贴代码了 框架里都有

image-20240309195841073

13.3IDistributedCache实现

IDistributedCache源码,IDistributedCache是微软官方提供的缓存接口标准。

image-20240309200548124

实现ICaching,将IDistributedCache传进去

image-20240309201729222

13.4注入

如果开启redis缓存,实现RedisCacheImpl

image-20240309204212900

如果开启内存缓存,实现MemoryDistributedCache

image-20240309204327607

image-20240309204116478

根据配置决定IDistributedCache的实现。

image-20240309204413241

注入

image-20240309204605635

13.5使用

 [HttpGet(Name = "GetUserInfo")]
 public async Task<object> GetUserInfo()
 {  
     var cacheKey = "peng";
     List<string> cacheKeys = await _caching.GetAllCacheKeysAsync();
     await Console.Out.WriteLineAsync("全部keys -->" + JsonConvert.SerializeObject(cacheKeys));

     await Console.Out.WriteLineAsync("添加一个缓存");
     await _caching.SetStringAsync(cacheKey, "pengpeng");
     await Console.Out.WriteLineAsync("全部keys -->" + JsonConvert.SerializeObject(await _caching.GetAllCacheKeysAsync()));
     await Console.Out.WriteLineAsync("当前key内容-->" + JsonConvert.SerializeObject(await _caching.GetStringAsync(cacheKey)));

     await Console.Out.WriteLineAsync("删除key");
     await _caching.RemoveAsync(cacheKey);
     await Console.Out.WriteLineAsync("全部keys -->" + JsonConvert.SerializeObject(await _caching.GetAllCacheKeysAsync()));
     return "";
 }

image-20240309204938350

14.ORM

  • 1、CURD + Page
  • 2、多表联查
  • 3、字段级别操作
  • 4、国产数据库
  • 5、事务处理
  • 6、多库操作
  • 7、多租户/数据权限
  • 8、分库分表操作
  • 9、读写分离/从库
  • 10、灾备数据库
  • 11、调试SQL与日志记录

15.SqlSuger入门

15.1SqlSuger包安装

<ItemGroup>
  <PackageReference Include="SqlSugarCore" Version="5.1.4.145" />
</ItemGroup>

image-20240309230319901

15.2SqlSuger实现

image-20240309230633371

注入:

/// <summary>
/// SqlSugar 启动服务
/// </summary>
public static class SqlsugarSetup
{
    public static void AddSqlsugarSetup(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));

        // 默认添加主数据库连接
        if (!string.IsNullOrEmpty(AppSettings.app("MainDB")))
        {
            MainDb.CurrentDbConnId = AppSettings.app("MainDB");
        }

        BaseDBConfig.MutiConnectionString.allDbs.ForEach(m =>
        {
            var config = new ConnectionConfig()
            {
                ConfigId = m.ConnId.ObjToString().ToLower(),
                ConnectionString = m.Connection,
                DbType = (DbType)m.DbType,
                IsAutoCloseConnection = true,
                MoreSettings = new ConnMoreSettings()
                {
                    IsAutoRemoveDataCache = true,
                    SqlServerCodeFirstNvarchar = true,
                },
                InitKeyType = InitKeyType.Attribute
            };
            if (SqlSugarConst.LogConfigId.ToLower().Equals(m.ConnId.ToLower()))
            {
                BaseDBConfig.LogConfig = config;
            }
            else
            {
                BaseDBConfig.ValidConfig.Add(config);
            }

            BaseDBConfig.AllConfigs.Add(config);
        });

        if (BaseDBConfig.LogConfig is null)
        {
            throw new ApplicationException("未配置Log库连接");
        }

        // SqlSugarScope是线程安全,可使用单例注入
        // 参考:https://www.donet5.com/Home/Doc?typeId=1181
        services.AddSingleton<ISqlSugarClient>(o =>
        {
            return new SqlSugarScope(BaseDBConfig.AllConfigs);
        });
    }
}
 // Sqlsugar ORM
 builder.Services.AddSqlsugarSetup();

image-20240309224007390

15.3BaseRepository

ISqlSugarClient只读,外部只可获取,不可修改

image-20240309230419868

15.4BaseServices

image-20240309230547998

15.5使用

属性注入

image-20240309230802198

16.SqlSuger事务简单用法

16.1实现IUnitOfWorkManage

实现IUnitOfWorkManage,通过依赖注入ISqlSugarClient获取实例实现事务

image-20240309232859389

16.2BeginTran

标准的开启、提交、回滚事物的写法

image-20240309233543435

16.3UnitOfWork

通过析构函数实现事务,此时不需要回滚事务。

image-20240309234039475

当Dispose时,会自动回滚事务

image-20240309234213036

17.SqlSuger事务高级用法

动态代理实现事务

image-20240310005051193

事务传播方式

image-20240310004807253

如果有一个[UseTran(Propagation = Propagation.Required)]就开启事务

image-20240310005148575

ConcurrentStack是线程安全的后进先出 (LIFO:栈) 集合。

第一个方法进来后开启事务,将方法加入队列。多个方法加入,只会在第一次开启事务。

image-20240310005445803

执行完一个事务后在执行下一个事务。

image-20240310004927010

当所有方法执行完成后,执行After。

image-20240310010004953

获取栈中第一个方法。

然后提交事务。

如果异常就回滚事务。

最后再从栈中第一个方法开始全部移除,直到删除完成。

image-20240310010355059

如果异常就直接回滚。

image-20240310010926504

移除所有方法并回滚。

image-20240310010958893

18.SqlSuger多库操作

Tenant指定数据库配置,不区分大小写。

SugarTable指定数据库表明。

image-20240310014714012

image-20240310013915191

ISqlSugarClient根据类上配置的数据库和表名获取数据库实例,从而实现多库。

建议使用GetConnectionScope,是线程安全的。

image-20240310014036916

对内_db,对外_Db,_dbBase默认主库

image-20240310014323086

19.SqlSuger分库分表

设置分表策略,这里是按月分表。image-20240310194832380

SplitField,设置分表字段,根据创建时间来分表。

image-20240310194920565

仓储和服务实现分表添加和查询。

image-20240310195126520

分表查询和添加。

image-20240310195217811

20.授权认证[Authorize]入门

1、理解[Authorize]特性

2、JWT组成和安全设计

3、Claims声明和安全设计

4、HttpContext上下文的处理

5、基于Role、Claims的授权

6、基于Requirement的复杂授权

7、分布式微服务下的统一授权

8、认证中心的设计、单点登录

9、微前端 + 微服务的门户网站设计

10、其他应用技巧(数据权限、租户等)

image-20240310200148254

在Controller加上特性[Authorize]

image-20240310203010367

访问接口时会报错,因为你选择加上认证特性,需要指定认证方案。

image-20240310203243639
包安装:

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.2" />
  </ItemGroup>

在Program加上认证方式。

// JWT
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,      //是否验证Issuer
            ValidateAudience = true,    //是否验证Audience
            ValidateLifetime = true,    //是否验证失效时间
            ValidateIssuerSigningKey = true, //是否验证SecurityKey
            ValidIssuer = "Peng.Core",  //发行人 //Issuer,这两项和前面签发jwt的设置一致
            ValidAudience = "wr",       //订阅人
            IssuerSigningKey = new    SymmetricSecurityKey(Encoding.UTF8.GetBytes("sdfsdfsrty45634kkhllghtdgdfss345t678fs"))//拿到SecurityKey
        };
    });

image-20240310203426208

21.基于源码分析Claims声明

21.1源码分析

app.UseAuthorization():开启授权

image-20240310212410094

AuthorizationMiddlewareInternal:使用授权中间件

image-20240310212457693

AuthorizationMiddleware:在授权中间件中添加策略

image-20240310212629549

AuthorizationPolicyBuilder:通过角色授权

image-20240310212742393

RolesAuthorizationRequirement:在请求上下文中获取角色信息

image-20240310212821200

21.2权限控制

只允许SuperAdmin角色访问

image-20240310213140196

21.3基于Claim和Role授权

image-20240310213616529

image-20240310213636110

21.4Claims声明产生

注入请求上下文服务

image-20240310213857396

从请求上下文获取Claims

image-20240310213927856

22.基于源码分析策略授权

22.1源码分析

AuthorizationMiddleware:授权中间件。

将所有策略添加到IList集合中一起处理。

image-20240310220529792

AuthorizationPolicyBuilder:所以自定策略需要实现IAuthorizationRequirement接口。

image-20240310220635240

IPolicyEvaluator:处理授权。

image-20240310221109010

IPolicyEvaluator:AuthenticateAsync处理策略授权。

image-20240310221654841

AuthorizationPolicyBuilder:Claim声明授权。

image-20240310221230605

AuthorizationPolicyBuilder:角色授权。

image-20240310221311562

Claim声明授权和角色授权都实现了AuthorizationHandler、IAuthorizationRequirement

image-20240310221416291

22.2自定义策略授权

看上边的源码分析实现Claim声明授权和角色授权都实现了AuthorizationHandler、IAuthorizationRequirement。

所以自定策略授权也需要实现AuthorizationHandler、IAuthorizationRequirement。

image-20240310221932618

在Program注入。

image-20240310222030736

Controller加上特性自定义策略。

image-20240310222139663

23.复杂策略授权

实现: AuthorizationHandler, IAuthorizationRequirement

image-20240310232636900

获取角色所有菜单权限。

或者当接口没有权限信息时去读取数据库进行初始化

image-20240310232719979

判断登录状态,如果登录判断token是否失效,失效就重新登录。

image-20240310233113085

判断角色权限

image-20240310233409051

获取token

image-20240310234917309

24.微服务鉴权

  • 分布式微服务下的统一鉴权
    • 第一种服务实例少,内部鉴权,基于角色。
    • 第二种服务实例多,需要管理接口,需要添加授权服务。
  • 认证中心、单点登录:
    • 和其他服务同级域名(一级域名)
    • 携带SSO,在目标域名解析SSO,实现单点登录
  • 微服务、微前端:
  • 其他应用(数据权限、租户等)

25.数据权限-字段多租户

25.1HttpContextAccessor获取用户信息

定义IUser接口并且实现

image-20240313223052746

AspNetUser从HttpContext请求获取用户信息

image-20240313223117421

IUser注入

image-20240313223217813

25.2SqlSuger实现字段多租户

全局配置User信息

image-20240313223958983

将租户字段配置为查询过滤条件

TenantId == 0,代表公共数据,所有人可见。

image-20240313224051017

SqlSugar配置数据权限

image-20240313224244568

增加一个数据权限表,所有租户的表都要继承ITenantEntity

image-20240313224701603

配置实体映射

image-20240313224750243

多租户测试

image-20240313224840308

26.数据权限-分表多租户

数据权限比菜单权限更细致。

分表多租户的表需要加上MultiTenant特性

image-20240313231850071

租户隔离方案

image-20240313231939252

获取Peng.Net8.Model命名空间下所有实体

image-20240313232654753

筛选出来有MultiTenant特性并且是表隔离的。

db.MappingTables.Add:将数据库表明中TableName换成了TableName_TenantId。image-20240313232125672

分表多租户测试

image-20240313233337552

27.数据权限-分库多租户

约定大于配置。

系统租户表。

DbType是数据库类型,这里是SqlLite。

Connection是数据库链接配置。

分表多租户数据表的数据库格式是TableName_Id(表名_租户ID)。

image-20240314212958556

增加系统租户表,增加库隔离的隔离方案枚举。

image-20240314214212543

读取系统租户表的租户配置,将租户配置的数据库链接添加到SqlSugar。

image-20240314214927452

添加多租户的业务表实体和实体视图,并配置实体映射。

image-20240314215446734

分库多租户测试

image-20240314215710844

注意需要配置的地方:

  • SysUserInfo表的登录用户的TenantId,将这个Id配置到SysTenant表的Id
  • SysTenant数据库链接,因为是SqlLite本地数据库,注意地址。

image-20240314223023274

image-20240314223221777

测试

image-20240314222940323

28.SqlSugar日志和缓存

28.1SqlSugar日志

实现SqlSugarAop,面向切面思想。

image-20240314224221945

配置到SqlSugar。

image-20240314224323970

使用登录接口,日志已经打印出来了。

image-20240315164234557

28.2SqlSugar缓存

实现SqlSugarCacheService,继承SqlSugar的ICacheService。

image-20240314225348347

开启缓存。

image-20240314225511986

BaseRepository实现QueryWithCache缓存查询。

image-20240314225633045

优化系统租户表查询,如果表没有更新会查询缓存。

image-20240314225714937

BaseServices实现QueryWithCache缓存查询。

image-20240314225811999

测试缓存查询

image-20240314225919802

需要注意的是如果手动更改数据库数据,缓存不会更新。

缓存只有代码增删改的时候才会更新缓存。

image-20240314230025687

标签:实战,string,builder,AspNetCore8.0,static,var,new,public
From: https://www.cnblogs.com/pengboke/p/18075835

相关文章

  • JVM工作原理与实战(四十三):JVM常见面试题目
    专栏导航JVM工作原理与实战RabbitMQ入门指南从零开始了解大数据目录专栏导航前言一、JVM常见面试题目1.什么是类加载器,有哪些常见的类加载器?2.什么是双亲委派机制,以及如何打破双亲委派机制?3.如何判断堆上的对象没有被引用?4.JVM中都有哪些引用类型?5.ThreadLoca......
  • 图书推荐|MySQL 8.0从入门到实战
    MySQL数据库从入门到实战,全面掌握MySQL的使用技能和解决实际问题的能力!本书简介MySQL数据库是目前全球流行的数据库之一。《MySQL8.0从入门到实战》从入门到实战,系统全面、由浅入深地介绍MySQL数据库应用的各个方面。全书分为8个部分,共18章。第1部分(第1~3章)介绍MySQL的基......
  • 性能数据Grafana数据面板实战
    一、安装Grafanahttps://grafana.com/zh-cn/grafana/?pg=graf&plcmt=hero-btn-1二、修改配置默认为3000,若被占用,就修改默认启用端口修改为中文界面3、启用服务或者在控制面板-服务中启动四、访问服务......
  • Python爬虫实战系列3:今日BBNews编程新闻采集
    一、分析页面打开今日BBNews网址https://news.bicido.com,下拉选择【编程】栏目1.1、分析请求F12打开开发者模式,然后点击Network后点击任意一个请求,Ctrl+F开启搜索,输入标题ApacheDoris2.1.0版本发布,开始搜索搜索结果显示直接返回的json格式,那就soeasy了,直接copycurl,......
  • 盘点一个Pandas实战需求的问题
    大家好,我是Python进阶者。一、前言前几天在Python最强王者交流群【wen】问了一个Pandas解决实际需求的实战问题。问题如下:请教:代码的目的为自动填充产品名字,有多个销售数据的表格,如例子,销售数据表格中的的产品名字一列为空,我把销售数据表格与产品信息表格进行根据产品IP进行合......
  • go反射实战
    文章目录demo1数据类型判断demo2打印任意类型数据demo1数据类型判断使用reflect.TypeOf()方法打印go中数据类型,可参考go官方API文档;使用格式化参数%T也能打印数据类型。packagemainimport"fmt"import"reflect"import"io"import"os"funcmain(){ T......
  • Kubernetes operator(十) kubebuilder 实战演练 之 开发多版本CronJob【更新中】
    云原生学习路线导航页(持续更新中)本文是Kubernetesoperator学习系列第十篇,本节会在前篇开发的Cronjob基础上,进行多版本Operator开发的实战本文的所有代码,都存储于github代码库:https://github.com/graham924/share-code-operator-study/tree/main/cronJob-operato......
  • RAG实战6-如何在LlamaIndex中使用自己搭建的API
    RAG实战6-如何在LlamaIndex使用自己搭建的大模型API在搭建一个大模型API服务中,我们介绍了如何使用SWIFT框架搭建一个大模型API服务。在RAG实战1-5中,我们一直使用的是本地加载大模型的方式来调用大模型,本文将介绍如何在LlamaIndex中使用自己搭建的大模型API。LlamaIndex支持部分......
  • Python实现BOA蝴蝶优化算法优化循环神经网络分类模型(LSTM分类算法)项目实战
    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取。1.项目背景蝴蝶优化算法(butterflyoptimizationalgorithm,BOA)是Arora等人于2019年提出的一种元启发式智能算法。该算法受到了蝴蝶觅食和交配行为的启发,......
  • Python实现BOA蝴蝶优化算法优化循环神经网络回归模型(LSTM回归算法)项目实战
    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取。1.项目背景蝴蝶优化算法(butterflyoptimizationalgorithm,BOA)是Arora等人于2019年提出的一种元启发式智能算法。该算法受到了蝴蝶觅食和交配行为的启发,......