annotation-driven 配置详解

article/2025/10/14 13:58:42

一、前沿

在 Spring MVC 的项目中,我们经常使用 <mvc:annotation-driven> 这个配置,那么这个配置到底是做什么的呢?下面来分析一下,首先找到 mvc 的命名空间的定义,如下图:

从上述图中可知,annotation-driven 配置的实现类应该是定义在了 MvcNamespaceHandler 类中,是不是这样的呢?下面我们看下 MvcNamespaceHandler 源码

二、MvcNamespaceHandler

MvcNamespaceHandler 源码如下:

/*** {@link NamespaceHandler} for Spring MVC configuration namespace.** @author Keith Donald* @author Jeremy Grelle* @author Sebastien Deleuze* @since 3.0*/
public class MvcNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {// 注册 annotation-driven 标签的解析器 AnnotationDrivenBeanDefinitionParser,用来解析 ControllerregisterBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());// 注册 default-servlet-handler 标签的解析器 DefaultServletHandlerBeanDefinitionParser,用来解析静态资源文件(html、jsp、js和css等)registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());// 注册 interceptors 标签的解析器 InterceptorsBeanDefinitionParser,用来解析拦截器registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());// 注册 resources 标签的解析器 ResourcesBeanDefinitionParser,用来解析资源文件registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());// 注册 view-controller、redirect-view-controller、status-controller 标签的解析器 ViewControllerBeanDefinitionParser,用来解析视图viewregisterBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());// 注册 view-resolvers 标签的解析器 ViewResolversBeanDefinitionParser,用来解析视图解析器registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());// 注册 freemarker-configurer 标签的解析器 FreeMarkerConfigurerBeanDefinitionParser,用来解析 freemarker 配置registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());// 注册 groovy-configurer 标签的解析器 GroovyMarkupConfigurerBeanDefinitionParser,用来解析 groovy 配置registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());// 注册 script-template-configurer 标签的解析器 ScriptTemplateConfigurerBeanDefinitionParser,用来解析 script 脚本模板配置registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());// 注册 cors 标签的解析器 CorsBeanDefinitionParser,用来解析跨域registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());}}

从上述源码中可知 annotation-driven 配置对应的解析类 AnnotationDrivenBeanDefinitionParser 确实是定义在 MvcNamespaceHandler 的 init 方法中,那么 annotation-driven 配置实现的具体业务肯定在 AnnotationDrivenBeanDefinitionParser 类中,下面我们分析一下 AnnotationDrivenBeanDefinitionParser 源码

三、AnnotationDrivenBeanDefinitionParser

AnnotationDrivenBeanDefinitionParser 的 parse 方法实现了具体的解析逻辑,源码如下:

/*** A {@link BeanDefinitionParser} that provides the configuration for the* {@code <annotation-driven/>} MVC namespace element.** <p>This class registers the following {@link HandlerMapping HandlerMappings}:</p>* <ul>* <li>{@link RequestMappingHandlerMapping}* 将请求映射到带注解的控制器方法时,按照0排序,例如 @RequestMapping("/test")* ordered at 0 for mapping requests to annotated controller methods.* <li>{@link BeanNameUrlHandlerMapping}* 将URL路径映射到控制器bean名称时,按照2排序* ordered at 2 to map URL paths to controller bean names.* </ul>** <p><strong>Note:</strong> Additional HandlerMappings may be registered* as a result of using the {@code <view-controller>} or the* {@code <resources>} MVC namespace elements.** <p>This class registers the following {@link HandlerAdapter HandlerAdapters}:* <ul>* <li>{@link RequestMappingHandlerAdapter}* 处理带有@controller注解的适配器(支持自定义的参数和返回值),对应于 RequestMappingHandlerMapping,例如 @RequestMapping("/test")* for processing requests with annotated controller methods.* <li>{@link HttpRequestHandlerAdapter}* 处理带有 HttpRequestHandlers 的适配器* for processing requests with {@link HttpRequestHandler HttpRequestHandlers}.* <li>{@link SimpleControllerHandlerAdapter}* 处理基于 Controllers 接口的适配器* for processing requests with interface-based {@link Controller Controllers}.* </ul>** 异常处理器* <p>This class registers the following {@link HandlerExceptionResolver HandlerExceptionResolvers}:* <ul>* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated* with {@link org.springframework.web.bind.annotation.ResponseStatus}.* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring* exception types* </ul>** <p>This class registers an {@link org.springframework.util.AntPathMatcher}* and a {@link org.springframework.web.util.UrlPathHelper} to be used by:* <ul>* <li>the {@link RequestMappingHandlerMapping},* <li>the {@link HandlerMapping} for ViewControllers* <li>and the {@link HandlerMapping} for serving resources* </ul>* Note that those beans can be configured by using the {@code path-matching}* MVC namespace element.** <p>Both the {@link RequestMappingHandlerAdapter} and the* {@link ExceptionHandlerExceptionResolver} are configured with instances of* the following by default:* <ul>* <li>A {@link ContentNegotiationManager}* <li>A {@link DefaultFormattingConversionService}* <li>A {@link org.springframework.validation.beanvalidation.LocalValidatorFactoryBean}* if a JSR-303 implementation is available on the classpath* <li>A range of {@link HttpMessageConverter HttpMessageConverters} depending on which third-party* libraries are available on the classpath.* </ul>** @author Keith Donald* @author Juergen Hoeller* @author Arjen Poutsma* @author Rossen Stoyanchev* @author Brian Clozel* @author Agim Emruli* @since 3.0*/
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {@Override@Nullablepublic BeanDefinition parse(Element element, ParserContext context) {Object source = context.extractSource(element);XmlReaderContext readerContext = context.getReaderContext();CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);context.pushContainingComponent(compDefinition);RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);// 定义 RequestMappingHandlerMappingRootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);handlerMappingDef.setSource(source);handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 排序值为 0handlerMappingDef.getPropertyValues().add("order", 0);handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);if (element.hasAttribute("enable-matrix-variables")) {Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);}configurePathMatchingProperties(handlerMappingDef, element, context);// 注册 RequestMappingHandlerMapping 类型对应的 RootBeanDefinitionreaderContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);RuntimeBeanReference conversionService = getConversionService(element, source, context);RuntimeBeanReference validator = getValidator(element, source, context);RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);bindingDef.setSource(source);bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);bindingDef.getPropertyValues().add("conversionService", conversionService);bindingDef.getPropertyValues().add("validator", validator);bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);ManagedList<?> messageConverters = getMessageConverters(element, source, context);ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);String asyncTimeout = getAsyncTimeout(element);RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, context);ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, context);// 定义 RequestMappingHandlerAdapterRootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);handlerAdapterDef.setSource(source);handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);// 消息转换器handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);addRequestBodyAdvice(handlerAdapterDef);addResponseBodyAdvice(handlerAdapterDef);if (element.hasAttribute("ignore-default-model-on-redirect")) {Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);}if (argumentResolvers != null) {handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);}if (returnValueHandlers != null) {handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);}if (asyncTimeout != null) {handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);}if (asyncExecutor != null) {handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);}handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);// 注册 RequestMappingHandlerAdapter 对应的 RootBeanDefinitionreaderContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);RootBeanDefinition uriContributorDef =new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);uriContributorDef.setSource(source);uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);csInterceptorDef.setSource(source);csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);mappedInterceptorDef.setSource(source);mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);// 定义 ExceptionHandlerExceptionResolver 异常处理器RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);methodExceptionResolver.setSource(source);methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);// 排序值为 0methodExceptionResolver.getPropertyValues().add("order", 0);addResponseBodyAdvice(methodExceptionResolver);if (argumentResolvers != null) {methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);}if (returnValueHandlers != null) {methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);}String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);// 定义 ResponseStatusExceptionResolver 返回状态异常解析器RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);statusExceptionResolver.setSource(source);statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 排序值为 1statusExceptionResolver.getPropertyValues().add("order", 1);String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);// 定义Spring中已知的异常解析器RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);defaultExceptionResolver.setSource(source);defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);// 排序值为 2defaultExceptionResolver.getPropertyValues().add("order", 2);String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);// 将上面所有的 bean 组件注册到上下文中context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));context.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"// 注册默认的一些组件,例如 BeanNameUrlHandlerMapping、HttpRequestHandlerAdapter、// SimpleControllerHandlerAdapter 和 HandlerMappingIntrospectorMvcNamespaceUtils.registerDefaultComponents(context, source);context.popAndRegisterContainingComponent();return null;}
}

