spring高级篇(五)

news2024/12/23 22:20:39

1、参数解析器

        前篇提到过,参数解析器是HandlerAdapters中的组件,用于解析controller层方法中加了注解的参数信息。

        有一个controller,方法的参数加上了各种注解:

public class Controller {
    
    public void test(
            @RequestParam("name1") String name1, // name1=张三
            String name2,                        // name2=李四
            @RequestParam("age") int age,        // age=18
            @RequestParam(name = "home", defaultValue = "${JAVA_HOME}") String home1, // spring 获取数据
            @RequestParam("file") MultipartFile file, // 上传文件
            @PathVariable("id") int id,               //  /test/124   /test/{id}
            @RequestHeader("Content-Type") String header,
            @CookieValue("token") String token,
            @Value("${JAVA_HOME}") String home2, // spring 获取数据  ${} #{}
            HttpServletRequest request,          // request, response, session ...
            @ModelAttribute("abc") A21.User user1,          // name=zhang&age=18
            A21.User user2,                          // name=zhang&age=18
            @RequestBody A21.User user3              // json
    ) {
    }
}

        在测试类中定义一个方法,模拟各种参数的请求信息:

   private static HttpServletRequest mockRequest() {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setParameter("name1", "zhangsan");
        request.setParameter("name2", "lisi");
        request.addPart(new MockPart("file", "abc", "hello".getBytes(StandardCharsets.UTF_8)));
        Map<String, String> map = new AntPathMatcher().extractUriTemplateVariables("/test/{id}", "/test/123");
        System.out.println(map);
        request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
        request.setContentType("application/json");
        request.setCookies(new Cookie("token", "123456"));
        request.setParameter("name", "张三");
        request.setParameter("age", "18");
        request.setContent("""
                    {
                        "name":"李四",
                        "age":20
                    }
                """.getBytes(StandardCharsets.UTF_8));
        return new StandardServletMultipartResolver().resolveMultipart(request);
    }

        测试类中获取ApplicationContext,准备测试请求:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
//获取beanFactory,为了解析${} 
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
// 准备测试 Request
HttpServletRequest request = mockRequest();

        由于我们没有使用AnnotationConfigServletWebServerApplicationContext现,不具备在初始化时收集所有 @RequestMapping 映射信息,封装为 Map(K:路径,V:HandlerMethod)的能力,

所以需要手动准备HandlerMethod:

// 要点1. 控制器方法被封装为 HandlerMethod
HandlerMethod handlerMethod = new HandlerMethod(new Controller(), Controller.class.getMethod("test", String.class, String.class, int.class, String.class, MultipartFile.class, int.class, String.class, String.class, String.class, HttpServletRequest.class, User.class, User.class, User.class));

        因为表单传递的参数类型默认都是String字符串,但是方法参数中的类型可能是其他,例如int,long等,所以还需要定义类型转换:

 // 要点2. 准备对象绑定与类型转换
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);

        还需要定义容器存储中间结果:

// 要点3. 准备 ModelAndViewContainer 用来存储中间 Model 结果
ModelAndViewContainer container = new ModelAndViewContainer();

        对参数值进行解析:

 for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
            RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(beanFactory,false);

            String annotations = Arrays.stream(parameter.getParameterAnnotations()).map(a -> a.annotationType().getSimpleName()).collect(Collectors.joining());
            String str = annotations.length() > 0 ? " @" + annotations + " " : " ";
            parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

            if (resolver.supportsParameter(parameter)) {
                // 支持此参数
                Object v = resolver.resolveArgument(parameter, container, new ServletWebRequest(request), factory);
//                System.out.println(v.getClass());
                System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName() + "->" + v);
            } else {
                System.out.println("[" + parameter.getParameterIndex() + "] " + str + parameter.getParameterType().getSimpleName() + " " + parameter.getParameterName());
            }

        只解析了@RequestParam注解的参数值

         但是可以看test中的第二个参数是没有解析到值的,该参数是隐式的使用了@RequestParam注解。RequestParamMethodArgumentResolver构造的第二个参数,如果填true,则可以进行识别。

RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(beanFactory,false);

        在上面的代码中添加了针对@RequestParam注解参数解析器:

