正文

jsp EL表达式详解(jsp比较两个数的大小)燃爆了

这里写图片描述

文章摘要

```html <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>EL表达式小结</title> </head> <body> <h2>EL表达式简介</h2> <p>EL表达式(Expression Language)是一种嵌入式语言,用于在JSP页面上获取数据和执行操作。它通过语法和运算符提供数据处理功能,具有高效性、简洁性和可扩展性。</p> <h2>EL表达式的语法结构</h2> <p>EL表达式的基本语法为:`${expression}`,其中`expression`可以是变量名、方法调用或运算表达式。例如:`${pageContext.request.contextPath}`。</p> <h2>EL表达式的内置对象</h2> <p>EL提供四个内置对象,用于从四大域中获取数据:</p> <ul> <li><strong>pageContext</strong>:获取从JSP语句到四大域的属性空间。</li> <li><strong>request</strong>:获取从 request 区域获取的指定参数。</li> <li><strong>session</strong>:从 session 区域获取指定的键值对。</li> <li><strong>application</strong>:从 application 区域获取指定的键值对。</li> </ul> <p>例如,`new java.util.RequestScope.request` 用于获取 request 区域的值。</p> <h2>EL表达式获取属性的示例</h2> <h3>从四大域获取属性</h3> <ul> <li>`pageContext.getAttribute("name")`:从 pageContext 区域获取指定属性的值。</li> <li>`request.getAttribute("name")`:从 request 区域获取指定属性的值。</li> <li>`session.getAttribute("name")`:从 session 区域获取指定属性的值。</li> <li>`application.getAttribute("name")`:从 application 区域获取指定属性的值。</li> </ul> <h3>从指定域获取属性</h3> <ul> <li>`pageContext.request.contextPath`:从 request 区域获取 contextPath 值。</li> <li>`request.session.request`:从 session 区域获取 request 值。</li> <li>`session.request.request`:从 request 区域获取 request 值。</li> <li>`application.session.request`:从 session 区域获取 request 值。</li> </ul> <h2>EL表达式的运算符和功能</h2> <h3>算术运算符</h3> <ul> <li>`+`:加法运算</li> <li>`-`:减法运算</li> <li>`*`:乘法运算</li> <li>`/`:除法运算</li> <li>`%`:取模运算</li> </ul> <h3>关系运算符</h3> <ul> <li>`==`:等于</li> <li>`!=`:不等于</li> <li>`>`:大于</li> <li>`>=`:大于等于</li> <li `<`:小于</li> <li>`<=`:小于等于</li> </ul> <h3>逻辑运算符</h3> <ul> <li>`!`:非</li> <li>`&&`:与</li> <li>`||`:或</li> <li>`?:`:条件运算符</li> </ul> <h3>条件运算符</h3> <ul> <li>`&&`:逻辑与</li> <li>`||`:逻辑或</li> <li>`?:`:条件</li> </ul> <h2>自定义EL函数</h2> <h3>定义和注册自定义函数</h3> <ol> <li>在目标类中定义函数。</li> <li>在目标类的头文件中注册函数。</li> <li>在JSP页面中调用自定义函数。</li> </ol> <p>例如,定义一个自定义函数`LowerToUpper`,用于将字符串转换为大写:</p> <pre><code class="language-java"> public class MyEL { private static MyEL instance; public static MyEL getInstance() { if (instance == null) { instance = new MyEL(); } return instance; } public static String LowerToUpper(String str) { return str.toUpperCase(); } } </code></pre> <h2>注意事项</h2> <ul> <li>EL表达式不能在Java代码块、表达式块等动态代码部分使用。</li> <li>EL表达式只能从四大域属性空间中获取数据(pageContext、request、session、application)。</li> <li>EL表达式不会抛出空指针异常,只会显示为`null`。</li> <li>EL表达式不会抛出数组越界异常,只会显示为无效索引。</li> <li>EL表达式不具有处理字符串的能力,只能通过JSTL或自定义EL函数进行处理。</li> </ul> </body> </html> ```