从上述源码中可以看出,在 AnnotationDrivenBeanDefinitionParser 中主要实现了注册了很多 bean 的功能,其中我们最关心的就是 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 两个类,功能分别如图:

RequestMappingHandlerMapping HandlerMapping 的实现类,它处理的是 @RequestMapping 注解,并将其注册到映射表中,即 url 对应的 Controller 的 方法

RequestMappingHandlerAdapter HandlerAdapter 的实现类,它是处理请求的适配器,就是确定调用哪个 Controller 的 哪个方法

到这里,AnnotationDrivenBeanDefinitionParser 的源码就分析完了,其中没有什么太多复杂的逻辑,代码注释都写清楚了

四、annotation-driven 作用

通过阅读 AnnotationDrivenBeanDefinitionParser 的源码可知,annotation-driven 配置的作用如下:

1)、主要是注册了 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter bean,存储了请求url到Controller方法的映射关系

2)、注册了异常处理器

五、总结

Spring MVC 中如果配置了 <mvc:annotation-driven> ,则所有的 Controller 就会被解析,所以相应的 .do 请求就会被 Controller 处理,因此这个配置至关重要。当请求没有匹配到处理类(其中包括没有配置 <mvc:annotation-driven> 或者 访问的是静态资源文件)时,就会去找  <mvc:default-servlet-handler> (处理静态资源文件)配置的 DefaultServletHttpRequestHandler 默认处理器处理了


