JavaEE基础之- Servlet相关

目录

[1. Servlet概述(了解)](#1. Servlet概述(了解))

[1.1. JavaWeb的三大组件](#1.1. JavaWeb的三大组件)

[1.2. Servlet的作用](#1.2. Servlet的作用)

[2. Servlet初识(熟练)](#2. Servlet初识(熟练))

[2.1. 第一个Servlet](#2.1. 第一个Servlet)

[2.1.1. Servlet说明](#2.1.1. Servlet说明)

[2.1.2. Servlet接口](#2.1.2. Servlet接口)

[2.1.3 创建Servlet](#2.1.3 创建Servlet)

[2.1.4 JavaWeb请求响应流程](#2.1.4 JavaWeb请求响应流程)

[2.2 Servlet生命周期](#2.2 Servlet生命周期)

3.HttpServlet

[3.1 HttpServlet介绍](#3.1 HttpServlet介绍)

[3.2 Http请求方法](#3.2 Http请求方法)

[3.3 创建HttpServlet](#3.3 创建HttpServlet)

[3.3.1 第一种方法](#3.3.1 第一种方法)

[3.3.2 第二种方法](#3.3.2 第二种方法)

[3.3.3 Servlet创建顺序](#3.3.3 Servlet创建顺序)

4.ServletConfig

5.Servlet路径映射

6.相对路径和绝对路径

[6.1 相对路径](#6.1 相对路径)

[6.2 绝对路径](#6.2 绝对路径)

7.ServletContext

[7.1 ServletContext介绍](#7.1 ServletContext介绍)

[7.2 ServletContext API](#7.2 ServletContext API)

[7.3 ServletContext使用](#7.3 ServletContext使用)

[7.3.1 常规应用](#7.3.1 常规应用)

[7.3.2 通过ServletContext对象读取资源文件](#7.3.2 通过ServletContext对象读取资源文件)

8.Servlet相关资料

[8.1 单例的Servlet 单例多线程](#8.1 单例的Servlet 单例多线程)

[8.2 Servlet的生命周期](#8.2 Servlet的生命周期)


1. Servlet概述(了解)

1.1. JavaWeb的三大组件

Servlet是JavaWeb三大组件之一,它是我们学习JavaWeb最为基本的组件,也就是说你一定要**100%**的掌握它。

其它两种:Filter(过滤器)、Listener(观察者模式),后续讲解

1.2. Servlet的作用

Servlet,即Server Let的意思,用来处理用户请求。当客户端发出请求后,由Tomcat去找到可以处理这一请求的Servlet来处理。

也就是说,用户的请求是由Servlet来处理的!例如用户发出登录请求,那么就应该由处理登录的Servlet来处理;用户发出登录请求,那么就应该由登录Servlet来处理。

项目

Tomcat先加载web.xml


2. Servlet初识(熟练)

2.1. 第一个Servlet

2.1.1. Servlet说明

servlet 是运行在 Web 服务器中的小型 Java 程序。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。也就是说,Servlet是由我们自己来完成的!但Servlet一定要实现javax.servlet.Servlet接口,并且还要在web.xml文件中部署!不然Tomcat是找不到我们写的Servlet的。(因为Tomcat一启动,就去访问web.xml配置文件)

2.1.2. Servlet接口

javax.servlet.Servlet接口中方法如下:

Ø void init(ServletConfig servletConfig):当Tomcat创建Servlet实例后,马上调用init()方法。这个方法只在创建后调用一次!用来做Servlet初始化工作!一个Servlet实例只被创建一次,所以init()方法也只被调用一次!(本方法编写对Servlet的初始化代码)

初始化:一开始去干的事,比如登录,登陆之前要干的。或者:晚上去教室学习,第一件事一定是开灯,每个人都要干,那么开灯这个事情可以放在 init()里。一调用这个类,就去开灯。

Ø void service(ServletRequest request, ServletResponse response):Servlet实例在每次处理请求时都调用service()方法。

Ø void destroy():当Tomcat要销毁Servlet实例时,会先调用destroy()方法,再销毁它。所谓销毁Servlet,其实就是在Servlet缓存池中把Servlet移除!一般只有Tomcat关闭时,才会销毁Servlet实例!

Ø ServletConfig getServletConfig():这个方法返回ServletConfig对象,但我们不能自己去创建ServletConfig对象,所以一般我们会在init()方法中把init()方法的参数保存起来,然后再在本方法中返回它。ServletConfig对象对应web.xml中当前Servlet实例的配置信息。

Ø String getServletInfo():这个方法只是返回一个字符串,用来说明当前Servlet。基本没用!

2.1.3 创建Servlet

第一步:常见HelloServlet实现Servlet的接口,实现接口中的方法

java 复制代码
public class Servlet1 implements Servlet{
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

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

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

    }

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

    @Override
    public void destroy() {

    }
}

点击Servlet,Ctrl+h

第二步:配置servlet的访问路径

XML 复制代码
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1"
         metadata-complete="true">

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.qcby.servlet.Servlet1</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
</web-app>

2.1.4 JavaWeb请求响应流程

Tomcat一启动,就去加载web.xml配置文件,根据请求里的路径,找到url-pattern对应的名字,根据名字找到对应的类。

当Tomcat接收到请求(http://localhost:8080/javaWeb/hello)后,Tomcat会找到 javaWeb 项目中的 web.xml 文件,然后通过 hello 这个请求路径,查找处理这个请求的Servlet类型。这刚好与 /hello匹配,这说明存在一个可以通过这个请求的Servlet。然后再通过/hello 查找到hello,然后再通过hello 查找到com.qcby.servlet.Servlet1。这时Tomcat已经得到了一个Servlet类名字(一个字符串而已)。

Tomcat通过Servlet类名字去查找内存中是否存在Servlet对象,如果存在,那么就不用再去创建,直接获取这个Servlet实例,调用它的service()方法完成请求!

如果这个Servlet不存在,那么Tomcat会通过反射来创建Servlet实例,并把Servlet实例存放到Servlet池中,再去调用Servlet的service方法处理请求。

2.2 Servlet生命周期

javax.servlet.Servlet接口中,有三个方法说明了Servlet的生命周期

Ø void init(ServletConfig): 创建后马上调用init()完成初始化;

Ø void service(ServletRequest,ServletResponse): 每次处理请求时调用service()方法;

Ø void destroy(): 当Tomcat要销毁Servlet实例时,先调用destroy()方法。

Servlet对象的实例默认情况下是在浏览器第一次调用servlet时候被创建的(可以修改其创建时机后续讲解)

现在你应该已经清楚了,Servlet的实例不由我们创建,Servlet的方法不由我们来调用,这一切都是由Tomcat来完成!!!这就是说由Tomcat来管理Servlet,而我们只需要去编写Servlet实现类,并将其部署到web.xml文件中去!

再次提醒,只有这三个方法是生命周期中的方法。也就是说,生命周期方法会被Tomcat在不同的时间点来调用!而其它方法就不会被调用了!!!如果你在自己写的Servlet中添加了其他方法,那么Tomcat也是不会去调用它们的!但你可以让生命周期方法去调用你自己写的方法就OK了!


3.HttpServlet

3.1 HttpServlet介绍

因为现在我们的请求都是基于HTTP协议的,所以我们应该专门为HTTP请求写一个Servlet做为通用父类。

由上图我们可以看出,以后再写Servlet 可以直接继承HttpServlet

Ø Servlet 一个标准

Ø GenericServlet 是Servlet接口子类

Ø HttpServlet 是GenericServlet子类,一个专门处理Http请求的Servlet

3.2 Http请求方法

HTTP请求方法不只是GET和POST,还有其他的方法,但基本上用不上。这里只是简单介绍一下。你自己心里有个数,HTTP请求除了GET和POST之外还有别的就行了。

& GET 通过请求URI得到资源

& POST 用于添加新的内容

& PUT 用于修改某个内容

& DELETE 删除某个内容

& CONNECT 用于代理进行传输,如使用SSL

& OPTIONS 询问可以执行哪些方法

& PATCH 部分文档更改

& RACE 用于远程诊断服务器

& HEAD 类似于GET, 但是不返回body信息,用于检查对象是否存在,以及得到对象的元数据。

& TRACE 用于远程诊断服务器

3.3 创建HttpServlet

3.3.1 第一种方法

xml方式:适用于配置多个Servlet

创建一个类继承HttpServlet:处理Http请求的Servlet

java 复制代码
package com.qcby.servlet;


import javax.servlet.http.HttpServlet;

public class Servlet2 extends HttpServlet {

}

配置Servlet的映射路径

XML 复制代码
<servlet>
    <servlet-name>hello2</servlet-name>
    <servlet-class>com.qcby.servlet.Servlet2</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>hello2</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>

3.3.2 第二种方法

IDEA直接创建servlet

ctrl+o重写。

创建完成,自己填写映射路径

@WebServlet表示这个类是一个Servlet

这个Servlet的名称是Servlet4,映射路径是/hello4

3.3.3 Servlet创建顺序

有些Servlet需要在Tomcat启动时就被创建,而不是第一次访问时被创建,那么可以在web.xml文件中配置 <servlet> 元素。

在 <servlet> 元素中添加子元素 <load-on-startup> 元素!

作用:

在 Web 应用启动时,Servlet 容器(如 Tomcat)默认情况下会在第一次收到针对某个 Servlet 的请求时才加载并初始化该 Servlet。但通过 <load-on-startup> 元素,可以指定某些 Servlet 在 Web 应用启动时就被加载和初始化,而不是等到第一次请求时才进行。

好处:

  • 提前初始化资源 :如果 Servlet 在初始化时需要进行一些耗时的操作,如加载配置文件、建立数据库连接等,使用 <load-on-startup> 可以在应用启动时就完成这些操作,避免在第一次请求时才执行这些操作导致的响应延迟。
  • 确保资源可用性:某些 Servlet 可能会在初始化时创建一些全局资源,如缓存、线程池等,提前加载这些 Servlet 可以确保这些资源在应用启动后就可用。
XML 复制代码
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.qcby.servlet.Servlet1</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;

所有添加了子元素的Servlet,都会在Tomcat启动时被创建!当然,只是被创建,但没有处理请求!但我们知道在Servlet生命周期中init()方法会在创建后被调用,所以你可以在init()方法中做一些输出,查看是否在Tomcat启动时调用了它。

元素的值是一个序号,Tomcat会使用这个序号给多个Servlet排序!然后在Tomcat启动时会按这个顺序来创建Servlet实例对象!


4.ServletConfig

ServletConfig对象对应web.xml文件中的元素。例如你想获取当前Servlet在web.xml文件中的配置名,那么可以使用**servletConfig.getServletName()**方法获取!

不能自己去创建ServletConfig对象,Servlet的init()方法的参数就是ServletConfig类型的。Tomcat在调用init()方法时,会传递ServletConfig对象。你可以在init()方法中使用它!

XML 复制代码
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.qcby.servlet.Servlet1</servlet-class>
    <init-param>
        <param-name>username</param-name>
        <param-value>root</param-value>
    </init-param>
    <init-param>
        <param-name>password</param-name>
        <param-value>qbjava</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

参数列表里配置ServletConfig,通过ServletConfig调用方法可以得到xml里设置的参数。

java 复制代码
String username = servletConfig.getInitParameter("username");
System.out.println("username:"+username);
Enumeration<String> parameterNames = servletConfig.getInitParameterNames();
while (parameterNames.hasMoreElements()){
    String element = parameterNames.nextElement();
    System.out.println(element+":"+servletConfig.getInitParameter(element));
}

添加了两个初始化参数,第一个参数的名称为paramName1,第一个参数的值为paramValue1;第二个参数的名称为paramName2,第二个参数值为paramValue2。

在 <servlet> 元素中可以加载多个 <init-param> ,每个 <init-param> 表示一个参数。 <init-param> 下有两个子元素 :<param-name> 和 <param-value> , 其中 <param-name> 表示参数的名称,而 <param-value> 元素参数的值。

注意,

<param-name> 是添加到<servlet> 元素中,而不是 <servlet-mapping> 中。

使用ServletConfig对象的getInitParameter(String paramName)方法可以获取指定参数名称的参数值。getInitParameterNames()方法返回所有参数的名字,返回值类型为Enumeration。


5.Servlet路径映射

关于Url-Pattern的配置:

Ø 完全路径匹配 以/开头 例如 /aaa /aaa/bbb

Ø 目录 匹配 以/开头 例如 /aaa/* /*

Ø 扩展名 匹配 不能以/开头 例如 *.do *.action ....

优先级: 完全路径匹配 > 目录匹配 > 扩展名匹配

经典错误: /*.do

举例:

对于如下的一些映射关系:

Ø Servlet11 映射到 /abc/*

Ø Servlet22 映射到 /*

Ø Servlet33 映射到 /abc

Ø Servlet44 映射到 *.do

问题:

Ø URL为"/abc/a.html","/abc/*"和"/*"都匹配,哪个servlet响应

Servlet引擎将调用Servlet1。同样是目录匹配所限定范围更精确的先匹配

Ø URL为"/abc"时,"/abc/*"和"/abc"都匹配,哪个servlet响应

Servlet引擎将调用Servlet3。

Ø URL为"/abc/a.do"时,"/abc/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet1。

Ø URL为"/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet2.

Ø URL为"/xxx/yyy/a.do"时,"/*"和"*.do"都匹配,哪个servlet响应

Servlet引擎将调用Servlet2。

服务器端路径 /demo4 (转发、包含...)


6.相对路径和绝对路径

Java中使用的路径,分为两种:绝对路径和相对路径。归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的!

我们以下面的项目路径来进行举例:

6.1 相对路径

相对路径,根据当前资源路径 与 目标资源路径,寻找相对位置关系,通过 . (当前目录) 和 .. (上一级目录) 访问目标资源;

综上所述,我们发现相对路径总需要分析当前路径与目标路径对应关系,编写规则会根据当前路径不同而不同。

6.2 绝对路径

绝对路径是指目录下的绝对位置,直接到达目标位置

带有协议完整路径 (跨网站) http://localhost/day5/hello

以/ 开始路径 (同一个站点内) : /day5/hello

服务器端和客户端对于/ 的区别

客户端访问路径:/day5/hello

服务器内部路径:/hello, 多用于服务器跳转, 页面包含(jsp)


7.ServletContext

7.1 ServletContext介绍

ServletContext是一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放。

ServletConfig和ServletContext区别:

ServletConfig: 每个Servlet在初始化时,Servlet容器会为其创建一个ServletConfig对象,并在调用Servlet的init(ServletConfig config) 方法时将其传递给Servlet。ServletConfig对象包含了Servlet的初始化参数信息,这些参数通过标签在web.xml中配置。 ServletConfig对象仅对具体的某一个Servlet有效,并且不能被其他Servlet访问.

ServletContext: Web容器在启动时会为每个Web应用程序创建一个唯一的ServletContext对象。ServletContext对象可以看作是Web应用的共享内存,所有的Servlet都可以访问这个对象。

request,一个用户可有多个; 请求域对象

session,一个用户一个; 会话域对象

而servletContext,所有用户共用一个。应用域对象

所以,为了节省空间,提高效率,ServletContext中,要放必须的、重要的、所有用户需要共享的线程又是安全的一些信息。

Ø WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。

Ø 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

Ø 查看ServletContext API文档,了解ServletContext对象的功能。

7.2 ServletContext API

|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Object | getAttribute(String name) Returns the servlet container attribute with the given name, or null if there is no attribute by that name. |
| String | getContextPath() Returns the context path of the web application. |
| String | getInitParameter(String name) Returns a String containing the value of the named context-wide initialization parameter, or null if the parameter does not exist. |
| String | getRealPath(String path) Returns a String containing the real path for a given virtual path |
| void | setAttribute(String name, Object object) Binds an object to a given attribute name in this servlet context. |
| InputStream | getResourceAsStream(String path) Returns the resource located at the named path as an InputStream object. |

Enumeration getInitParameterNames()

获取 web.xml 文件的中的所有的上下文参数名称。其返回值为枚举类型 Enumeration。

void setAttribute(String name, Object object)

在 ServletContext 的公共数据空间中,也称为域属性空间,放入数据。这些数据对于 Web应用来说,是全局性的,与整个应用的生命周期相同。当然,放入其中的数据是有名称的,通过名称来访问该数据。

Object getAttribute(String name)

从 ServletContext 的域属性空间中获取指定名称的数据。

void removeAttribute(String name)

从 ServletContext 的域属性空间中删除指定名称的数据。

String getRealPath(String path)

获取当前 Web 应用中指定文件或目录在本地文件系统中的路径,是基于盘符的路径。

String getContextPath()

获取当前应用在 Web 容器中的名称。

7.3 ServletContext使用

7.3.1 常规应用

Ø 获取WEB应用的全局初始化参数

Ø 通过ServletContext对象实现数据共享

key-value值:全局的

XML 复制代码
<context-param>
    <param-name>aaaa</param-name>
    <param-value>bbbb</param-value>
</context-param>
java 复制代码
ServletContext servletContext = servletConfig.getServletContext();
String aaaa = servletContext.getInitParameter("aaaa");
System.out.println("aaaa:"+aaaa);

示例:

统计站点访问次数

java 复制代码
package com.qcby.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "Servlet3",urlPatterns = "/hello3")
public class Servlet3 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public Servlet3() {
        super();
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置字符编码
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html; charset=utf-8");



        //获取全局的共享数据
        ServletContext servletContext = this.getServletContext();

        //获取计数器count
        Integer count = (Integer) servletContext.getAttribute("count");

        //如果获取的计算器对象为空 ,说明是第一次访问,并将count,放入servletCount
        if( servletContext.getAttribute("count") == null) {
            count = 1;
            servletContext.setAttribute("count", count);
        }else {
            //否则就不是第一次访问,将登陆的计数器进行加1的数据更新
            servletContext.setAttribute("count", count+1);
        }

        //将登陆的次数显示在页面上
        PrintWriter out =response.getWriter();
        out.print("<!DOCTYPE html>\r\n" +
                "<html>\r\n" +
                "<head>\r\n" +
                "<meta charset=\"UTF-8\">\r\n" +
                "<title>登陆网页次数统计</title>\r\n" +
                "</head>\r\n" +
                "<body>");
        out.print("<h1>");
        out.print("您是第 "+ servletContext.getAttribute("count")+"位访客");
        out.print("<h1>");
        out.print("</body>\r\n" +
                "</html>");
    }
}

7.3.2 通过ServletContext对象读取资源文件

第一种方法,

通过ServletContext.getRealPath()方法

java 复制代码
// 获得到ServletContext对象ServletContext 
servletContext = config.getServletContext();
// 获得工程目录web下文件的绝对路径 
//D:\workspace\idea\servlet-demo1\out\artifacts\servlet_demo1_war_exploded\tx.properties

// getRealPath的参数内容不会被校验,只有真正要用这个路径的时候才知道路径对不对
// String path = servletContext.getRealPath("tx.properties");
String path = servletContext.getRealPath("tx.properties");
System.out.println(path);
try {
    InputStream is = new FileInputStream(path);
    Properties p = new Properties();    
    p.load(is);    
    String username = p.getProperty("username");    
    System.out.println(username);
} catch (Exception e){
    e.printStackTrace();
}

第二种方法,

使用servletContext.getResourceAsStream()方法

java 复制代码
//获得工程目录web下文件的流第一个/代表项目的根目录
InputStream is = servletContext.getResourceAsStream("/WEB-INF/tx1.properties");

第三种方法,

使用getClass().getClassLoader().getResourceAsStream()方法

java 复制代码
//使用类加载器的方式来读取classpath下的资源文件,好处不依赖与ServletContext,任何类都可以获得classpath下的资源文件,
//不需要再自己指定/WEB-INF/classes
InputStream is = this.getClass().getClassLoader().getResourceAsStream("tx2.properties");

8.Servlet相关资料

面试题

8.1 单例的Servlet 单例多线程

因为Servlet实例是由Tomcat来创建的,但Tomcat只会创建一个Servlet实例,所以Servlet就是单例的!这与我们自己写的单例模式不太一样。因为这种单例是通过容器tomcat来管理而实现的!

一个实例需要在同一个时间点上处理多个请求!

同步就是安全,但效率太低!

Servlet是线程不安全的!

Ø 不写属性;

Ø 不写可以存储数据的属性!

8.2 Servlet的生命周期

Ø Servlet 通过调用 init () 方法进行初始化

Ø Servlet 调用 service() 方法来处理客户端的请求

Ø Servlet 通过调用 destroy() 方法终止(结束)。

Ø 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

相关推荐
用键盘当武器的秋刀鱼11 分钟前
springBoot统一响应类型3.5.3版本
java·spring boot·spring
qq_4315101617 分钟前
tomcat组件概览
java·tomcat
栗筝i24 分钟前
Spring 核心技术解析【纯干货版】- XVII:Spring 网络模块 Spring-WebFlux 模块精讲
java·网络·spring
工业互联网专业35 分钟前
基于springcloud微服务架构的巡游出租管理平台
java·vue.js·spring cloud·微服务·毕业设计·源码·课程设计
cwtlw43 分钟前
Spring相关面试题总结
java·笔记·后端·spring
漫天转悠1 小时前
Java8 到 Java21 系列之 Lambda 表达式:函数式编程的开端(Java 8)
java·开发语言
yiridancan1 小时前
深入浅出:Spring Bean 的初始化流程详解
java·开发语言·后端·spring
苏格拉没有底_coder1 小时前
【Easylive】convertVideo2Ts 和 union 方法解析
java
nlog3n2 小时前
Java 原型模式 详解
java·开发语言·原型模式
ylfhpy3 小时前
Java面试黄金宝典30
java·数据库·算法·面试·职场和发展