目录一、EL表达式介绍语法格式二、EL获取数据(从四大域中获取属性)三、EL中的内置对象1、域属性相关(4个)2、其他重要内置对象(4个)1、pageContext2、param(获取请求中的指定参数)3、paramValues4、initParam四、EL访问Bean的属性1、什么是java Bean2、EL访问Bean属性五、EL访问数组中的数据六、EL获取list中数据七、EL访问Map八、EL中的运算符(empty)1、先说说几个常用运算符2、empty运算符九、自定义EL函数十、EL总结
Expression Language表达式语言 是一种在JSP页面获取数据的简单方式(只能获取数据,不能设置数据) 在JSP2.0开始引入概念

在JSP页面的任何静态部分均可通过:${expression}来获取到指定表达式的值

二、EL获取数据(从四大域中获取属性)

EL只能从四大域中获取属性

1、如果没有使用EL的内置对象,则查找数据顺序是依次按照由小到大范围从四大域中查找指定名称的属性值

EL只能从四大域中获取属性

1、如果没有使用EL的内置对象,则查找数据顺序是依次按照由小到大范围从四大域中查找指定名称的属性值

– pageContext<request<session<application <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% pageContext.setAttribute(“name”, “linjie”); request.setAttribute(“name”, “lucy”); session.setAttribute(“name”, “king”); application.setAttribute(“name”, “bilibili”); %> name=${name } </body> </html>

这里写图片描述

可以看出没有使用EL内置对象时查找顺序是由小到大,所以最先显示的是pageContext域中的

2、使用EL内置对象,从指定域中获取数据,提高了查找效率

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% pageContext.setAttribute(“name”, “linjie”); request.setAttribute(“name”, “lucy”); session.setAttribute(“name”, “king”); application.setAttribute(“name”, “bilibili”); %> name=${applicationScope.name } </body> </html>

这里写图片描述

可以看出,使用applicationScope即可指定application域中的name输出,当然其他域也是类似,下文会说这四大域属性相关的内置对象

EL有11个内置对象,这里主要讲域属性相关的4个和其他4个

EL的11个内置对象,除了pageContext以外,其他10个内置对象的类型都是java.util.Map类型 pageScope:从page范围域属性空间中查找指定的key requestScope:从request范围域属性空间中查找指定的key sessionScope:从session范围域属性空间中查找指定的key applicationScope:从application范围域属性空间中查找指定的key
<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% pageContext.setAttribute(“name”, “linjie”); request.setAttribute(“name”, “lucy”); session.setAttribute(“name”, “king”); application.setAttribute(“name”, “bilibili”); %> name=${applicationScope.name }<br> name=${pageScope.name }<br> name=${sessionScope.name }<br> name=${requestScope.name }<br> </body> </html>

这里写图片描述

1、pageContext

该pageContext与JSP内置对象pageContext是同一个对象。通过该对象,可以获取到request、response、session、servletContext、servletConfig等对象注意:这些对象在EL里不是内置对象,这些对象只能通过pageContext获取

在EL中直接${pageContext.request}即可获取request对象,其底层调用的是pageContext.getRequest()方法。同理,也可以通过类似方法获取其他对象

重点:其中最常用的:,代表web应用下的根,可以看出下面action中的路径可读性更强了

Regster.java

package linjie.com; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Regster extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().append(“Served at: “).append(request.getContextPath()); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <%– ${pageContext.request.contextPath }代表web应用的根 –%> <form action=”${pageContext.request.contextPath }/regster” method=”POST”> xxx<input type=”text” name=”name”/><br> yyy<input type=”text” name=”age”/><br> <input type=”submit” value=”点击”> </form> </body> </html>

这里写图片描述

2、param(获取请求中的指定参数)

其底层实际调用request.getParameter()

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <%– ${pageContext.request.contextPath }代表web应用的根 –%> <form action=”${pageContext.request.contextPath }/show.jsp” method=”POST”> xxx<input type=”text” name=”name”/><br> yyy<input type=”text” name=”age”/><br> <input type=”submit” value=”点击”> </form> </body> </html>

show.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> name=${param.name }<br> age=${param.age }<br> </body> </html>

客户浏览器访问结果

这里写图片描述 

这里写图片描述

3、paramValues

获取请求中的指定参数的所以值,其底层实际调用request.getParameterValues()

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <%– ${pageContext.request.contextPath }代表web应用的根 –%> <form action=”${pageContext.request.contextPath }/show.jsp” method=”POST”> xxx<input type=”text” name=”name”/><br> yyy<input type=”text” name=”age”/><br> 爱好: <input type=”checkbox” name=”hobby” value=”sleep”>睡觉 <input type=”checkbox” name=”hobby” value=”play”>玩 <input type=”checkbox” name=”hobby” value=”eat”>吃 <input type=”submit” value=”点击”> </form> </body> </html>