RequestParamMethodArgumentResolver resolver = new RequestParamMethodArgumentResolver(beanFactory,false);

        如果需要对剩下的注解添加解析器,可以使用组合的方式,一次性加入所有的参数解析器:

   // 多个解析器组合
            HandlerMethodArgumentResolverComposite composite = new HandlerMethodArgumentResolverComposite();
            composite.addResolvers(
                    //                                          false 表示必须有 @RequestParam
                    new RequestParamMethodArgumentResolver(beanFactory, false),
                    new PathVariableMethodArgumentResolver(),
                    new RequestHeaderMethodArgumentResolver(beanFactory),
                    new ServletCookieValueMethodArgumentResolver(beanFactory),
                    new ExpressionValueMethodArgumentResolver(beanFactory),
                    new ServletRequestMethodArgumentResolver(),
                    new ServletModelAttributeMethodProcessor(false), // 必须有 @ModelAttribute
                    new RequestResponseBodyMethodProcessor(Collections.singletonList(new MappingJackson2HttpMessageConverter())),
                    new ServletModelAttributeMethodProcessor(true), // 省略了 @ModelAttribute
                    new RequestParamMethodArgumentResolver(beanFactory, true) // 省略 @RequestParam
            );

        后续判断是否支持参数,获取对应的参数时,只需要采用组合的对象即可,此外,加上了@ModelAttribute注解的参数,还会将模型数据放入ModelAndViewContainer中。

2、参数名的获取

        在上面的案例中,能获取到参数名是因为加入了参数名解析器:

 parameter.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());

        下面模拟一种无法获取参数名的情况,首先创建一个类,其中有foo(String name,int age)方法:

        手动进行编译,会发现参数名丢了:

        那么如何才能保留参数名?可以在编译时加-parameters参数:

        没有加参数时,通过javap -c -v反编译的结果,在方法上没有与参数名有关的信息:

       加上参数反编译后,多了一些信息记录方法参数名称

        也可以在编译时加入-g选项:

        这样做反编译后会生成一个本地变量表:

两者大致的区别:MethodParameters中的信息可以通过反射获取,但是LocalVariableTable可以通过ASM获取。

3、转换接口

3.1、类型转换

        在参数解析器的案例中,存在这样的情况:

