SEO技术

SEO技术

Products

当前位置:首页 > SEO技术 >

哪里可以免费下载珠海建设公司网站源码?

96SEO 2026-02-20 08:09 0


联系qq184480602加我进群大家一起学习一起进步一起对抗互联网寒冬

哪里可以免费下载珠海建设公司网站源码?

上一篇我们通过编写MyBatis的转换器最终完成枚举在DAO层和数据库之间的转换

现在让我们把目光往前移思考一下如何编写SpringMVC的转换器完成前端与Controller层的枚举转换。

环境准备

pom.xml小册使用的版本都是2.3.4但今天遇到坑了后面会提到

?xml

xmlnshttp://maven.apache.org/POM/4.0.0

xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0

https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.4.RELEASE/versionrelativePath/

!--

--/parentgroupIdcom.example/groupIdartifactIdspringboot_enum/artifactIdversion0.0.1-SNAPSHOT/versionnamespringboot_enum/namedescriptionDemo

project

Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/projectPOJO

Data

{log.info(userDTO.toString());}PostMapping(/postForm)public

void

{log.info(userDTO.toString());}PostMapping(/postJson)public

void

{log.info(userDTO.toString());}}

关于GET与POST

GET和POST有个很大区别是GET请求的参数放在请求行而POST请求的参数放在请求体(Body)。

另外POST请求又细分很多种

form-datax-www-form-urlencodedjson

如果你足够细心平时使用Postman时就会注意到以上三种POST请求形式虽不同但参数都在Body

需要注意的是从后端接口参数的格式看POST请求中的表单提交方式和GET请求是很相似的

GET与POST表单POST

枚举名称(name)分别叫STUDENTTEACHER之前分析过所有的枚举类默认继承Enum而Enum重写了toString()

所以当我们打印STUDENT或TEACHER对象时最终会打印:

UserTypeEnum有两个字段type和desc抽象父类Enum也有两个字段ordinal序号从0开始和name枚举名称

请求反序列化

测试请求时我们的关注点是前端传入userType:STUDENT后端是如何变成UserTypeEnum对象的。

测试GET与POST表单

很明显前端传STUDENT、TEACHER等枚举名称(name)时SpringMVC能自动帮我们转为对应的枚举对象而在实际打印时由于调用了toString()所以显示userTypeSTUDENT。

那么为什么枚举名称name为什么会自动转为枚举对象UserTypeEnum呢我们先不管SpringMVC怎么做到的通过断点很容易发现SpringMVC在解析STUDENT这个字符串时最终调用了Enum#valueOf()然后根据name获取枚举对象

传入Enum.ordinal转换失败

无论是GET还是POST表单传入0或1都失败了ordinal从0开始也就是说SpringMVC默认不支持根据ordinal转换

Resolved

[org.springframework.validation.BindException:

org.springframework.validation.BeanPropertyBindingResult:

errors

[typeMismatch.userDTO.userType,typeMismatch.userType,typeMismatch.com.bravo.demo.enums.UserTypeEnum,typeMismatch];

arguments

[org.springframework.context.support.DefaultMessageSourceResolvable:

codes

com.bravo.demo.enums.UserTypeEnum

for

org.springframework.core.convert.ConversionFailedException:

Failed

[com.bravo.demo.enums.UserTypeEnum]

for

java.lang.IllegalArgumentException:

enum

com.bravo.demo.enums.UserTypeEnum.1]]

也就是说对于GET/POST表单请求SpringMVC都是根据valueOf()来匹配枚举对象的。

也即是说对于GET和POST表单请求而言如果想正确的反序列化String转为Enum对象前端只能传Enum.name。

理由如上

对于前端来说他们可能更喜欢传递枚举内部的字段比如UserTypeEnum.type而不是Enum.name。

有没有办法更改SpringMVC的默认行为当前端传递userType1时把1转为UserTypeEnum的“学生”对象呢

了解GET/POST表单请求时SpringMVC默认的转换机制改写这个机制

由于我们已经知道整个请求链路的终点是调用Enum#valueOf()进行转换于是给valueOf()打上断点

省略中间的步骤根据调用链进行反推很快定位到AbstractPropertyAccessor#setPropertyValues()

这是个for循环它拿到了UserDTO的所有属性并逐个进行赋值。

比如截图的代码显示SpringMVC正在给UserDTO.userType字段赋值。

再往下走几步会看到GenericConversionService#convert()

找到converter后调用converter的convert()方法进行值转换

我们发现SpringMVC默认的枚举转换器是StringToEnumConverterFactory

它的convert()方法正好调用了Enum.valueOf()所以GET/POST表单请求时只能传Enum.name至此真相大白。

