Enterprise Library深入解析与灵活应用(4):创建一个自定义Exception Handler改变ELAB的异常处理机制

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

、背景与动机

微软Enterprise Library ELABException Handling Application Block)提供了一种基于策略(Policy)的异常处理方式,在不同的环境中,比如多层架构中不同的层次中,我们可以定义不同的异常处理策略。对于ELAB来说,Exception Handling Policy = Exception Type + Exception Handler(s) ,也就是说异常处理策略规定了对于某种类型的类型需要通过那些Exception Handler去处理。

从这种意义上讲,ELAB的异常处理机制是基于Exception Type的,异常的类型是我们处理异常的最小粒度。对于一个确定的异常处理策略,在不同场合抛出的同种类型的异常,都会使用相同的Exception Handler去处理。举个例子,对于一个基于SQL ServerData Access操作,对于数据库连接失败和违反数据一致性约束,都会抛出SqlException。这两种情况对于ELAB都是等效的,因为它只能根据异常的类型进行异常处理。

在很多情况下,这种基于异常类型级别的异常处理并不能解决我们实际异常处理的问题。我们往往需要粒度更细的异常处理机制——对于抛出的同一种类型的异常,根据异常对象具体的属性值进行相应的异常处理。

、从基于类型的异常处理到基于属性值异常处理

我们需要实现的目标很清楚,就是对于抛出的异常,我们可以根据某个属性的具体的值,为其指定对应的Exception Handler进行处理。由于ELAB基于异常类型的Exception Handler的分发机制,我们不能改变,我们只能采用一些变通的机制实现曲线救国,达到我们基于属性的分发Exception Handler的目的。

具体的实现方案就是创建一个特殊的Exception Handler,该Exception Handler根据异常对象某个属性的值,指定相应的Exception Handler。对于这个特殊的Exception Handler来说,他实现了基于属性值的筛选功能,我们把它命名为FilterableExceptionHandler

一般情况下,异常的类型和对应的Exception Handler通过下图的形式直接进行匹配。当FooException抛出,两个Exception HandlerExceptionHandlerAExceptionHandlerB先后被执行。

 

当引入了FilterableExceptionHandler以后,整个结构变成下面一种形式:FilterableExceptionHandler被指定到FooException,当FooException被抛出的时候,FilterableExceptionHandler被执行。而FilterableExceptionHandler本身并不执行异常处理相关的逻辑,它的工作是根据exception的某个属性值,创建相对应的ExceptionHandler(s),并使用他们来处理该异常。如下图所示,当exception.Property=Value1是,创建ExceptionHandlerAExceptionHandlerB处理异常;当exception.Property=Value2时,真正创建出来进行异常处理的是ExceptionHandlerCExceptionHandlerD

 

三、FilterableExceptionHandler的配置

接下来,我们就来创建这样一个特殊的FilterableExceptionHandler。和一般的自定义Exception Handler一样,除了定义FilterableExceptionHandler本身之外,还需要定义两个辅助的类:ExceptionHandlerDataExceptionHandlerAssembler,前者定义ExceptionHandler相关的配置信息;后者通过配置创建相应的ExceptionHandler