@RequestParam("age") int age

        因为表单传递的参数类型默认都是String字符串,在案例中我们是定义了类型转换,在Spring中,类型转换又分为两套底层转换和一套高层实现:

        第一套底层转换:

        

  • Printer 把其它类型转为 String

  • Parser 把 String 转为其它类型

  • Formatter 是Printer 和Parser 共同实现的接口,综合 Printer 与 Parser 功能

  • Converter 可转换任意类型

  • Printer、Parser、Converter 经过适配转换成 GenericConverter 放入 Converters 集合

  • FormattingConversionService 的主要作用是将一个对象从一种表示形式转换为另一种表示形式,或者将一个对象格式化为字符串形式,以便在用户界面中显示或者从用户界面中读取。

        第二套底层转换:

  • PropertyEditor 把 String 与其它类型相互转换

  • PropertyEditorRegistry 可以注册多个 PropertyEditor 对象

  • 与第一套接口直接可以通过 FormatterPropertyEditorAdapter 来进行适配

        高层接口:

  • SimpleTypeConverter 仅做类型转换

  • BeanWrapperImpl 为 bean 的属性赋值,当需要时做类型转换,通过get()、set()方法

  • DirectFieldAccessor 为 bean 的属性赋值,当需要时做类型转换,无需get()、set()方法,直接通过字段即可

  • ServletRequestDataBinder 为 bean 的属性执行绑定(将请求参数中的信息绑定到java对象上),当需要时做类型转换,根据 directFieldAccess的布尔值选择通过get()、set()方法还是通过字段,具备校验与获取校验结果功能。

  • 上述四个接口都实现了 TypeConverter 这个高层转换接口,在转换时,会用到 TypeConverter Delegate 委派ConversionService 与 PropertyEditorRegistry 真正执行转换(Facade 门面模式)

    • 首先看是否有自定义转换器, @InitBinder 添加的即属于这种 (用了适配器模式把 Formatter 转为需要的 PropertyEditor)

    •  再看有没有 ConversionService 转换(是第一套底层FormattingConversionService 的顶级接口

    • 再利用默认的 PropertyEditor 转换(是第二套底层PropertyEditor的顶级接口

    • 最后有一些特殊处理


        SimpleTypeConverter:只有类型转换的功能

// 仅有类型转换的功能
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
Integer number = typeConverter.convertIfNecessary("13", int.class);
Date date = typeConverter.convertIfNecessary("1999/03/04", Date.class);
System.out.println(number);
System.out.println(date);

        BeanWrapperImpl:为bean的属性赋值,需要bean具有get()、set()方法

 // 利用反射原理, 为 bean 的属性赋值
 MyBean target = new MyBean();
 BeanWrapperImpl wrapper = new BeanWrapperImpl(target);
 wrapper.setPropertyValue("a", "10");
 wrapper.setPropertyValue("b", "hello");
 wrapper.setPropertyValue("c", "1999/03/04");
 System.out.println(target);

        DirectFieldAccessor:为 bean 的属性赋值,bean无需get()、set()方法

// 利用反射原理, 为 bean 的属性赋值
 MyBean target = new MyBean();
 DirectFieldAccessor accessor = new DirectFieldAccessor(target);
 accessor.setPropertyValue("a", "10");
 accessor.setPropertyValue("b", "hello");
 accessor.setPropertyValue("c", "1999/03/04");
 System.out.println(target);

        ServletRequestDataBinder:在web环境下,将请求参数中的信息绑定到java对象上

  // web 环境下数据绑定
  MyBean target = new MyBean();
  ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(target);
  MockHttpServletRequest request = new MockHttpServletRequest();
  request.setParameter("a", "10");
  request.setParameter("b", "hello");
  request.setParameter("c", "1999/03/04");

  dataBinder.bind(new ServletRequestParameterPropertyValues(request));

  System.out.println(target);

3.2、绑定器工厂

        假设我们现在有两个内部类:

public static class User {
//        @DateTimeFormat(pattern = "yyyy|MM|dd")
        private Date birthday;
        private Address address;

        public Address getAddress() {
            return address;
        }

        public void setAddress(Address address) {
            this.address = address;
        }

        public Date getBirthday() {
            return birthday;
        }

        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }

        @Override
        public String toString() {
            return "User{" +
                   "birthday=" + birthday +
                   ", address=" + address +
                   '}';
        }
    }

    public static class Address {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Address{" +
                   "name='" + name + '\'' +
                   '}';
        }
    }

        发送模拟请求:

  MockHttpServletRequest request = new MockHttpServletRequest();
  request.setParameter("birthday", "1999|01|02");
  request.setParameter("address.name", "西安");

        通过默认的ServletRequestDataBinder将请求参数中的信息绑定到java对象上

  ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(target);
  dataBinder.bind(new ServletRequestParameterPropertyValues(request));

        birthday字段的值并没有被绑定上,原因在于,默认的转换器无法识别yyyy|MM|dd这样的日期格式:

         这种情况就需要自定义转换器进行扩展:

         在进行自定义扩展前,我们需要换一种ServletRequestDataBinder的实现方式,即使用ServletRequestDataBinderFactory,以便于加入各种扩展:

ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, null);
factory.createBinder(new ServletWebRequest(request),target,"user");

        注意此时是没有任何扩展功能的,依旧无法对birthday字段的值进行绑定。

        ServletRequestDataBinderFactory的有参构造:

  • List<InvocableHandlerMethod> binderMethods:它封装了处理程序方法的相关信息,如方法本身、所属的 Controller、方法参数等。通常与PropertyEditor转换接口配合使用
  • initializer:在数据绑定过程中应用自定义的初始化逻辑。通常与ConversionService 转换接口配合使用
3.2.1、自定义转换方式一

        使用第二套底层转换接口PropertyEditor

        首先自定义一个类对转换器进行扩展,将来在执行factory.createBinder 时会回调该方法

        @InitBinder: 用于标记一个方法,该方法用于初始化 DataBinder对象,从而自定义数据绑定的行为。在控制器类中使用 @InitBinder注解标记的方法会在控制器处理请求之前被调用,可以用来注册自定义的属性编辑器、验证器等。

 static class MyController {
        @InitBinder
        public void aaa(WebDataBinder dataBinder) {
            // 扩展 dataBinder 的转换器
            dataBinder.addCustomFormatter(new MyDateFormatter("用 @InitBinder 方式扩展的"));
        }
    }

        MyDateFormatter中编写了具体扩展的逻辑,实现了Formatter接口:

public class MyDateFormatter implements Formatter<Date> {
    private static final Logger log = LoggerFactory.getLogger(MyDateFormatter.class);
    private final String desc;

    public MyDateFormatter(String desc) {
        this.desc = desc;
    }

    @Override
    public String print(Date date, Locale locale) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.format(date);
    }

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        log.debug(">>>>>> 进入了: {}", desc);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy|MM|dd");
        return sdf.parse(text);
    }


}

        将转换器扩展类封装成InvocableHandlerMethod对象,并且新建工厂,创建绑定器:

InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Collections.singletonList(method), null);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));

3.2.2、自定义转换方式二

       使用第一套底层转换接口的ConversionService

