详细分析开发一个Servlet的步骤从实现Servlet接口,继承GenericServlet抽象类,继承HttpServlet抽象类逐步完善

news2024/11/25 12:31:18

实现Servlet接口

jakarta.servlet.Servlet是Servlet规范中的核心接口

Servlet对象的生命周期

Servlet对象的创建,对象上方法的调用,对象最终的销毁都是由Tomcat服务器全权负责的,JavaWeb程序员是无权干预的

  • 第一步: Tomcat服务器本质是一个WEB容器, 服务器在启动的时候会解析XML文件根据Servlet类的全类名利用反射机制创建Servlet对象
  • 第二步: 这些创建的Servlet对象都会被放到一个HashMap集合当中,请求路径作为map集合的key,Servlet对象作为map集合的value

只有放到Tomcat服务器创建的HashMap集合中的Servlet对象才能够被WEB容器管理, 我们自己new的Servlet对象不在容器当中不受WEB容器的管理

在这里插入图片描述

Servlet对象的创建时机: 默认情况下服务器在启动的时候Servlet对象并不会被实例化(启动服务器的时发现Servlet中的无参构造方法没有执行)

  • 用户没有发送请求之前,如果提前创建出来所有的Servlet对象必然耗费内存,另外如果这些Servlet一直没有用户访问那就没必要先创建

服务器启动的时候创建Servlet对象: 在servlet父标签中添加 子标签(整数值越小优先级越高)

<servlet>
    <servlet-name>aservlet</servlet-name>
    <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class>
    <!--在servlet标签中添加<load-on-startup>子标签,在该子标签中填写整数,越小的整数优先级越高-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>aservlet</servlet-name>
    <url-pattern>/a</url-pattern>
</servlet-mapping>

Servlet类中的一些方法

Servlet类中如果没有提供无参数的构造方法Tomcat就无法实例化Servlet对象,此时会出现500状态表示服务器端的Java程序发生了错误

  • 在Servlet实际开发当中不建议程序员来手动定义构造方法,因为定义不当很容易让无参数构造方法消失使服务器无法实例化Servlet对象

是否可以把Servlet中的init方法中的代码放到无参数构造方法中执行(无参数构造方法和init方法都是在对象第一次创建的时候执行并且只执行一次)

  • 不能,因为在编写Servlet类的时候手动编写构造方法很容易让无参数构造方法消失导致代码无法执行, 所以需要将初始化的的操作单独放在一个init方法中
方法名执行时机方法作用使用次数
无参构造方法当服务器接收到用户的请求路径时执行创建Servlet对象的方法只执行一次
init在Servlet对象第一次被创建之后执行完成初始化操作的方法(如初始化数据库连接池,线程池等)只需要执行一次
service只要用户发送一次请求,service方法必然会执行一次处理用户请求的核心方法,使用最多的方法发送N次请求则执行N次
destroy在销毁Servlet对象之前会调用一次destroy方法销毁Servlet对象之前的最后准备工作(如关闭程序中开启的流和数据库连接资源)只执行一次
getServletInfo获取servlet的相关信息(作者,版权号)
getServletConfig获取ServletConfig 对象(封装Servelt对象的初始化参数信息)

对Servlet中方法的测试

public class AServlet implements Servlet {

    // 无参数构造方法
    public AServlet() {
        System.out.println("AServlet无参数构造方法执行了");
    }
   
    // init方法通常是完成初始化操作的,init方法在执行的时候AServlet对象已经被创建出来了
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("AServlet's init method execute!");
    }

    // 只要用户发送一次请求,service方法必然会执行一次
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("AServlet's service method execute!");
    }

   
    // destroy方法在执行的时候,AServlet对象的内存还没有被销毁
    @Override
    public void destroy() {
        System.out.println("AServlet's destroy method execute!");
    }

    //获取Servlet的相关信息(作者,版权号)
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    //获取ServletConfig对象(封装Servelt对象的初始化参数信息)
    @Override
    public String getServletInfo() {
        return null;
    }
}

Servlet处理请求的流程