前端发起请求传递userTypeSTUDENT从Tomcat的Servlet到SpringMVC的Controller中间要经过很多类和方法SpringMVC会解析入参对象的每一个字段选取合适的ConverterFactory为其进行转换默认使用StringToEnumConverterFactory为枚举类型进行转换即调用Enum.valueOf(name)

有了上面的铺垫关于GET/POST表单请求时如何自定义枚举入参转换器已经很明确了。

/***

自定义枚举转换器直接抄StringToEnumConverterFactory**

author

StringToEnum(targetType);}private

static

StringToEnumConverterFactory默认是调用Enum.valueOf()也就是根据Enum.name匹配*

param

(source.equals(String.valueOf(enumObject.ordinal())))

{return

addFormatters(FormatterRegistry

registry)

把我们自定义的枚举转换器添加到Spring容器Spring容器会把它加入到SpringMVC的拦截链路中registry.addConverterFactory(new

特别特别注意把MyEnumConverterFactory加入调用链后jackson原本的StringToEnumConverterFactory就不起作用了此时前端传入STUDENT、TEACHER将无法成功解析。

改进自定义枚举转换器

上面这样还是无法满足我们的需求我们只是把原先默认支持Enum.name改为Enum.ordinal。

部分同学可能有疑问你刚才为什么不直接在上面的ConverterFactory中调用getType()或者getDesc()呢

getType()/getDesc不够通用项目中其他枚举可能叫getValue()/getDescription()最重要的是class

implements

Enum使用Enum限定内部元素只能使用父类Enum的方法无法直接调用getType()等方法

解决办法有两个

抽取公共的IEnum接口强制指定按哪个字段反序列化使用注解反射

IEum接口

StringToEnum(targetType);}private

static

默认项目中所有Enum都实现了IEnum那么必然有getValue()if

(source.equals(String.valueOf(enumObject.getValue())))

{return

Retention(RetentionPolicy.RUNTIME)

public

MyEnumConverterFactory主要负责第2、3步**

author

StringToEnum(targetType);}private

static

enumObject.getClass().getDeclaredFields();for

(Field

(declaredField.isAnnotationPresent(MyJsonCreator.class))

{declaredField.setAccessible(true);//

fieldValue

declaredField.get(enumObject);//

(source.equals(String.valueOf(fieldValue)))

{return

我们惊奇的发现SpringMVC默认就支持了Enum.name和Enum.ordinal的转换但对于子类UserTypeEnum的特有字段type、desc是不识别的。

GET/POST表单默认使用StringToEnumConverterFactory只支持Enum.namePOST

很明显POST

JSON和GET/POST表单使用的不是同一个转换器并且从上面的异常信息可以捕捉到一丝丝信息

Resolved

[org.springframework.http.converter.HttpMessageNotReadableException:

JSON

com.bravo.demo.enums.UserTypeEnum

from

com.fasterxml.jackson.databind.exc.InvalidFormatException:

Cannot

com.bravo.demo.enums.UserTypeEnum

from

com.bravo.demo.pojo.UserDTO[userType])]

jacksonSpringBoot内置fastjson阿里gson谷歌

SpringBoot默认使用jackson作为JSON转换工具比如我们经常会用的ObjectMapper其实就是jackson的。

JSON请求RequestBody响应ResponseBody

GET或POST表单请求由于参数并不是JSON形式所以用不到jackson只需要实现ConverterFactory

而POST

JSON请求则需要实现HttpMessageConverterjackson已经提供

请注意ConverterFactory和HttpMessageConverter两个接口的包路径都不一样并没有什么关联。

SpringMVC如何处理JSON请求

由于JSON请求本质是字符串所以必须要有反序列化的过程。

SpringMVC对外提供了HttpMessageConverter接口用于处理JSON而SpringBoot内置的jackson提供了该接口的实现类MappingJackson2HttpMessageConverter

当一个JSON请求达到SpringMVC容器会根据为当前请求参数挑选合适的Converter

此时就轮到jackson的MappingJackson2HttpMessageConverter出场了。

如果你跟着debug就会发现实际上大部分工作都是AbstractJackson2HttpMessageConverter干的jackson的主要贡献是提供了ObjectMapper实例及各种Serializer、Deserializer用于序列化和反序列化

AbstractJackson2HttpMessageConverter内部的ObjectMapper被赋值后通过构造器如果有请求到达SpringMVC它会调用ObjectMapperSerializer、Deserializer对参数进行转换。

比如EnumDeserializer默认支持转换Enum.name、Enum.ordinal

具体的源码就不在这里带大家跟读了我们会在Spring章节分析RequestBody时解释目前大家可以像下面截图一样打上断点然后用Postman分别传递数字(Enum.ordinal)或字符串(Enum.name)体会一下

你会发现jackson的EnumDeserializer默认的解析策略是

如果你刚好是从上一篇文章过来的就会发现jackson的策略和MyBatis很像都支持了Enum.name和Enum.ordinal的转换。

那么如果前端传递的是UserTypeEnum.type或者UserTypeEnum.desc呢

JsonCreator自定义反序列化字段

好在jackson还提供了JsonCreator注解让我们自己指定反序列化的字段

Slf4j

用JsonValue指定序列化字段后面再介绍不用管*/JsonValueprivate