http://chatgpt.dhexx.cn/article/IO8blaC3.shtml

相关文章

深入JAVA注解(Annotation):自定义注解

一、基础知识&#xff1a;元注解 要深入学习注解&#xff0c;我们就必须能定义自己的注解&#xff0c;并使用注解&#xff0c;在定义自己的注解之前&#xff0c;我们就必须要了解Java为我们提供的元注解和相关定义注解的语法。 元注解&#xff1a; 元注解的作用就是负责注解其…

Java:annotation注解的简单理解和总结

Java annotation 注解Annotation1、Annotation的概述1.1、定义1.2、Annotation作用分类1.3、Annotation 架构 2、Annotation的语法形式3、Annotation的分类3.1、基本注解3.2、元注解3.2.1、Target3.2.2、Retention3.2.3、Documented3.2.4、Inherited: 4、自定义annotation4 .1、…

java注解的jar包_Java 注解 Annotation

目录: 从 JDK5 开始,Java 增加了对元数据(MetaData)的支持,也就是 Annotation。Annotation 其实就是代码里面的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。…

Java Annotation Processing 概述

文章目录 JAVA ANNOTATION PROCESSING引言代码实现annotation-processor实现一个 Processor创建 AbstractProcessor 的子类 app 测试Reference JAVA ANNOTATION PROCESSING 引言 源码级别的注解处理最开始出现于 JAVA 5 中, 它提供了一种在编译期生成额外 “源文件” 的机制.…

annotation matlab,matlab 关于annotation函数的一点用法

这几天做毕设&#xff0c;用到了matlab的一些功能&#xff0c;先吐槽一下matlab的官方api写的是真的不好&#xff0c;很多东西不解释清楚。 首先对于所有的annotation函数&#xff0c;里面不论是维度还是大小参数&#xff0c;都是归一化到[0,1]之间的&#xff0c;也就是说&…

Android AnnotationProcessor

Android AnnotationProcessor 一.项目结构二.定义注解三.实现注解处理器(一)依赖(二)注解处理器(三)处理注解 四.使用注解处理器(一)依赖(二)使用注解(三)生成的代码 五.注意事项 注解处理器通常可以用在模块间解藕、自动生成代码等地方&#xff0c;比如router路由或者butterkn…

Annotation Processor

annotationProcessor和android-apt的功能是一样的&#xff0c;它们是替代关系。annotationProcessor是APT工具中的一种&#xff0c;他是google开发的内置框架&#xff0c;不需要引入&#xff0c;可以直接在build.gradle文件中使用。android-apt是由一位开发者自己开发的apt框架…

annotation是什么,用处,举例

1.概念&#xff1a;注解Annotation是java 1.5的新特性&#xff0c;是一种能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解&#xff0c;可用来将信息元数据与程序元素进行关联。Annotation 中文常译为“注解”。 2.用处&#xff1a; (1)生成文档。这…

Web大学生网页作业成品:个人博客主页 (纯HTML+CSS代码)

Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 个人博客网站 | 个人主页介绍 | 个人简介 | 个人博客设计制作 | 等网站的设计与制作 | 大学生个人HTML网页设计作品 | HTML期末大学生网页设计作业 HTML&#xff1a;结构 CSS&…

vue搭建博客