FormattingConversionService service = new FormattingConversionService();
        service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
 ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));

 3.2.3、两种转换方式结合使用

        当两种转换方式结合使用时,第二套底层转换接口PropertyEditor的优先级别更高,上文也提到过:

InvocableHandlerMethod method = new InvocableHandlerMethod(new MyController(), MyController.class.getMethod("aaa", WebDataBinder.class));
FormattingConversionService service = new FormattingConversionService();
service.addFormatter(new MyDateFormatter("用 ConversionService 方式扩展转换功能"));
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(Collections.singletonList(method), initializer); WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));

3.2.4、使用默认方式转换

        最后还可以通过默认方式进行转换:

ApplicationConversionService service = new ApplicationConversionService();
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
initializer.setConversionService(service);
ServletRequestDataBinderFactory factory = new ServletRequestDataBinderFactory(null, initializer);
WebDataBinder dataBinder = factory.createBinder(new ServletWebRequest(request), target, "user");
dataBinder.bind(new ServletRequestParameterPropertyValues(request));

        但是在实体类对应的字段上要加上

@DateTimeFormat(pattern = "yyyy|MM|dd")
private Date birthday;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1633468.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redux Toolkit 中持久化路由配置数组的实践指南

要将路由配置数组保存到 Redux Toolkit 中并持久化,你可以按照以下步骤进行操作: 创建一个 Slice 来管理路由配置 在 Redux Toolkit 中,我们使用 createSlice 来创建一个 slice,用于管理路由配置的状态。 import { createSlice } from reduxjs/toolkit;const routesSlice c…

【重磅】史上最全企业数字化转型项目流程管理资料大合集下载(2.5G,1429份)

重磅分享&#xff0c;史上最全企业数字化转型项目流程管理资料大合集&#xff0c;共1429份。 一、项目管理实战工具大全&#xff08;甘特图、表格、项目阶段文档等527份&#xff09; 二、项目管理流程规范制度&#xff08;各类流程制度共86份&#xff09; 三、项目管理模板&…

编译Qt6.5.3LTS版本(Mac/Windows)的mysql驱动(附带编译后的全部文件)

文章目录 0 背景1 编译过程2 福利参考 0 背景 因为项目要用到对MYSQL数据库操作&#xff0c;所以需要连接到MYSQL数据库。但是连接需要MYSQL驱动&#xff0c;但是Qt本身不自带MYSQL驱动&#xff0c;需要自行编译。网上有很多qt之前版本的mysql驱动&#xff0c;但是没有找到qt6…

Python数据结构与算法(1):将序列分解为单独的变量

问题 现在有一个包含 N 个元素的元组或者是序列&#xff0c;怎样将它里面的值解压后同时赋值给 N 个变量&#xff1f; 解决方案 任何的序列&#xff08;或者是可迭代对象&#xff09;可以通过一个简单的赋值操作来分解为单独的变量。 唯一的要求就是变量的总数和结构必须与序…

【数据结构7-1-查找-线性-二分法-二叉树-哈希表】

目录 1 查找基本概念2 线性表的查找2.1 顺序查找2.2 二分法查找2.3 分块查找 3 树表的查询3.1 二叉排序树3.1.1 定义3.1.2 二叉树的建立、遍历、查找、增加、删除&#xff1a;3.1.3 代码实现&#xff1a; 3.2 平衡二叉树3.2.1 平横因子3.2.2 不平横树的调整-左旋3.2.3 不平横树…

Unity 数字字符串逗号千分位

使用InputField时处理输入的数字型字符串千分位自动添加逗号&#xff0c;且自动保留两位有效数字 输入&#xff1a;123 输出&#xff1a;123.00 输入&#xff1a;12345 输出&#xff1a;12,345.00 代码非常简单 using UnityEngine; using TMPro;public class …

[机器学习系列]深入解析K-Means聚类算法:理论、实践与优化

目录 一、KMeans (一)Kmeans简介 (二)Kmeans作用和优点 (三)Kmeans局限和缺点 (四)Kmeans步骤 (五)如何选取最佳的K值的三种方法 (六)手肘法和目标函数的变化两种确定K值方法的区别 (七)如何选取第一次迭代的K个类中心------KMeans方法 (八)KMeans的常用参数介绍 二、…

【C语言刷题系列】对数字添加逗号

目录 一、问题描述 二、解题思路 三、源代码 拓展&#xff1a; 个人主页&#xff1a; 倔强的石头的博客 系列专栏 &#xff1a;C语言指南 C语言刷题系列 一、问题描述 二、解题思路 题目的要求&#xff0c;即对于一个较大的整数&#xff0c;每三位数字之间添加…