我们先来定于FilterableExceptionHandlerExceptionHandlerDataFilterableExceptionHandlerData。在这之前,我们来看看一个FilterableExceptionHandler配置的实例:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <configuration>
   3:  <configSections>
   4:     <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   5:  </configSections>
   6:  <exceptionHandling>
   7:         <exceptionPolicies>
   8:             <add name="Exception Policy">
   9:                 <exceptionTypes>
  10:                     <add type="Artech.CustomExceptionHandlers.FooException,Artech.CustomExceptionHandlers.Demo"
  11:                         postHandlingAction="ThrowNewException" name="Exception">
  12:                         <exceptionHandlers>
  13:                             <add type="Artech.ExceptionHandlers.FilterableExceptionHandler,Artech.ExceptionHandlers" name="Custom Handler">
  14:                                 <filters>
  15:                                     <add property="Message" value="xxx" name="filter1" typeConverter="System.ComponentModel.TypeConverter,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  16:                                         <exceptionHandlers>
  17:                                             <add exceptionMessage="Bar" exceptionMessageResourceType="" 
  18:                                 replaceExceptionType="Artech.CustomExceptionHandlers.BarException,Artech.CustomExceptionHandlers.Demo"
  19:                                 type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  20:                                 name="Replace Handler" />
  21:                                         </exceptionHandlers>
  22:                                     </add>
  23:                                     <add property="Message" value="yyy" name="filter2" typeConverter="System.ComponentModel.TypeConverter,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  24:                                         <exceptionHandlers>
  25:                                             <add exceptionMessage="Baz" exceptionMessageResourceType=""
  26:                                 replaceExceptionType="Artech.CustomExceptionHandlers.BazException,Artech.CustomExceptionHandlers.Demo"
  27:                                 type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  28:                                 name="Replace Handler" />
  29:                                         </exceptionHandlers>
  30:                                     </add>
  31:                                 </filters>
  32:                             </add>
  33:                         </exceptionHandlers>
  34:                     </add>
  35:                 </exceptionTypes>
  36:             </add>
  37:         </exceptionPolicies>
  38:     </exceptionHandling>
  39: </configuration>
  40:  

其中和FilterableExceptionHandler相关的配置集中在如下一段。整个配置的结果是这样的:<filters>中一个filter列表,定义了对异常对象属性名/属性值的筛选和符合该条件的Exception Handler列表。下面一段配置表达的场景是:对于抛出的异常(Artech.CustomExceptionHandlers.FooException,Artech.CustomExceptionHandlers.Demo),我们需要调用ReplaceHandler用一个另一个异常对其进行替换。具体的替换规则是:如何Message属性为“xxx”,则将其替换成BarException;如何Message属性为“yyy”,则替换成BazException。最终的Message分别为“Bar”“Baz”

   1: <filters>
   2:     <add property="Message" value="xxx" name="filter1" typeConverter="System.ComponentModel.TypeConverter,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
   3:         <exceptionHandlers>
   4:             <add exceptionMessage="Bar" exceptionMessageResourceType=""
   5: replaceExceptionType="Artech.CustomExceptionHandlers.BarException,Artech.CustomExceptionHandlers.Demo"
   6: type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
   7: name="Replace Handler" />
   8:         </exceptionHandlers>
   9:     </add>
  10:     <add property="Message" value="yyy" name="filter2" typeConverter="System.ComponentModel.TypeConverter,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  11:         <exceptionHandlers>
  12:             <add exceptionMessage="Baz" exceptionMessageResourceType=""
  13: replaceExceptionType="Artech.CustomExceptionHandlers.BazException,Artech.CustomExceptionHandlers.Demo"
  14: type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ReplaceHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  15: name="Replace Handler" />
  16:         </exceptionHandlers>
  17:     </add>
  18: </filters>

四、如何创建如何创建FilterableExceptionHandler

对配置有一个初步了解后,我们来定义FilterableExceptionHandlerData

   1: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
   2: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
   3: using System.Configuration;
   4: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder; 
   5: namespace Artech.ExceptionHandlers
   6: {
   7:     [Assembler(typeof(FilterableExceptionHandlerAssembler))]
   8:     public class FilterableExceptionHandlerData:ExceptionHandlerData
   9:     {
  10:         [ConfigurationProperty("filters", IsRequired = true)]
  11:         public NamedElementCollection<FilterEntry> Filters
  12:         {
  13:             get
  14:             {
  15:                 return base["filters"] as NamedElementCollection<FilterEntry>; 
  16:             }
  17:             set
  18:             {
  19:                 this["filters"] = value;
  20:             }
  21:         }
  22:     }
  23: }