用户发送第一次请求的时候

  • 第一步: Tomcat服务器根据请求路径解析web.xml文件匹配对应的全类名然后通过反射机制调用无参数构造方法创建Servlet对象
  • 第二步: Tomcat服务器调用Servlet对象的init方法完成初始化(init方法在执行的时候Servlet对象已经被创建出来了)
  • 第三步: Tomcat服务器调用Servlet对象的service方法处理请求

用户在发送第二次,第三次请求的时候,此时不会再创建新的Servlet对象,直接调用之前创建好的Servlet对象的service方法处理请求

  • 虽然Servlet对象Tomcat只创建了一次只有一个实例但是Servlet类并不符合单例模式的特点(构造方法私有化), 只能说是假单例

由于关闭服务器时Tomcat服务器会释放Servlet对象的内存, 所以此时会调用Servlet对象的destroy方法做销毁之前的最后准备工作

  • destroy方法执行的时候AServlet对象还没有被销毁, destroy方法执行结束之后AServlet对象的内存才会被Tomcat释放

继承GenericServlet抽象类

jakarta.servlet.GenericServlet 是官方提供的标准通用的Servlet抽象类不需要我们手写 , 但是我们可以仿造手写一个

适配器设计模式

手机不能直接插到220V的电压上,可以找一个充电器/适配器(Adapter)连接220V的电压解决问题

对于UserService类来说core方法是最主要的方法;对于CustomerService类来说m2是最主要的方法;其他方法大部分情况下是不用使用的

public interface MyInterface {
    void m1();
    void m2();
    void m3();
    void core();
}

UserService类的适配器是个抽象类没有实现主要的方法core()

public abstract class UserAdapter implements MyInterface {
    @Override
    public void m1() {

    }
    @Override
    public void m2() {

    }
    @Override
    public void m3() {

    }
    //UserService类的主要方法适配器没有去实现
    public abstract void core();
}

// UserService类只需要实现主要的core()方法即可,其他方法适配器已经帮我们实现了
public class UserService extends UserAdapter {
    @Override
    public void core() {
    }
}

CustomerService类专用的适配器是个抽象类没有实现主要的方法m2()

public abstract class CustomerAdapter implements MyInterface {
    @Override
    public void m1() {

    }
    //CustomerService类的主要方法适配器没有去实现
    public abstract void m2() ;

    @Override
    public void m3() {

    }
    @Override
    public void core() {

    }
}
// CustomerService类只需要实现主要的m2()方法即可,其他方法适配器已经帮我们实现了
public class CustomerService extends CustomerAdapter{
    @Override
    public void m2() {

    }
}

手写GenericServlet

对于编写的Servlet类经常使用service方法,其他方法大部分情况下是不需要使用的, 如果直接实现Servlet接口就需要实现接口中的所有方法代码会很冗余

编写一个标准通用的抽象类GenericServlet实现Servlet接口(这个类就是一个适配器), 其中有一个抽象方法service

  • 以后编写的所有Servlet类都不要直接实现Servlet接口了 , 直接继承GenericServlet重写service方法即可

Servlet类中的init方法中的ServletConfig对象是Tomcat服务器创建的,当调用init方法的时候会将ServletConfig对象传给了init方法(方法参数上属于局部变量)

public class Tomcat {
    public static void main(String[] args){
        // .....
        // 创建LoginServlet对象(通过反射机制调用无参数构造方法来实例化LoginServlet对象)
        Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
        Object obj = clazz.newInstance();

        // 向下转型
        Servlet servlet = (Servlet)obj;

        // 创建ServletConfig对象
        // 多态(Tomcat服务器完全实现了Servlet规范)
        ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();

        // 调用Servlet的init方法
        servlet.init(servletConfig);

        // 调用Servlet的service方法
        //....
    }
}

// init方法通常是完成初始化操作的,init方法在执行的时候AServlet对象已经被创建出来了
@Override
public void init(ServletConfig servletConfig) throws ServletException {
    System.out.println("AServlet's init method execute!");
}

如何保证ServletConfig对象在service方法中能够使用

  • 当XxxServlet继承GenericServlet类之后,当调用XxxServlet对象init方法时最终会调用父类GenericServlet中的init方法接收ServletConfig对象(局部变量)
  • 给GenericServlet类声明一个成员变量把init方法中的局部变量赋值给成员变量 , 这样子类就可以通过父类提供的getServletConfig方法访问