final

UserDTO.class);System.out.println(userDTO);//

响应序列化String

objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(userDTO);System.out.println(returnJson);}

name:

看起来很完美啊但是你用Postman去请求就会报错。

我调试了一晚上坑爹结果发现是当前SpringBoot版本问题2.3.3SpringBoot2.0.x是可以的估计2.3.3修改了jackson的默认设置

响应序列化

介绍完前端如何传入枚举参数入最后讲讲枚举如何响应给前端出。

其实答案已经呼之欲出JsonValue但方案不止一种。

在测试前请大家修改Controller让接口返回UserDto

Slf4j

{log.info(userDTO.toString());return

userDTO;}PostMapping(/postForm)public

UserDTO

{log.info(userDTO.toString());return

userDTO;}PostMapping(/postJson)public

UserDTO

{log.info(userDTO.toString());return

userDTO;}}

把之前请求相关的配置先注释掉并把SpringBoot版本改为2.0.5

OK我们自定义MyEnumConverterFacotry注释后对于GET/POST表单请求重新使用默认的StringToEnumConverterFactory仅支持Enum.name反序列化。

而POST

JSON请求默认支持Enum.name和Enum.ordinal。

现在你可以认为代码都回到了最初创建SpringBoot项目的状态。

由于这回是测试响应形式我们不关心入参所以统一传递大家都支持的Enum.name。

JSON请求它们只是请求方式不同而响应形式其实都是JSON因为我们使用了RestController

Controller

至于使用了ResponseBody后SpringMVC如何处理返回值由于篇幅已经太长留到Spring部分再聊。

但有一点可以肯定正如JSON请求那样JSON响应也会经过jackson的处理而且必然调用HttpMessageConverter的write()。

中间复杂的调用就跳过了直接看AbstractJackson2HttpMessageConverter#writeInternal()

即最终会调用objectWriter.writeValue(generator,

value)进行序列化写入response缓冲区。

我们注意到在调用writeValue()之前userType字段还是个UserTypeEnum对象

STUDENT这和SpringMVC本身没什么关系取决于JSON转换工具怎么设计的而jackson默认就是调用Enum.name()。

方案1JsonValue

在需要序列化的字段上加JsonValue即可。

特别注意对于POST

JSON请求使用JsonValue必须配合使用JsonCreator否则会报错很难受

做了上面的设置相当于告诉jackson序列化响应时调用对象的toString()即可相应地我们要重写toString()

/***

自定义JSON响应时枚举字段的序列化行为调用toString()**

return*/

Jackson2ObjectMapperBuilderCustomizer

customizer()

builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);

Getter

没想到吧UserTypeEnum序列化后竟然是完全无关的文字~;}

测试GET响应

SpringMVC对请求和响应的处理原本就复杂再加上枚举使得整篇文章难度加大不少。

很多同学可能有点晕这里总结一下并尝试给出我推荐的方案。

POST

JSONJsonCreatorGET/POSTMyJsonCreator

/***

JSON请求反序列字段请用jackson原生注解JsonCreator**

author

Retention(RetentionPolicy.RUNTIME)

public

MyEnumConvertFactoryMyJsonCreator指定GET/POST表单请求根据哪个字段反序列化*/MyJsonCreatorprivate

final

addFormatters(FormatterRegistry

registry)

把我们自定义的枚举转换器添加到Spring容器Spring容器会把它加入到SpringMVC的拦截链路中registry.addConverterFactory(new

MyEnumConverterFactory());}/***

自定义JSON响应时枚举字段的序列化行为调用toString()**

return*/Beanpublic

Jackson2ObjectMapperBuilderCustomizer

customizer()

builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);}}