FilterableExceptionHandlerData仅仅是FilterEntry的集合。我们接着来看看FilterEntry的定义:

   1: using System;
   2: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
   3: using System.Configuration;
   4: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
   5: using System.ComponentModel;
   6: namespace Artech.ExceptionHandlers
   7: {
   8:     public class FilterEntry : NamedConfigurationElement
   9:     {
  10:         [ConfigurationProperty("property", IsRequired = true)]
  11:         public string PropertyName
  12:         {
  13:             get
  14:             {
  15:                 return this["property"] as string;
  16:             }
  17:            set
  18:             {
  19:                 this["property"] = value;
  20:             }
  21:         }
  22:  
  23:         [ConfigurationProperty("value", IsRequired = true)]
  24:         public string PropertyValue
  25:         {
  26:            get
  27:             {
  28:                return this["value"] as string;
  29:             }
  30:             set
  31:             {
  32:                 this["value"] = value;
  33:             }
  34:         }
  35:         [ConfigurationProperty("typeConverter", IsRequired = false, DefaultValue = "")]
  36:         public string TypeConverterData
  37:         {
  38:             get
  39:             {
  40:                 return this["typeConverter"] as string;
  41:             }
  42:            set
  43:             {
  44:                 this["typeConverter"] = value;
  45:             }
  46:         }
  47:         public TypeConverter TypeConverter
  48:         {
  49:             get
  50:             {
  51:                 if (string.IsNullOrEmpty(this.TypeConverterData))
  52:                 {
  53:                     return new TypeConverter();
  54:                 }
  55:  
  56:                 Type typeConverterType = null;
  57:                 try
  58:                 {
  59:                     typeConverterType = Type.GetType(this.TypeConverterData);
  60:                 }
  61:                 catch (Exception ex)
  62:                 {
  63:                     throw new ConfigurationErrorsException(ex.Message);
  64:                 }
  65:                 TypeConverter typeConverter = Activator.CreateInstance(typeConverterType) as TypeConverter;
  66:                 if (typeConverter == null)
  67:                 {
  68:                     throw new ConfigurationErrorsException(string.Format("The {0} is not a valid TypeConverter.", this.TypeConverterData));
  69:                 }
  70:  
  71:                 return typeConverter;
  72:             }
  73:         }
  74:  
  75:         [ConfigurationProperty("exceptionHandlers")]
  76:         public NameTypeConfigurationElementCollection<ExceptionHandlerData, CustomHandlerData> ExceptionHandlers
  77:         {
  78:             get
  79:             {
  80:                 return (NameTypeConfigurationElementCollection<ExceptionHandlerData, CustomHandlerData>)this["exceptionHandlers"];
  81:             }
  82:         }
  83:     }
  84: }

由于我们需要根据exception的某个属性来动态指定具体的ExceptionHandler,我们定了3个必要的属性:PropertyNamePropertyValueExceptionHandlers。他们分别表示用于筛选的属性名称和属性,以及满足筛选条件所采用的Exception Handler的配置。此外还具有一个额外的属性:TypeConverter,用于类型的转化。在进行筛选比较的时候,我们通过反射得到exception某个属性(PropertyName)的值,然后和指定的值(PropertyValue)进行比较。简单起见,我们在这里进行字符串的比较,所以我们需要通过TypeConverter将通过反射得到的属性值转换成字符串。默认的TypeConverterSystem.ComponentModel.TypeConverter

接下来我们看看真正的FilterableExceptionHandler的定义:

using System;

using System.Collections.Generic;

using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

using System.Reflection;

using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;

 

namespace Artech.ExceptionHandlers

{

    [ConfigurationElementType(typeof(FilterableExceptionHandlerData))]

    public class FilterableExceptionHandler : IExceptionHandler

    {

        private FilterableExceptionHandlerData _filterableExceptionHandlerData;

 

        public FilterableExceptionHandler(FilterableExceptionHandlerData handlerData)

        {

            this._filterableExceptionHandlerData = handlerData;

        }

 

        private IList<IExceptionHandler> GetFilteredHandler(Exception exception, FilterableExceptionHandlerData handlerData)

        {

            IList<IExceptionHandler> handlers = new List<IExceptionHandler>();

            foreach (FilterEntry filterEntry in handlerData.Filters)

            {

                PropertyInfo propertyInfo = exception.GetType().GetProperty(filterEntry.PropertyName);

                if (propertyInfo == null)

                {

                    throw new ConfigurationErrorsException(

                        string.Format("The {0} does not have the {1} property.", exception.GetType().Name, filterEntry.PropertyName));

                }

                object propertyValue = propertyInfo.GetValue(exception, null);

                if (string.Compare(filterEntry.TypeConverter.ConvertToString(propertyValue), filterEntry.PropertyValue, true) == 0)

                {

                    foreach(ExceptionHandlerData exceptionHandlerData in filterEntry.ExceptionHandlers)

                    {

                        handlers.Add( ExceptionHandlerCustomFactory.Instance.Create(null,exceptionHandlerData,null,null));

                    }

                }

            }

 

            return handlers;

        }

 