public abstract class GenericServlet implements Servlet {
    // 成员变量
    private ServletConfig config;

    @Override
    //使用final修饰的init方法子类不能重写
    public final void init(ServletConfig config) throws ServletException {
        //System.out.println("servletConfig对象,小猫咪创建好的:" + config);
        this.config = config;

        // 调用init()方法,这个this就是Servlet对象,如果子类重写了init方法那么一定是调用子类的init方法
        this.init();
    }


    //这个init方法是供子类重写的
    public void init(){

    }
    //抽象方法,这个方法最常用。所以要求子类必须实现service方法。
    public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse)
        throws ServletException, IOException;
    
    @Override
    public void destroy() {

    }
    @Override
    public String getServletInfo() {
        return null;
    }
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
}

GenericServlet类提供一个无参的init方法供子类重写 , 当服务器调用有参的init方法的时候再调用无参的init方法,如果子类重写了无参的init方法就调用子类的

  • 子类重写有参的init方法会导致父类有参的init方法不执行 , 那么GenericServlet类的ServletConfig对象就为null , 将来造成空指针异常
public class LoginServlet extends GenericServlet{
    @Override
    public void init(){
        System.out.println("LoginServlet's init() method execute!");
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("正在处理用户登录请求,请稍后。。。。。");
        // 想在LoginServlet子类中使用ServletConfig对象怎么办?
        ServletConfig config = this.getServletConfig();
        System.out.println("service方法中是否可以获取到ServletConfig对象?" + config);
    }
}

GenericServlet中的方法

GenericServlet实现Servlet类的基本方法

方法名功能
getServletConfig()获取 ServletConfig对象(封装Servelt对象的初始化参数信息即init-param标签中的信息)
getServletContext()获取ServletContext对象(封装了上下文初始化参数信息的对象即context-param标签中的信息)
getServletInfo()获取servlet的相关信息(作者,版权号)
log()记录日志信息

GenericServlet继承ServletConfig类的扩展方法

  • 只要自己写的Servlet继承了GenericServlet就可以不用先通过获取ServletConfig对象调用方法, 使用this调用父类ServletConfig的这四个方法
方法名功能
public String getInitParameter(String name)通过init-param标签的name获取value
public Enumeration< String > getInitParameterNames()获取所有init-param标签的name
public ServletContext getServletContext()获取context-param标签中的信息
public String getServletName()获取Servlet对象的name

继承HttpServlet抽象类

以后我们编写Servlet类的时候,实际上也不会直接继承 GenericServlet类,而是要继承HttpServlet(处理HTTP协议更便捷)

  • B/S结构的系统是基于HTTP超文本传输协议的 , 所以在Servlet规范当中专门提供了一个为HTTP协议准备的Servlet类叫做HttpServlet

jakarta.servlet.http包下的类和接口

  • HttpServletRequest: Tomcat服务器将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中
  • HttpServletResponse: Tomcat服务器将“响应协议”中的数据全部解析出来,然后将这些数据全部封装到response对象当中
类或接口作用
jakarta.servlet.http.HttpServlet(抽象类)HTTP协议专用的Servlet类,处理HTTP协议更便捷
jakarta.servlet.http.HttpServletRequest(接口)HTTP协议专用的请求对象,面向HttpServletRequest获取请求协议中的数据
jakarta.servlet.http.HttpServletResponseHTTP协议专用的响应对象

HttpServlet类的继承结构及源码分析: 我们以后编写的Servlet要继承HttpServlet类

  • jakarta.servlet.Servlet(接口)【爷爷】
  • jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
  • jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】

HttpServlet处理请求流程

第一步用户发起请求: Tomcat服务器匹配请求路径,通过反射机制调用无参数构造方法创建对应的Servlet对象,

public class HelloServlet extends HttpServlet {
    // 用户第一次请求,创建HelloServlet对象的时候会执行这个无参数构造方法
    public HelloServlet() {
    }
    //override 重写 doGet方法
    //override 重写 doPost方法
}

第二步Tomcat服务器调用Servlet对象的有参init方法完成初始化: 若本类没有提供该方法,父类HttpServlet也没有,最终执行GenericServlet类中有参的init方法