show.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> name=${param.name }<br> age=${param.age }<br> hobby[0]=${paramValues.hobby[0] }<br> hobby[1]=${paramValues.hobby[1] }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

这里写图片描述

4、initParam

获取初始化参数,其底层调用的是ServletContext.getInitParameter()

web.xml

<?xml version=”1.0″ encoding=”UTF-8″?> <web-app xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns=”http://java.sun.com/xml/ns/javaee” xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd” id=”WebApp_ID” version=”2.5″> <display-name>07eltttt</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!–初始化参数 –> <context-param> <param-name>name</param-name> <param-value>林杰</param-value> </context-param> <servlet> <display-name>Regster</display-name> <servlet-name>Regster</servlet-name> <servlet-class>linjie.com.Regster</servlet-class> </servlet> <servlet-mapping> <servlet-name>Regster</servlet-name> <url-pattern>/regster</url-pattern> </servlet-mapping> </web-app>

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> name=${initParam.name } </body> </html>

客户浏览器显示结果

这里写图片描述

JavaBean是公共Java类,但是为了编辑工具识别

需要满足至少三个条件

有一个public默认构造器(例如无参构造器) 属性使用public 的get,set方法访问,也就是说设置成private同时get,set方法与属性名的大小也需要对应。例如属性name,get方法就要写成,public String getName(){},N大写。 需要序列化。这个是框架,工具跨平台反映状态必须的

EL可以通过${key.属性}的方式获取到指定值,其底层实际调用的是该对象的相应属性的get方法

Demo.java

package linjie.com; public class Demo { private String name; private int age; public Demo(String name,int age){ this.name=name; this.age=age; } public String getName() { return name; } public void setName(String name) { this.name=name; } public int getAge() { return age; } public void setAge(int age) { this.age=age; } @Override public String toString() { return super.toString(); } }

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ import=”linjie.com.Demo” pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% Demo test=new Demo(“linjie”,12); request.setAttribute(“ELttt”, test); %> name=${requestScope.ELttt.name }<br> age=${requestScope.ELttt.age }<br> <!– 若访问为null的对象的属性,EL是不会抛出空指针异常的,只是不显示而已 –> names=${requestScope.ELtttxx.name }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% String[] names=<br> </body> </html>

下面是访问类的数组

Stu.java

package linjie.com; public class Stu { private String sname; private String address; public Stu() { super(); } public Stu(String sname, String address) { super(); this.sname=sname; this.address=address; } public String getSname() { return sname; } public void setSname(String sname) { this.sname=sname; } public String getAddress() { return address; } public void setAddress(String address) { this.address=address; } @Override public String toString() { return super.toString(); } }

index.jsp

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ import=”linjie.com.*” pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% Stu[] stus=new Stu[3]; stus[0]=new Stu(“xlj”,”A”); stus[1]=new Stu(“lucy”,”B”); stus[2]=new Stu(“kingA”,”C”); pageContext.setAttribute(“stus”,stus); %> stus[1].Sname=${stus[1].sname } </body> </html>

客户浏览器显示结果

这里写图片描述

<%@page import=”java.util.*”%> <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% List<String> names=new ArrayList<String>(); names.add(“xlj”); names.add(“lucy”); pageContext.setAttribute(“names”, names); %> <!– 因为List底层是数组,所以可以这样写 –> names[1]=${names[1] }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

注意:

EL可以通过索引访问List,但无法访问Set。因为Set中没有索引概念

<%@page import=”java.util.*”%> <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% Map<String,Object> map=new HashMap<String,Object>(); map.put(“age”, 20); map.put(“name”, “xlj”); pageContext.setAttribute(“map”, map); %> name=${map.name }<br> age=${map.age }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

算术运算符:+、-、*、/、%(不支持++、–) 关系运算符:==、!=、>、>=、<、<= 逻辑运算符:!、&&、||、not、and、or 条件运算符:?: 取值运算符:[]、点号

用法为${empty 变量},结果为布尔值