【go项目01_学习记录day01】

博客系统 1 vscode开发go项目插件推荐1.1 CtrlShiftP&#xff08;俗称万能键&#xff09; &#xff1a;打开命令面板。在打开的输入框内&#xff0c;可以输入任何命令。1.2 开发时&#xff0c;我们需要经常查阅 Go 语言官方文档&#xff0c;可惜因国内访问外网不稳定&#xff0…

自己手写了一个大模型RAG项目-05.基于知识库的大模型问答

大家好&#xff0c;我是程序锅。 github上的代码封装程度高&#xff0c;不利于小白学习入门。 常规的大模型RAG框架有langchain等&#xff0c;但是langchain等框架源码理解困难&#xff0c;debug源码上手难度大。 因此&#xff0c;我写了一个人人都能看懂、人人都能修改的大…

03-JAVA设计模式-解析器模式

解释器模式 什么是解析器模式 在Java中&#xff0c;解释器模式&#xff08;Interpreter Pattern&#xff09;是一种行为设计模式&#xff0c;它给定一个语言&#xff0c;定义它的文法的一种表示&#xff0c;并定义一个解释器&#xff0c;该解释器使用该表示来解释语言中的句子…

unity3d使用3D WebView播放网页中的视频

Unity2021.3.35f1&#xff0c;硬件ESP32-Cam&#xff0c;3D WebView插件 1.新建工程&#xff0c;导入3D WebView for Winfows和3D WebView for Android 2.打开场景Assets\Vuplex\WebView\Demos\Scenes\2_CanvasWebViewDemo 3.修改Canvas的Render Mode为Screen Space-Camera&am…

30秒出服装设计稿,森马用Serverless+AIGC 整“新活”!

“创新项目如何去赋能我们的业务&#xff0c;这件事情在森马很重要。阿里云函数计算帮我们屏蔽掉了想把AI落地到实际业务场景中 GPU 算力资源储备、采购成本、技术门槛等很多难题&#xff0c;从而迅速做出决策&#xff0c;快人一步站在正确的起点&#xff0c;体验新技术对整个服…

『春招实习』2023年3月春招实习求职经历

『春招实习』2023年3月春招实习求职经历 简介货拉拉一面杭州吉里一面传墨科技一面 简介 3月初我便开始陆续投递简历&#xff0c;直观的感受就是【投递的太晚了】&#xff0c;很多公司很早就开始招聘实习生了。 但是自己一直在担心没准备好&#xff0c;所以就想着再准备一天就投…

数据结构单链表”质检员“*2

1.随机链表的逻辑结构复制 原题链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode-cn.com/problems/copy-list-with…

机器学习:深入解析SVM的核心概念(问题与解答篇)【三、核函数】

核函数 **问题一&#xff1a;为什么说是有限维就一定存在高维空间可分呢&#xff1f;**原始空间与特征空间为什么映射到高维空间可以实现可分核函数的作用 **问题二&#xff1a;最终怎么得到函数**从对偶问题到决策函数的步骤&#xff1a;结论 **问题三&#xff1a;为什么说特征…

Kafka 3.x.x 入门到精通(03)——Kafka基础生产消息

Kafka 3.x.x 入门到精通&#xff08;03&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.4.1 生产消息的基本步骤2.4.2 生产消息的基本代码2.4.3 发送消息2.4.3.1 拦截器2.4.3.1.1 增加拦截器类2.4.3.1.2 配置拦截器 2.4.3…

c#数据库:1.c#创建并连接数据库

安装软件:SQL Server Management Studio Management Studio Visual Studio 2022 启动服务: 打开SQL Server Management Studio Management Studio ,连接到服务器(GUANZU是我的计算机名) 新建数据库,随便起个名字叫aq: c#代码: using System; using System.Collections.Gener…

UG NX二次开发(C++)-获取模型中所有的拉伸(Extrude)特征

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在UG 12中创建几个拉伸特征3、UFun中获取对象类型4、通过NXOpen过渡5.测试结果1、前言 在采用UG NX二次开发时,有时需要在模型中获取特定的对象,比如拉伸特征、关联特征等等。但是通过…

大数据分析与应用实验(黑龙江大学)

实验一 Hadoop伪分布式实验环境搭建与WordCount程序 一、实验目的 1、学习搭建Hadoop伪分布式实验环境 2、在伪分布式实验环境下运行WordCount程序 二、实验内容 1、搭建Hadoop伪分布式实验环境&#xff0c;并安装Eclipse。 2、在Eclipse环境下&#xff0c;编写并执行Wor…