public abstract class GenericServlet implements Servlet, ServletConfig,java.io.Serializable {
    // 用户第一次请求的时候,HelloServlet对象第一次被创建之后这个init方法会执行
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    // 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后会执行这个没有参数的init()
    public void init() throws ServletException {
        // NOOP by default
    }
}

第三步Tomcat服务器调用Servlet对象的参数不含httpXxx的service方法处理请求: 若本类没有提供service方法,执行父类HttpServlet类service方法

public abstract class HttpServlet extends GenericServlet {
    // 用户发送第一次请求的时候这个service会执行, 用户发送第N次请求的时候还是会执行
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest  request;
        HttpServletResponse response;
        try {
            // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        // 调用重载的service模板方法
        service(request, response);
    }
}

第四步调用HttpServlet类提供的重载的参数含httpXxx的service模板方法(该方法负责定义核心算法骨架,具体的实现步骤延迟到子类中去完成)

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    // 获取请求方式,可能是GET POST PUT DELETE HEAD OPTIONS TRACE七种之一
    String method = req.getMethod();

    // 如果请求方式是GET请求,则执行doGet方法。
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        // 如果请求方式是POST请求,则执行doPost方法。
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

第五步Tomcat服务器根据请求方式调用具体的doXxx方法处理请求: 若本类没有提供则调用父类的doXxx方法(此时就会报405错误)

public class HelloServlet extends HttpServlet {x
    // 通过无参数构造方法创建对象
    public HelloServlet() { }
    // 本类没有提供有参的init方法,执行父类HttpServlet的该方法。HttpServlet类中没有该方法,会继续执行GenericServlet类中的该方法
    // 本类没有提供参数不含httpXxx的service方法, 执行父类HttpServlet的该方法
    // 当前端发送的请求是get请求的时候,我这里重写doGet方法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doGet</h1>");
    }

    // 当前端发送的请求是post请求的时候,我这里重写doPost方法
    /*protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doPost</h1>");
    }*/
}   

HttpServlet细节分析

如果Tomcat根据前端发起的请求方式调用具体的doXxx方法处理请求时而XxxServlet没有提供对应的方法时就会执行HttpServelt的doXxx方法(必然报405错误)

//HttpServlet中的doXxx方法的作用就是报一个405错误,提醒开发人员编写对应的方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException{
    // 报405错误
    String msg = lStrings.getString("http.method_get_not_supported");
    sendMethodNotAllowed(req, resp, msg);
}

//如果子类没有重写doPost方法 , 则会执行 HttpServlet 的doPost方法
//该方法的作用是报一个 405 错误
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    // 报405错误
    String msg = lStrings.getString("http.method_post_not_supported");
    sendMethodNotAllowed(req, resp, msg);
}

重写HttpServlet类中重载的参数含httpXxx的service()模板方法后就享受不到HTTP协议专属的东西如405错误的提醒

public class HelloServlet extends HttpServlet {x
    // 通过无参数构造方法创建对象
    public HelloServlet() {
    }
	// 重写HttpServlet类中重载的service()方法后HttpServlet类中的service()模板方法就不会在执行,里面的doXxx方法也就无法执行 , 就不会报405错误
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.print("<h1>hello servlet</h1>");
    } 
}

在Servlet类当中将doGet和doPost方法都进行重写确实可以避免405错误的发生,但是不建议因为405错误还是有用的该报错的时候就应该让他报错

  • 如果你要是同时重写doGet和doPost,还不如直接重写service模板方法代码还能少写一点
  • 后端决定前端需要发什么样的请求,后端重写了doGet方法前端一定要发get请求,端重写了doPost方法,前端一定要发post请求

Servlet类的最终开发步骤

第一步:编写一个Servlet类,直接继承HttpServlet

第二步:重写doGet方法或者重写doPost方法,到底重写谁javaweb程序员说了算( 重写父类某个方法有快捷键 ctrl + o )

第三步:将编写的Servlet类配置到web.xml文件当中

第四步:准备前端页面指定发起的请求路径