/***

MyEnumConverterFactory主要负责第2、3步**

author

StringToEnum(targetType);}private

static

enumObject.getClass().getDeclaredFields();for

(Field

(declaredField.isAnnotationPresent(MyJsonCreator.class))

{declaredField.setAccessible(true);//

fieldValue

declaredField.get(enumObject);//

(source.equals(String.valueOf(fieldValue)))

{return

我个人实际开发时无论是Controller层还是DAO层都习惯手动转换枚举。

作者简介大家好我是smart哥前中兴通讯、美团架构师现某互联网公司CTO



SEO优化服务概述

作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。

百度官方合作伙伴 白帽SEO技术 数据驱动优化 效果长期稳定

SEO优化核心服务

网站技术SEO

  • 网站结构优化 - 提升网站爬虫可访问性
  • 页面速度优化 - 缩短加载时间,提高用户体验
  • 移动端适配 - 确保移动设备友好性
  • HTTPS安全协议 - 提升网站安全性与信任度
  • 结构化数据标记 - 增强搜索结果显示效果

内容优化服务

  • 关键词研究与布局 - 精准定位目标关键词
  • 高质量内容创作 - 原创、专业、有价值的内容
  • Meta标签优化 - 提升点击率和相关性
  • 内容更新策略 - 保持网站内容新鲜度
  • 多媒体内容优化 - 图片、视频SEO优化

外链建设策略

  • 高质量外链获取 - 权威网站链接建设
  • 品牌提及监控 - 追踪品牌在线曝光
  • 行业目录提交 - 提升网站基础权威
  • 社交媒体整合 - 增强内容传播力
  • 链接质量分析 - 避免低质量链接风险

SEO服务方案对比

服务项目 基础套餐 标准套餐 高级定制
关键词优化数量 10-20个核心词 30-50个核心词+长尾词 80-150个全方位覆盖
内容优化 基础页面优化 全站内容优化+每月5篇原创 个性化内容策略+每月15篇原创
技术SEO 基本技术检查 全面技术优化+移动适配 深度技术重构+性能优化
外链建设 每月5-10条 每月20-30条高质量外链 每月50+条多渠道外链
数据报告 月度基础报告 双周详细报告+分析 每周深度报告+策略调整
效果保障 3-6个月见效 2-4个月见效 1-3个月快速见效

SEO优化实施流程

我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:

1

网站诊断分析

全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。

2

关键词策略制定

基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。

3

技术优化实施

解决网站技术问题,优化网站结构,提升页面速度和移动端体验。

4

内容优化建设

创作高质量原创内容,优化现有页面,建立内容更新机制。

5

外链建设推广

获取高质量外部链接,建立品牌在线影响力,提升网站权威度。

6

数据监控调整

持续监控排名、流量和转化数据,根据效果调整优化策略。

SEO优化常见问题

SEO优化一般需要多长时间才能看到效果?
SEO是一个渐进的过程,通常需要3-6个月才能看到明显效果。具体时间取决于网站现状、竞争程度和优化强度。我们的标准套餐一般在2-4个月内开始显现效果,高级定制方案可能在1-3个月内就能看到初步成果。
你们使用白帽SEO技术还是黑帽技术?
我们始终坚持使用白帽SEO技术,遵循搜索引擎的官方指南。我们的优化策略注重长期效果和可持续性,绝不使用任何可能导致网站被惩罚的违规手段。作为百度官方合作伙伴,我们承诺提供安全、合规的SEO服务。
SEO优化后效果能持续多久?
通过我们的白帽SEO策略获得的排名和流量具有长期稳定性。一旦网站达到理想排名,只需适当的维护和更新,效果可以持续数年。我们提供优化后维护服务,确保您的网站长期保持竞争优势。
你们提供SEO优化效果保障吗?
我们提供基于数据的SEO效果承诺。根据服务套餐不同,我们承诺在约定时间内将核心关键词优化到指定排名位置,或实现约定的自然流量增长目标。所有承诺都会在服务合同中明确约定,并提供详细的KPI衡量标准。

SEO优化效果数据

基于我们服务的客户数据统计,平均优化效果如下:

+85%
自然搜索流量提升
+120%
关键词排名数量
+60%
网站转化率提升
3-6月
平均见效周期

行业案例 - 制造业

  • 优化前:日均自然流量120,核心词无排名
  • 优化6个月后:日均自然流量950,15个核心词首页排名
  • 效果提升:流量增长692%,询盘量增加320%

行业案例 - 电商

  • 优化前:月均自然订单50单,转化率1.2%
  • 优化4个月后:月均自然订单210单,转化率2.8%
  • 效果提升:订单增长320%,转化率提升133%

行业案例 - 教育

  • 优化前:月均咨询量35个,主要依赖付费广告
  • 优化5个月后:月均咨询量180个,自然流量占比65%
  • 效果提升:咨询量增长414%,营销成本降低57%

为什么选择我们的SEO服务

专业团队

  • 10年以上SEO经验专家带队
  • 百度、Google认证工程师
  • 内容创作、技术开发、数据分析多领域团队
  • 持续培训保持技术领先

数据驱动

  • 自主研发SEO分析工具
  • 实时排名监控系统
  • 竞争对手深度分析
  • 效果可视化报告

透明合作

  • 清晰的服务内容和价格
  • 定期进展汇报和沟通
  • 效果数据实时可查
  • 灵活的合同条款

我们的SEO服务理念

我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。

提交需求或反馈

Demand feedback