        #region IExceptionHandler Members

 

        public Exception HandleException(Exception exception, Guid handlingInstanceId)

        {

            foreach (IExceptionHandler handler in this.GetFilteredHandler(exception, this._filterableExceptionHandlerData))

            {

                exception = handler.HandleException(exception,handlingInstanceId);

            }

 

            return exception;

        }

 

        #endregion

    }

}

FilterableExceptionHandler的构造函数接受一个FilterableExceptionHandlerData 参数。在GetFilteredHandler方法中,我们通过具体的Exception对象和FilterableExceptionHandlerData筛选出真正的ExceptionHandler。逻辑并不复杂:便利FilterableExceptionHandlerData 中的所有FilterEntry,通过反射得到FilterEntry指定的属性名称(PropertyName)对应的属性值;通过TypeConverter转化成字符串后和FilterEntry指定的属性值(PropertyValue)进行比较。如果两者相互匹配,得到FilterEntry所有ExceptionHandlerExceptionHandlerData,通过ExceptionHandlerCustomFactory创建对应的Exception Handler对象。最后将创建的Exception Handler对象加入目标列表。

HandleException方法中,只需要逐个执行通过GetFilteredHandler方法筛选出来的Exception Handler就可以了。

最后简单看看FilterableExceptionHandlerAssembler 的定义。

 

   1: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
   2: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
   3: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
   4: using Microsoft.Practices.ObjectBuilder2;
   5: using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
   6:  
   7: namespace Artech.ExceptionHandlers
   8: {
   9:     public class FilterableExceptionHandlerAssembler : IAssembler<IExceptionHandler, ExceptionHandlerData>
  10:     {
  11:         #region IAssembler<IExceptionHandler,ExceptionHandlerData> Members
  12:  
  13:         public IExceptionHandler Assemble(IBuilderContext context, ExceptionHandlerData objectConfiguration, 
  14:             IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
  15:         {
  16:             return new FilterableExceptionHandler(objectConfiguration as FilterableExceptionHandlerData);
  17:         }
  18:  
  19:         #endregion
  20:     }
  21: }
  22:  

五、使用、验证FilterableExceptionHandler

现在我们通过一个简单的Console Application来验证FilterableExceptionHandler是否能够按照我们希望的方式进行工作。我们使用在第三节列出的配置。为次我们我需要创建3ExceptionFooException BarException BazException

   1: using System;
   2: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
   3: using System.Runtime.Serialization; 
   4: namespace Artech.CustomExceptionHandlers
   5: {
   6:     [global::System.Serializable]
   7:     public class FooException : Exception
   8:     {
   9:         public FooException() { }
  10:         public FooException(string message) : base(message) { }
  11:         public FooException(string message, Exception inner) : base(message, inner) { }
  12:         protected FooException(SerializationInfo info,StreamingContext context)
  13:             : base(info, context) { }
  14:     } 
  15:     [global::System.Serializable]
  16:     public class BarException : Exception
  17:     {
  18:         public BarException() { }
  19:         public BarException(string message) : base(message) { }
  20:         public BarException(string message, Exception inner) : base(message, inner) { }
  21:         protected BarException(SerializationInfo info,StreamingContext context)
  22:             : base(info, context) { }
  23:     } 
  24:     [global::System.Serializable]
  25:     public class BazException : Exception
  26:     {
  27:         public BazException() { }
  28:         public BazException(string message) : base(message) { }
  29:         public BazException(string message, Exception inner) : base(message, inner) { }
  30:         protected BazException(SerializationInfo info,StreamingContext context)
  31:             : base(info, context) { }
  32:     }
  33: }  

在通过配置我们可以看到,我们希望的是对FooException 进行异常的处理,并通过Message的属性,通过ReplaceHandler将其替换成BarException BazException;为此我们写一个HandleException方法。如下所以,我们人为地抛出一个FooExceptionMessage通过参数指定。在try/catch中,通过ExceptionPolicy.HandleException方法通过 ELAB进行异常的处理。在最外层的catch中,输出最终的Exception的类型和Message。在Main方法中,两次调用HandleException方法,在参数中指定FooExceptionMessage“xxx”“yyy”)。

 

   1: using System;
   2: using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
   3: using System.Runtime.Serialization; 
   4: namespace Artech.CustomExceptionHandlers
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:        {
  10:             HandleException("xxx");
  11:             HandleException("yyy");            
  12:         } 
  13:         private static void HandleException(string message)
  14:         {
  15:             try
  16:             {
  17:                 try
  18:                 {
  19:                     throw new FooException(message);
  20:                 }
  21:                 catch (Exception ex)
  22:                 {                 
  23:                     if (ExceptionPolicy.HandleException(ex, "Exception Policy"))
  24:                     {
  25:                         throw;
  26:                     } 
  27:                 }
  28:             }
  29:             catch (Exception ex)
  30:             {
  31:                 Console.WriteLine("Exception Type: {0}; Message: {1}", ex.GetType().Name, ex.Message);
  32:             }
  33:         }
  34:     } 
  35: }