public class HelloServlet extends HttpServlet {x
    // 通过无参数构造方法创建对象
    public HelloServlet() { }
    // 本类没有提供有参的init方法,执行父类HttpServlet的该方法。HttpServlet类中没有该方法,会继续执行GenericServlet类中的该方法
    // 本类没有提供参数不含httpXxx的service方法, 执行父类HttpServlet的该方法
    // 当前端发送的请求是get请求的时候,我这里重写doGet方法
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doGet</h1>");
    }

    // 当前端发送的请求是post请求的时候,我这里重写doPost方法
    /*protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doPost</h1>");
    }*/
}   

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

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

相关文章

cocos creator 鼠标画笔|画线

cocos creator 版本使用 至少适配版本2.3.2以上 案例: 简要思路:MOUSE_MOVE事件和Graphics组件实现 前端也可以通过canvas和mousemove事件实现&#xff0c;原理一致 具体步骤如下&#xff1a; 1.添加节点Node 2.在Node节点上绑定组件Graphics 3.添加下方脚本drawcontroll.ts…

JMeter测试笔记(三):基本组件的使用

引言&#xff1a; 关于JMeter的测试笔记&#xff0c;这是第三篇&#xff0c;主要是介绍JMeter的基本组件的使用。 在进行性能测试时&#xff0c;了解并掌握基本组件的使用非常重要&#xff0c;因为它们是构成JMeter测试计划的基础。在本文中&#xff0c;我们将深入了解JMeter…

解决IDEA 引入GO SDK无法加载本地Go,The selected directory is not a valid home for Go SDK

解决IDEA 引入GO SDK无法加载本地Go&#xff0c;The selected directory is not a valid home for Go SDK 解决方法&#xff1a; 找到按照go目录下对应&#xff1a; C:\Program Files\go\go1.20.4\src\runtime\internal\sys 位置有一个文件名“zversion.go” 编辑文件 const …

JAVA开发(保姆级微服务搭建过程)

一、微服务的概念以及发展过程 微服务是指只提供一项功能的服务。 微服务是围绕某个业务领域展开的。比如把电商业务比较一个业务领域&#xff0c;技术部、产品部等就是围绕电商业务领域展开的微服务。在电商项目领域&#xff0c;由支付、商品、订单等微服务组成。 微服务架构…

【TypeScript】枚举类型和泛型的详细介绍

目录 TypeScript枚举类型 TypeScript泛型介绍 &#x1f3b2;泛型的基本使用 &#x1f3b2;泛型接口的使用 &#x1f3b2;泛型类的使用 &#x1f3b2;泛型的类型约束 枚举类型 枚举类型是为数不多的TypeScript特有的特性之一, JavaScript是没有的&#xff1a; 枚举其实就…

CiteScore 2022正式发布,AI Open首获即达22.5分,三大高被引论文值得一看

当前&#xff0c;由 ChatGPT、Stable Diffusion 等 AI 大模型掀起的新一轮科技浪潮&#xff0c;正在引领各个行业的变革性发展。及时、深入、全面地了解 AI 行业的前沿动态&#xff0c;有助于我们跟上 AI 行业的发展步伐&#xff0c;抓住时代机遇。 一本学术期刊的高影响力&am…

Java避免踩坑:Set对象排重注意避免重复-以commons-csv读取csv文件并排查为例

场景 HashSet HashSet 基于 HashMap 来实现的&#xff0c;是一个不允许有重复元素的集合。HashSet 允许有 null 值。 HashSet 是无序的&#xff0c;即不会记录插入的顺序。 HashSet 不是线程安全的&#xff0c; 如果多个线程尝试同时修改 HashSet&#xff0c; 则最终结果是…

Classification-based framework for binarization on OCT-ME论文学习和总结

论文&#xff1a;Classification-based framework for binarization on mice eye image in vivo with optical coherence tomography 源码&#xff1a;https://github.com/MIP2019/mip2019.github.io/blob/main/spsvm 目录 一、背景和出发点 二、创新点 三、SPSVM的具体实现…

hive创建udf函数流程

1.编写udf函数 引入pom文件 <dependencies> <dependency> <!-- 这个属于额外的jar包 自己按需引用 比如你想搞得函数 里面要连接mysql 这里肯定需要引入mysql的驱动包 我这个包是为了计算字符串的表达式的。 --> <groupId>org.apache.com…