使用vue2.0/3.0搭建博客&#xff0c;前端情绪员 实现原理安装nodejs和vue写项目项目打包和托管项目创建github仓库安装git托管项目到github仓库检查仓库并设置pages购买域名和解析域名修改更新项目ps(gitee搭建vue博客) 实现原理 使用vue打包出来的dist&#xff0c;把dist中的…

博客中动态图的制作

在博客中的动态图片的制作&#xff0c;可以在Android Studio里面连上手机进行视频的录制。之后用在视频的基础上用gifcam软件转化成gif动态图&#xff1a; 下载gitcam绿色版软件进行视频的处理&#xff1a; 打开GIFcam转化成gif格式的动态图&#xff1a;

手把手,从零开始搭建个人博客网站(附源码)

项目介绍 一、项目功能&#xff1a; 实现登录功能 密码数据加密 用户管理功能&#xff08;用户增删改查&#xff09; 文章管理功能&#xff08;文章增删改查&#xff09; 文章首页展示 文章评论功能&#xff08;需登录&#xff09; 首页文章展示、 用户管理、文章管理的…

HTML5+CSS编写个人博客界面

刚入门html和css&#xff0c;花了一天入门基础&#xff0c;第二天花了五六个小时完成老师布置的作业——个人博客界面&#xff0c;初学者可以进行参考编写。 注&#xff1a;里面的链接都不能点击 废话少说&#xff0c;看结果&#xff1a; 参考代码&#xff1a; microbloy.htm…

js个人博客设计大作业

视频演示&#xff1a; 大作业演示 图片看效果&#xff1a; 网站规划设计 1.结构设计 共设计3个HTML界面&#xff1a;一个登陆界面&#xff0c;一个注册界面&#xff0c;以及主页面&#xff1b; 2.内容规划 登陆界面包含logo&#xff0c;输入用户名&#xff0c;输入密码和登…

python数据可视化:使用dash给博客制作一个dashboard

项目部署在&#xff1a;https://ffzs-blog-dashboard.herokuapp.com/ 项目代码在&#xff1a;https://github.com/ffzs/dash_blog_dashboard 1.dashboard 仪表板通常提供与特定目标或业务流程相关的关键绩效指标&#xff08;KPI&#xff09;的概览。另一方面&#xff0c;“仪表…

四、登录注册页功能实现《iVX低代码/无代码个人博客制作》

注&#xff1a;iVX也有免费直播课《第八期直播课》 首先打开在线编辑器进入我们的项目&#xff1a;https://editor.ivx.cn/ 一、登录页功能实现 上一节中已经完成了登录页的页面制作&#xff0c;那么这一节就开始对应的完成登录页的功能实现。 登录页的功能实现主要是对用户…

七、文章管理页面及功能实现《iVX低代码/无代码个人博客制作》

注&#xff1a;iVX也有免费直播课《第八期直播课》 一、文章管理页页面制作 文章管理页的基本结构与首页类似&#xff0c;我们复制一个首页&#xff0c;并且重命名首页的名称为文章管理页&#xff1a; 我们接着删除如下图所框选部分内容&#xff1a; 接着重命名导航为内容…

五、文章详情页制作及跳转功能实现《iVX低代码/无代码个人博客制作》

注&#xff1a;iVX也有免费直播课《第八期直播课》 一、详情页制作 在之前的章节中&#xff0c;我们已经制作完毕了登录、注册、首页等内容&#xff0c;在这一节中&#xff0c;我们编写详情页以及详情页功能制作。 详情页页面如下&#xff1a; 详情页头部也就是一个头部栏&…

三、登录页制作《iVX低代码/无代码个人博客制作》

注&#xff1a;iVX也有免费直播课《第八期直播课》 一、登录页实现 本节需要做的登录页如下&#xff1a; 该页面我们复习可以的值&#xff0c;首先设置整个页面页面的垂直和水平对其为居中&#xff0c;随后一个容器包裹对应的登录区域&#xff0c;此时我们创建一个页面命名为…

一、首页、详情页、文章编辑页制作《iVX低代码/无代码个人博客制作》

注&#xff1a;iVX也有免费直播课《第八期直播课》 一、首页制作 首页预览如下&#xff1a; 首先在博客页创建一个相对应项目&#xff1a; 接着选择前台&#xff0c;创建一个页面&#xff0c;命名为首页&#xff1a; 接着更改当前屏幕为小屏尺寸&#xff1a; 接着我们分…