运行该程序,你将会看到如下的输出结果。可见对应抛出的同一种类型的ExceptionFooException),通过我们的FilterableExceptionHandler,根据Message属性值的不同,最终被分别替换成了BarException BazException 



作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
8天前
|
运维 数据库连接 PHP
PHP中的异常处理机制深度解析####
本文深入探讨了PHP中异常处理机制的工作原理,通过实例分析展示了如何有效地使用try-catch语句来捕获和处理运行时错误。我们将从基础概念出发,逐步深入到高级应用技巧,旨在帮助开发者更好地理解和利用这一强大的工具,以提高代码的稳定性和可维护性。 ####
|
8天前
|
PHP 开发者 UED
PHP中的异常处理机制解析####
本文深入探讨了PHP中的异常处理机制,通过实例解析try-catch语句的用法,并对比传统错误处理方式,揭示其在提升代码健壮性与可维护性方面的优势。文章还简要介绍了自定义异常类的创建及其应用场景,为开发者提供实用的技术参考。 ####
|
12天前
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
7天前
|
机器学习/深度学习 搜索推荐 API
淘宝/天猫按图搜索(拍立淘)API的深度解析与应用实践
在数字化时代,电商行业迅速发展,个性化、便捷性和高效性成为消费者新需求。淘宝/天猫推出的拍立淘API,利用图像识别技术,提供精准的购物搜索体验。本文深入探讨其原理、优势、应用场景及实现方法,助力电商技术和用户体验提升。
|
12天前
|
缓存 NoSQL Java
千万级电商线上无阻塞双buffer缓冲优化ID生成机制深度解析
【11月更文挑战第30天】在千万级电商系统中,ID生成机制是核心基础设施之一。一个高效、可靠的ID生成系统对于保障系统的稳定性和性能至关重要。本文将深入探讨一种在千万级电商线上广泛应用的ID生成机制——无阻塞双buffer缓冲优化方案。本文从概述、功能点、背景、业务点、底层原理等多个维度进行解析,并通过Java语言实现多个示例,指出各自实践的优缺点。希望给需要的同学提供一些参考。
35 7
|
11天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
16天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
45 8
|
13天前
|
编译器 PHP 开发者
PHP 8新特性解析与实战应用####
随着PHP 8的发布,这一经典编程语言迎来了诸多令人瞩目的新特性和性能优化。本文将深入探讨PHP 8中的几个关键新功能,包括命名参数、JIT编译器、新的字符串处理函数以及错误处理改进等。通过实际代码示例,展示如何在现有项目中有效利用这些新特性来提升代码的可读性、维护性和执行效率。无论你是PHP新手还是经验丰富的开发者,本文都将为你提供实用的技术洞察和最佳实践指导。 ####
25 1
|
14天前
|
存储 供应链 算法
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
39 0
|
15天前
|
存储 监控 API
深入解析微服务架构及其在现代应用中的实践
深入解析微服务架构及其在现代应用中的实践
25 0

推荐镜像

更多