【C】使用AutoMapper-看这篇就够了

【C】使用AutoMapper-看这篇就够了1 基本配置 Profile 配置 publicclassO Profile publicOrgani CreateMap Foo FooDto 使用 varconfigura newMapperCon cfg gt cfg CreateMap Foo Bar cfg AddProfile lt Foo Bar Foo FooDto

前言
本文基于发稿时:

  • Automapper的最新版本:10.1.1。此扩展用于非ASP.NET Core程序。
  • AutoMapper.Extensions.Microsoft.DependencyInjection的最新版本:8.1.1。此扩展用于ASP.NET Core程序

浏览的时候比较重要内容我在标题前加了“★”, 可以重点关注。

0. 快速开始

首先,配置映射关系

public class OrganizationProfile : Profile { 
    public OrganizationProfile() { 
    CreateMap<Foo, FooDto>(); } } 

如果是控制台应用,则:

var configuration = new MapperConfiguration(cfg => { 
    //cfg.CreateMap 
   
     (); 
    cfg.AddProfile<OrganizationProfile>();//或者cfg.AddProfile(new OrganizationProfile()); }); var mapper=configuration.CreateMapper();//或者var mapper=new Mapper(configuration); var dest=mapper.Map<OrderDto>(order); 

如果ASP.NET Core应用,则(需安装AutoMapper.Extensions.Microsoft.DependencyInjection):

//1. ConfigureServices里Add,入参类型为params services.AddAutoMapper(typeof(OrganizationProfile)); //2. 然后在Controller里使用即可: public XXXController(IMapper mapper) { 
    _mapper = mapper; var dest=mapper.Map<OrderDto>(order); } 

1.常见配置

1.1 Profile的配置

除了上述的手动添加profile,还可以自动扫描profile并添加:

//方法1 var configuration = new MapperConfiguration(cfg => cfg.AddMaps(myAssembly)); //方法2:通过程序集名 var configuration = new MapperConfiguration(cfg => cfg.AddMaps(new [] { 
    "Foo.UI", "Foo.Core" }); ); //方法3:通过typeof var configuration = new MapperConfiguration(cfg => cfg.AddMaps(new [] { 
    typeof(HomeController), typeof(Entity) }); ); 

1.2 兼容不同的命名方式 camelCase/PascalCase等

作用:驼峰命名与Pascal命名的兼容。

以下全局配置会映射property_name到PropertyName

var configuration = new MapperConfiguration(cfg => { 
    cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention(); }); 

或者针对某个profile进行配置(这种方式全局通用):