<%@page import=”java.util.*”%> <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <% String name1=null; String name2=””; List<String> name3=new ArrayList<String>(); pageContext.setAttribute(“name1”, name1); pageContext.setAttribute(“name2”, name2); pageContext.setAttribute(“name3”, name3); %> empty对于没有定义的变量,运算结果为true: empty namex=${empty namex }<br> empty对于null的引用,运算结果为true: empty name1=${empty name1 }<br> empty对于为空串的String引用,运算结果为true: empty name2=${empty name2 }<br> empty对于没有元素的数组或集合,运算结果为true: empty name3=${empty name3 }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

因为EL本身不具有处理字符串能力,所以可以自定义EL函数

– 定义函数(新建MyEL.java类)

– 注册:先找到jsp2-example-taglib.tld,将头部以及注册函数复制到自己创建的.tld文件中(.tld放在WEB-INF下)

– 在index.jsp中使用,使用时需要<%@ taglib uri=”http://tomcat.apache.org/jsp2-example-taglib” prefix=”MyEL” %>

1、定义函数MyEL.java

package linjie.com; //自定义函数 //该类及其函数,需要在扩展名为.tld的xml文件中注册 //tld:tag library definition(标签库定义) //xml文件是需要约束的,即需要配置文件头部。这个头部约束可以从一下文件中进行复制 //在Tomcat安装目录下:webapps\examples\WEB-INF\jsp2 //文件为:jsp2-example-taglib.tld //这个.tld的xml文件,需要定义在当前web项目的WEB-INF目录下,在此目录下创建以.tld结尾的xml文件 //将jsp2-example-taglib.tld中头部复制到创建的xml文件中 //再将函数注册,还是在jsp2-example-taglib.tld底部中复制 public class MyEL { private static MyEL instance; public static MyEL getInstance() { if(instance==null) { instance=new MyEL(); } return instance; } //字符串小写变大写 public static String LowerToUpper(String str) { return str.toUpperCase(); } }

2、将jsp2-example-taglib.tld中头部部分以及底部的注册函数部分复制到自己创建的tld(在WEB-INF下)文件中

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

MyEL.tld

<?xml version=”1.0″ encoding=”UTF-8″?> <taglib xmlns=”http://java.sun.com/xml/ns/j2ee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd” version=”2.0″> <!– 定义标签库信息 –> <description>A tag library exercising SimpleTag handlers.</description> <tlib-version>1.0</tlib-version> <short-name>MyEL</short-name><!– 标签库名称,一般定义成和文件名一样 –> <uri>http://tomcat.apache.org/jsp2-example-taglib</uri> <!– 注册函数 –> <function> <name>MyLowerToUpper</name> <function-class>linjie.com.MyEL</function-class><!– 方法得类 –> <function-signature>java.lang.String LowerToUpper( java.lang.String )</function-signature><!– 方法签名 :需要返回值以及方法名、参数–> </function> </taglib>

3、在index.jsp中使用,使用时需要<%@ taglib uri=”http://tomcat.apache.org/jsp2-example-taglib” prefix=”MyEL” %>

<%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <%@ taglib uri=”http://tomcat.apache.org/jsp2-example-taglib” prefix=”MyEL” %><!– tld中的uri和short-name –> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <html> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″> <title>Insert title here</title> </head> <body> <!– 这个方法名是在tld注册时的name –> ${MyEL:MyLowerToUpper(“sasas”) }<br> <!– EL函数只能处理四大域中的属性值及String常量 –> <% String name=”xlj”; pageContext.setAttribute(“name”, name); %> ${MyEL:MyLowerToUpper(name) }<br> </body> </html>

客户浏览器显示结果

这里写图片描述

EL表达式不能出现在Java代码块、表达式块等JSP动态代码部分 EL只能从四大域属性空间中获取数据(pageContext、request、session、application) EL不会抛出空指针异常,只会不显示 EL不会抛出数组越界异常,只会不显示 EL不具有对字符串进行处理的能力(可以使用JSTL的EL或者自定义EL函数)

到此这篇关于jsp EL表达式详解的文章就介绍到这了,更多相关jsp EL表达式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:JSP页面无法识别EL表达式问题解决方案使用IDEA编写jsp时EL表达式不起作用的问题及解决方法浅谈SpringMVC jsp前台获取参数的方式 EL表达式jsp 开发之struts2中s:select标签的使用JSP中EL表达式用法_动力节点Java学院整理JSP 开发之 releaseSession的实例详解springMVC如何将controller中Model数据传递到jsp页面JSP之EL表达式基础详解