Git GitLab 使用及规范

Git 基本操作 Git安装配置及基本使用 从官网下载安装包&#xff0c;手动完成安装。打开Git Bash命令行工具&#xff0c;执行命令ssh-keygen -t rsa -C Email-Addresss生成一个密钥对。登录到GitLab&#xff0c;点击右上角你的用户头像&#xff0c;点击Edit Profile settings&…

【Linux】linux和Linus

1991.09.17 21岁的芬兰学生林纳斯.托瓦兹在网上发布开源操作系统Linux0.01。 林纳斯本纳第克特托瓦兹&#xff08;Linus Benedict Torvalds&#xff0c;1969年12月28日- &#xff09;&#xff0c;芬兰赫尔辛基人&#xff0c;著名的电脑程序员&#xff0c;Linux内核的发明人及 …

Revit如何在体量中进行放样及如何生成垫层

一、Revit如何在体量中进行放样 体量中的放样在常规族放样的基础上进行了简化&#xff0c;下面通过实例来说明如何在体量中进行放样。 &#xff08;1&#xff09;新建概念体量。点击“新建概念体量”→“公制体量”→“打开”。 &#xff08;2&#xff09;绘制放样路径。点击“…

盖雅「劳动力账户」助力物业行业实现精细化工时成本管理

物业行业的用工形式和工时制度多样&#xff0c;需要大量的劳动力提供安保、清洁、维修及其他服务&#xff0c;所以人工成本成为了物业公司最大的经营成本之一。而这些员工近半数都是外包人员。因此&#xff0c;物业公司需要利用数字化工具实时记录员工的出勤和工时&#xff0c;…

Grid++Report多个子报表实现

子报表实现参考 GridReport子报表实现 基于上述单个子报表的实现&#xff0c;我们可以衍生多个子报表实现 子报表与主报表可以是关联关系&#xff0c;也可以是独立存在。 配置主从关系字段即代表有关联关系 只有明细网格节点内配置的子报表才可以设置主从关系字段 报表头、…

强光LED手电筒方案开发设计

在户外活动中&#xff0c;不管是徒步还是露营&#xff0c;经常需要使用多功能强光手电筒。宇凡微推出的多功能战术强光LED手电筒方案&#xff0c;具有十多年LED灯项目研发经验&#xff0c;方案成熟&#xff0c;支持定制开发。 一、战术强光LED手电筒方案功能介绍 户外使用的LED…

安装Logstash并导入Movielens测试数据集(基于elasticsearch-8.5.2版本)

安装Logstash并导入Movielens测试数据集 0 安装前准备工作 0.1 安装包下载 组件安装包下载地址Logstashlogstash-8.5.2-linux-x86_64.tar.gzelastic官网&#xff1a;https://www.elastic.co/cn/downloads/past-releases#logstash elastic中文社区&#xff1a;https://elasti…

第六届中国软件开源创新大赛-openGauss赛道全面开启

第六届“中国软件开源创新大赛”在国家自然科学基金委信息科学部的指导下&#xff0c;由中国计算机学会&#xff08;CCF&#xff09;主办&#xff0c;西北工业大学、绿色计算产业联盟、CCF 开源发展委员会联合承办。旨在为国内开源社区提供展示、交流、合作的平台&#xff0c;激…

实力出圈!联诚发LED屏与xr虚拟拍摄解决方案亮相文博会!

六月初夏的深圳&#xff0c;正迎来一场文化产业界的盛事。6月7日&#xff0c;为期5天的第十九届中国(深圳)国际文化产业博览交易会(下称“文博会”)正式拉开帷幕。联诚发LCF作为国家级高新技术企业、国家级专精特新小巨人企业&#xff0c;以及优秀的LED显示与数字装备企业代表亮…

03.Web大前端时代之:HTML5+CSS3入门系列~H5功能元素

2.功能元素 1.hgroup 对网页或区段&#xff08;section&#xff09;的标题进行组合 2.figure <figure> 标签规定独立的流内容&#xff08;图像、图表、照片、代码等等&#xff09;。 figure 元素的内容应该与主内容相关&#xff0c;但如果被删除&#xff0c;则不应对…

如何申请项目管理专业人员能力等级评价(CSPM)?

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…