public class OrganizationProfile : Profile { 
    public OrganizationProfile() { 
    SourceMemberNamingConvention = new LowerUnderscoreNamingConvention(); DestinationMemberNamingConvention = new PascalCaseNamingConvention(); //Put your CreateMap... Etc.. here } } 

1.3 映射时针对某些字符进行替换

var configuration = new MapperConfiguration(c => { 
    c.ReplaceMemberName("Ä", "A"); c.ReplaceMemberName("í", "i"); c.ReplaceMemberName("Airlina", "Airline"); }); 

进行以上配置之后,会自动将Äbc映射到Abc上,将íng映射到ing上,将AirlinaMark映射到AirlineMark上。

1.4 映射时匹配前缀或后缀

var configuration = new MapperConfiguration(cfg => { 
    cfg.RecognizePrefixes("frm"); //cfg.RecongizePostfixes("后缀"); cfg.CreateMap<Source, Dest>(); }); 

这样frmValue就可以map到Value上。

Automapper默认匹配了Get前缀,如果不需要可以清除:

cfg.ClearPrefixes();//清除所有前缀 

★1.5控制哪些属性和字段能够被映射

使用ShouldMapFieldShouldMapProperty

cfg.ShouldMapField = fi => false; cfg.ShouldMapProperty = pi =>pi.GetMethod != null && (pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate); 

默认所有public的field和property都会被map,也会map private 的setter,但是不会map整个property都是internal/private的属性。

1.6 提前编译

默认是调用的时候才编译映射,但是可以要求AutoMapper提前编译,但是可能会花费点时间:

var configuration = new MapperConfiguration(cfg => { 
   }); configuration.CompileMappings(); 

2. Projection(映射)

AutoMapper只会映射扁平的类,所以嵌套的类,继承的类,需要进行手动配置。成员名称不一致时,也要手动配置映射。

AutoMapper默认会自动映射以下类型,并且映射时会先清空dest对应成员的数据:

  • IEnumerable
  • IEnumerable
  • ICollection
  • ICollection
  • IList
  • IList
  • List
  • Arrays

这几个集合之间可以相互映射,如:mapper.Map>(sources);

★2.1 手动控制某些成员的映射 ForMember+MapFrom

// Configure AutoMapper var configuration = new MapperConfiguration(cfg => cfg.CreateMap<CalendarEvent, CalendarEventForm>() .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date)) .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour)) .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute))); 

将src的Date.Date映射到dest的EventDate成员上

★2.2 嵌套(Nested)类和继承类映射

某些成员可能是一个类,那么这个类也要配置映射。同理一个类的父类也要配置映射。

var config = new MapperConfiguration(cfg => { 
    cfg.CreateMap<OuterSource, OuterDest>(); cfg.CreateMap<InnerSource, InnerDest>(); }); 

★2.3 映射继承与多态 Include/IncludeBase

假如ChildSource继承ParentSource,ChildDestination继承ParentDestination。并且有这么一个业务,ParentSource src=new ChildSource()需要把src转为ParentDestination

直接转的话肯定会有member丢失,所以要进行如下配置:

var configuration = new MapperConfiguration(c=> { 
    c.CreateMap<ParentSource, ParentDestination>() .Include<ChildSource, ChildDestination>(); c.CreateMap<ChildSource, ChildDestination>(); }); 

或者也可以这么写:

CreateMap<ParentSource,ParentDestination>(); CreateMap<ChildSource,ChildDestination>().IncludeBase<ParentSource,ParentDestination>(); 

如果有几十个类都继承了ParentSource和ParentDestination,那么上述两种方法就太啰嗦了,可以这么写:

CreateMap<ParentSource,ParentDestination>().IncludeAllDerived(); CreaetMap<ChildSource,ChildDestination>(); 

更复杂的用法参考:Mapping-inheritance

2.4 构造函数映射

如果dest构造函数的入参名和src的某个member一致,则不用手动配置,automapper会自动支持:

 public int Value { 
    get; set; } } public class SourceDto { 
    public SourceDto(int value) { 
    _value = value; } private int _value; public int Value { 
    get { 
    return _value; } } } 

如果这里构造的入参不叫value而叫value2,则要进行如下配置:

var configuration = new MapperConfiguration(cfg => cfg.CreateMap<Source, SourceDto>() .ForCtorParam("value2", opt => opt.MapFrom(src => src.Value)) ); 

2.5 复杂类映射成扁平类

public class Src { 
    public Customer Customer { 
   get;set;} public int GetTotal() { 
    return 0; } } public class Customer { 
    public string Name { 
   get;set;} } public class Dest { 
    public string CustomerName { 
   get;set;} public int Total { 
   get;set;} } 

则src可以自动映射成dest,包括CustomerNameTotal字段。这种与手动配置cfg.CreateMap

().ForMember(d=>d.CustomerName,opt=>opt.MapFrom(src=>src.Customer.Name))
然后进行映射的方式类似。

映射时AutoMapper发现,src里没有CustomerName这个成员,则会将dest的CustomerName按照PascalCase的命名方式拆分为独立的单词。所以CustomerName会映射到src的Customer.Name。如果想禁用这种自动映射,则调用cfg.DestinationMemberNamingConvention = new ExactMatchNamingConvention();使用精确映射。

如果感觉AutoMapper的这种基于PascalCase命名拆分的自动映射没法满足你的需要,则还可以手动指定某些成员的映射:

class Source { 
    public string Name { 
    get; set; } public InnerSource InnerSource { 
    get; set; } public OtherInnerSource OtherInnerSource { 
    get; set; } } class InnerSource { 
    public string Name { 
    get; set; } public string Description { 
    get; set; } } class OtherInnerSource { 
    public string Name { 
    get; set; } public string Description { 
    get; set; } public string Title { 
    get; set; } } class Destination { 
    public string Name { 
    get; set; } public string Description { 
    get; set; } public string Title { 
    get; set; } } cfg.CreateMap<Source, Destination>().IncludeMembers(s=>s.InnerSource, s=>s.OtherInnerSource); cfg.CreateMap<InnerSource, Destination>(MemberList.None); cfg.CreateMap<OtherInnerSource, Destination>(); var source = new Source { 
    Name = "name", InnerSource = new InnerSource{ 
    Description = "description" }, OtherInnerSource = new OtherInnerSource{ 
    Title = "title",Description="descpripiton2" } }; var destination = mapper.Map<Destination>(source); destination.Name.ShouldBe("name"); destination.Description.ShouldBe("description"); destination.Title.ShouldBe("title"); 

IncludeMembers参数的顺序很重要,这也就是dest的Description为“description”而不是“description2”的原因,因为InnerSourceDescription属性最先匹配到了DestinationDescription属性。

IncludeMembers相当于把子类打平添加到了src里,并进行映射。

★2.6 映射反转(Reverse Mapping)

reverse mapping一般在CreateMap方法或者ForMember等方法之后,相当于src和dest根据你自己的配置可以相互映射,少写一行代码:

cfg.CreateMap<Order, OrderDto>().ReverseMap(); //等同于以下两句 cfg.CreateMap<Order,OrderDto>(); cfg.CreateMap<OrderDto,Order>(); 

如果还想对reverse map进行自定义(大多数情况下都不需要),则可以使用ForPath:

cfg.CreateMap<Order, OrderDto>() .ForMember(d => d.CustomerName, opt => opt.MapFrom(src => src.Customer.Name)) .ReverseMap() .ForPath(s => s.Customer.Name, opt => opt.MapFrom(src => src.CustomerName)); 

注意:
如果reverse之前定义了一些诸如ForMember之类的约束,这些约束是不会自动reverse的,需要手动配置。以下代码配置了不管从Order映射到OrderDto还是从OrderDto映射到Order,都忽略CustomerName属性。

cfg.CreateMap<Order, OrderDto>() .ForMember(d => d.CustomerName, opt => opt.Ignore()) .ReverseMap() .ForMember(d => d.CustomerName, opt => opt.Ignore()) 

2.7 使用特性映射

(C#称作特性,Java叫注解)

[AutoMap(typeof(Order))] public class OrderDto { 
   } 

等同于CreateMap

()
。然后配置的时候用AddMaps方法:

var configuration = new MapperConfiguration(cfg => cfg.AddMaps("MyAssembly")); var mapper = new Mapper(configuration); 

注解里还有如下参数供设置:

  • ReverseMap (bool)
  • ConstructUsingServiceLocator (bool)
  • MaxDepth (int)
  • PreserveReferences (bool)
  • DisableCtorValidation (bool)
  • IncludeAllDerived (bool)
  • TypeConverter (Type)

映射注解的更多信息参考:Attribute-mapping

2.8 动态类型Dynamic到普通类型的映射

默认就支持,不用手动CreateMap

★2.9 泛型映射

public class Source<T> { 
   } public class Destination<T> { 
   } var configuration = new MapperConfiguration(cfg => cfg.CreateMap(typeof(Source<>), typeof(Destination<>))); 

注意:CreateMap不需要传具体的T

★3. 扩展IQueryable(与EF等ORM配合使用)

需要安装nuget包:AutoMapper.EF6

这个功能存在的意义是为了解决一些orm框架返回的是IQueryable类型,使用一般的mapper.Map做转换时,会查询出来整行数据,然后再挑选出来某些字段做映射,会降低性能的问题。解决方法是使用ProjectTo

public class OrderLine { 
    public int Id { 
    get; set; } public int OrderId { 
    get; set; } public Item Item { 
    get; set; } public decimal Quantity { 
    get; set; } } public class Item { 
    public int Id { 
    get; set; } public string Name { 
    get; set; } } public class OrderLineDTO { 
    public int Id { 
    get; set; } public int OrderId { 
    get; set; } public string Item { 
    get; set; } public decimal Quantity { 
    get; set; } } var configuration = new MapperConfiguration(cfg => cfg.CreateMap<OrderLine, OrderLineDTO>() .ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name))); public List<OrderLineDTO> GetLinesForOrder(int orderId) { 
    using (var context = new orderEntities()) { 
    return context.OrderLines.Where(ol => ol.OrderId == orderId) .ProjectTo<OrderLineDTO>(configuration).ToList(); } } //这样查Item表的时候,只会select name字段。 

3.1 Expression Translation

TBD

3.2 枚举映射

大多数情况下都不需要进行此项的配置。考虑以下两个枚举:

public enum Source { 
    Default = 0, First = 1, Second = 2 } public enum Destination { 
    Default = 0, Second = 2 } 

如果想把Source.First映射到Destination.Default上,则需要安装AutoMapper.Extensions.EnumMapping nuget包,然后进行如下配置:

internal class YourProfile : Profile { 
    public YourProfile() { 
    CreateMap<Source, Destination>() .ConvertUsingEnumMapping(opt => opt // optional: .MapByValue() or MapByName(), without configuration MapByValue is used .MapValue(Source.First, Destination.Default) ) .ReverseMap(); } } 

默认情况下AutoMapper.Extensions.EnumMapping 会将源枚举里所有的数据根据枚举值枚举名称映射到目标枚举上。如果找不到且启用了EnumMappingValidation,则会抛出异常。

4.进阶

★4.1 类型转换(可针对所有的maps生效)

当从可空类型与不可空类型相互转换时,当stringint等转换时,就需要自定义类型转换。一般有以下三种方法:

void ConvertUsing(Func<TSource, TDestination> mappingFunction); void ConvertUsing(ITypeConverter<TSource, TDestination> converter); void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>; 

简单情景下使用第一种,复杂情景下自定义类去实现ITypeConverter接口。综合举例如下:

public class Source { 
    public string Value1 { 
    get; set; } public string Value2 { 
    get; set; } public string Value3 { 
    get; set; } } public class Destination { 
    public int Value1 { 
    get; set; } public DateTime Value2 { 
    get; set; } public Type Value3 { 
    get; set; } } public void Example() { 
    var configuration = new MapperConfiguration(cfg => { 
    cfg.CreateMap<string, int>().ConvertUsing(s => Convert.ToInt32(s)); cfg.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter()); cfg.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>(); cfg.CreateMap<Source, Destination>(); }); configuration.AssertConfigurationIsValid(); var source = new Source { 
    Value1 = "5", Value2 = "01/01/2000", Value3 = "AutoMapperSamples.GlobalTypeConverters.GlobalTypeConverters+Destination" }; Destination result = mapper.Map<Source, Destination>(source); result.Value3.ShouldEqual(typeof(Destination)); } public class DateTimeTypeConverter : ITypeConverter<string, DateTime> { 
    public DateTime Convert(string source, DateTime destination, ResolutionContext context) { 
    return System.Convert.ToDateTime(source); } } public class TypeTypeConverter : ITypeConverter<string, Type> { 
    public Type Convert(string source, Type destination, ResolutionContext context) { 
    return Assembly.GetExecutingAssembly().GetType(source); } } 

4.2 通过MapFrom自定义值解析(Value Resolve )

针对的是某一个map的某一个member,有3种写法:

  • MapFrom
  • MapFrom(typeof(CustomValueResolver))
  • MapFrom(new CustomResolver())
public class Source { 
    public int Value1 { 
    get; set; } public int Value2 { 
    get; set; } } public class Destination { 
    public int Total { 
    get; set; } } public class CustomResolver : IValueResolver<Source, Destination, int> { 
    public int Resolve(Source source, Destination destination, int member, ResolutionContext context) { 
    //可以添加其他逻辑 return source.Value1 + source.Value2; } } var configuration = new MapperConfiguration(cfg => cfg.CreateMap<Source, Destination>() .ForMember(dest => dest.Total, opt => opt.MapFrom<CustomResolver>())); configuration.AssertConfigurationIsValid(); var source = new Source { 
    Value1 = 5, Value2 = 7 }; var result = mapper.Map<Source, Destination>(source); result.Total.ShouldEqual(12); 

这种与一般的MapFrom(src=>src.Value1+src.Value2)区别是可以添加更加复杂的逻辑。

如果想要一个更通用的CustomResolver,不管src和dest是什么类型的都能用,则可以实现IValueResolver
接口。但是这个resolver里没法获取dest的value,如果有这种需要的话,可以实现ImemberValueResolver接口,进行更细粒度的控制。

4.3 映射时传入数据

只支持传入键值对格式的数据

cfg.CreateMap<Source, Dest>() .ForMember(dest => dest.Foo, opt => opt.MapFrom((src, dest, destMember, context) => context.Items["Foo"])); mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar"); 

4.4 条件映射 (ConditionsPreconditions

符合某些条件时才映射

var configuration = new MapperConfiguration(cfg => { 
    cfg.CreateMap<Foo,Bar>() .ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0))); }); 
var configuration = new MapperConfiguration(cfg => { 
    cfg.CreateMap<Foo,Bar>() .ForMember(dest => dest.baz, opt => { 
    opt.PreCondition(src => (src.baz >= 0)); opt.MapFrom(src => { 
    //有了Precondition,这里的操作有可能不执行,节省资源 }); }); }); 

Condition()方法会在MapFrom方法后执行,而Preconditions会在MapFrom前执行

4.5 空值替换

作用:如果src为null的话,就给dest一个默认值

var config = new MapperConfiguration(cfg => cfg.CreateMap<Source, Dest>() .ForMember(destination => destination.Value, opt => opt.NullSubstitute("Other Value"))); var source = new Source { 
    Value = null }; var mapper = config.CreateMapper(); var dest = mapper.Map<Source, Dest>(source); dest.Value.ShouldEqual("Other Value"); source.Value = "Not null"; dest = mapper.Map<Source, Dest>(source); dest.Value.ShouldEqual("Not null"); 

4.6 ValueTransformers 映射点缀(自己起的名字)

作用:在赋值的时候,我想额外在值上加点东西

var configuration = new MapperConfiguration(cfg => { 
    cfg.ValueTransformers.Add<string>(val => val + "!!!"); }); var source = new Source { 
    Value = "Hello" }; var dest = mapper.Map<Dest>(source); dest.Value.ShouldBe("Hello!!!"); 

可以应用到全局、某个Profile、某个Map或某个member。

4.7 映射前或映射后干点额外的事 MapAction

可以提前配置:

var configuration = new MapperConfiguration(cfg => { 
    cfg.CreateMap<Source, Dest>() .BeforeMap((src, dest) => src.Value = src.Value + 10) .AfterMap((src, dest) => dest.Name = "John"); }); 

也可以在map时进行配置:

int i = 10; mapper.Map<Source, Dest>(src, opt => { 
    opt.BeforeMap((src, dest) => src.Value = src.Value + i); opt.AfterMap((src, dest) => dest.Name = HttpContext.Current.Identity.Name); }); 

MapAction也可以创建全局的。

详细用法:Before-and-after-map-actions

5. AutoMapper的一些默认行为

  1. 当映射集合时,如果src的成员为null,则dest的成员会new一个默认值,而不是直接复制为null。如果真要映映射为null,则如下修改:
var configuration = new MapperConfiguration(cfg => { 
    cfg.AllowNullCollections = true; cfg.CreateMap<Source, Destination>(); }); 

这个功能可以配置成全局的、某个profile的或某个member的。

  1. CreateMap
    ,创建映射时前后书写顺序不重要。
  2. 映射集合时,会把dest的数据给清除掉,如果不想要这么做,参考github项目AutoMapper.Collection
  3. 假如某个成员的名称为NameAAA,则名为NameAAA的field,与名为NameAAA的property,与名为GetNameAAA的方法,三者之间可以自动相互映射。
  4. 类型转换:AutoMapper支持.NET Framework自带的一些基本的类型转换。所以当src的member是非string类型,dest的是string类型时,映射的时候AutoMapper会调用ToString方法。对于那些不支持的类型转换,需要自己定义Type Converter

6.常见问题

6.1 某一个功能用ITypeConverterIValueConverterIValueResolverIMemberValueResover好像都可以实现,我应该用哪个?

这几个的释义如下:

  • Type converter = Func
  • Value resolver = Func
  • Member value resolver = Func
  • Value converter = Func

这四个的使用方式都是使用ConvertUsing()方法,区别是type converter 是针对全局的,其它三个是针对某个member的。 入参出参也不一样。

6.2 什么时候需要手动调用CreateMap

()

虽然AutoMapper的官方文档,一直都写着在映射之前要先用CreateMap方法进行配置。但在实际使用过程中,我发现并不是任何时候都需要先配置才能用。

假如dest里的每一个member(属性、字段、Get方法)都能在src里找得到,则不需要额外的配置,即下属代码也可以正常运行:

class Person { 
    public string Name { 
    get; set; } public int Age { 
    get; set; } } class Person2 { 
    public string Name { 
    get; set; } public int? Age { 
    get; set; } public DateTime BirthTime { 
    get; set; } } public class NormalProfile : Profile { 
    public NormalProfile() { 
    //CreateMap 
   
     ();// 
    } } var cfg = new MapperConfiguration(c => { 
    c.AddProfile<NormalProfile>(); }); //cfg.AssertConfigurationIsValid(); var mapper = cfg.CreateMapper(); var s3 = mapper.Map<Person>(new Person2 { 
    Name = "Person2" }); 

6.3 ReverseMap到底能Reverse哪些东西?

  1. 可以Reverse像PascalCase的命名方式拆分的member,就像CreateMap可以自动处理Customer.NameCustomerName的映射一样。即:CreateMap不需要额外配置正向就能映射的,那 ReverseMap也可以自动反向映射。
  2. opt.MapForm()操作可以被reverse,如CreateMap ().ForMember(dest => dest.Name2, opt => opt.MapFrom(src => src.Name)).ReverseMap(); ,当从Person映射到Person2的时候,Name2也可以直接映射到Name上。
  3. 不支持opt.Ignore()反向映射,即CreateMap ().ForMember(dest => dest.Name, opt => opt.Ignore()).ReverseMap() 支持Person2->Person时,忽略Name属性,但是从Person->Person2时,不会忽略,如果要忽略的话,还需要再加上.ForMember(dest => dest.Name, opt => opt.Ignore())

6.4 映射时如何跳过某些成员?

  1. 使用CreateMap

    ().ForMember(dest=>dest.AAA,opt=>opt.Ignore())
    ,跳过成员AAA的映射。
  2. 使用ShouldMapField委托跳过某些字段,使用ShouldMapProperty委托跳过某些属性。
public class NormalProfile : Profile { 
    public NormalProfile() { 
    ShouldMapField = fi => false; CreateMap<Person2, Person>().ReverseMap(); } } 或者 var cfg = new MapperConfiguration(c => { 
    c.AddProfile<XXXXProfile>(); c.ShouldMapField = fi => false; }); 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204293.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午8:36
下一篇 2026年3月19日 下午8:36


相关推荐

  • 分布式系统的 CAP 定理

    分布式系统的 CAP 定理CAP定理指出,在一个分布式系统中,对于一致性、可用性、分区容错这三个特性,不可能同时满足,而是必须有所舍弃。我们设计分布式系统时,必须在三者之间(尤其是一致性和可用性之间)有所取舍和平衡。作者:王克锋出处:https://kefeng.wang/2018/07/26/distributed-cap/版权:自由转载-非商用-非衍生-保持署名,转载请标明作者和出处。1概述…

    2025年7月20日
    5
  • SpringBootTest使用MockMvc测试Controller

    SpringBootTest使用MockMvc测试Controllerimportstaticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;importstaticorg.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@RunWith(SpringRunner.class)@…

    2022年5月5日
    49
  • Java分布式锁

    Java分布式锁Java分布式锁我的理解应该叫集群锁或者跨实例锁锁的作用是在多线程情况下,控制线程同步访问变量,执行代码块、方法,例如synchronized,在单个jvm进程中,这样是奏效的。但是在分布式环境中,单个服务往往都是要部署多台实例的,在有多个jvm进程的集群里,synchronized就达不到我们的要求了。synchronized只能控制当前jvm进程中的线程,对于其它jvm进程中的线程,它无能为力。也就是说有可能一个jvm中的线程是同步执行的,在此过程中,或许会有集群里其它jvm的线程执行到

    2022年6月10日
    48
  • cas单点登录实现原理(用户登录测试用例)

    转载地址http://www.cnblogs.com/lihuidu/p/6495247.html1、基于Cookie的单点登录的回顾    基于Cookie的单点登录核心原理:   将用户名密码加密之后存于Cookie中,之后访问网站时在过滤器(filter)中校验用户权限,如果没有权限则从Cookie中取出用户名密码进行登录,让用户从某种意义上觉得只登录了一次。   该方式缺…

    2022年4月14日
    135
  • 谈谈内存映射文件[通俗易懂]

    谈谈内存映射文件[通俗易懂]http://blog.csdn.net/ithzhang/article/details/7001650内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非系统的页交换文件。将文件映射到内存中后,我们就可以在内存中操作他们了,就像他们被载入内存中一样。内存映射文件主要有三方面的用途:1:系

    2022年6月17日
    34
  • Scrapy ip代理池

    Scrapy ip代理池一、概述在众多的网站防爬措施中,有一种是根据ip的访问频率进行限制,即在某一时间段内,当某个ip的访问次数达到一定的阀值时,该ip就会被拉黑、在一段时间内禁止访问。应对的方法有两种:1.降低爬虫的爬取频率,避免IP被限制访问,缺点显而易见:会大大降低爬取的效率。2.搭建一个IP代理池,使用不同的IP轮流进行爬取。环境说明操作系统:centos7.6ip地址:192.1…

    2022年6月9日
    49

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号