JSP自定义标签就是如此简单


声明:本文转载自https://my.oschina.net/u/3777556/blog/1619444,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

为什么要用到简单标签?

上一篇博客中我已经讲解了传统标签,想要开发自定义标签,大多数情况下都要重写doStartTag(),doAfterBody()和doEndTag()方法,并且还要知道SKIP_BODY,EVAL_BODY等等的变量代表着什么,在什么方法中使用。这样实在是太麻烦了!

因此,为了简化标签开发的复杂度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。

一般来说,实现了SimpeTag接口的标签称之为简单标签

SimpleTag接口

  • 首先我们来看一下它的源码吧
 	public interface SimpleTag extends JspTag { 	    void doTag() throws JspException, IOException; 	 	    void setParent(JspTag var1); 	 	    JspTag getParent(); 	 	    void setJspContext(JspContext var1); 	 	    void setJspBody(JspFragment var1); 	}  
  • setParent()和getParent()方法就不多说了,我们来看一下剩下的3个方法
 		void doTag() throws JspException, IOException;  	    void setJspContext(JspContext var1); 	 	    void setJspBody(JspFragment var1);   
  • 明显地:
    • doTag()就是我们要写代码处理逻辑地方
    • setJspContext(JspContext var1)是将PageContext对象传递给标签处理器类(PageContext是JspContext的子类)
    • setJspBody(JspFragment var1)把代表标签体的JspFragment对象传递给标签处理器对象

快速入门

  • 一般地,我们做开发都是继承SimpleTagSupport类(该类实现了SimpleTag)来编写自定义标签

  • 下面我们就来个快速入门吧

  • 目标:传入字符串格式就可以显示想要的格式日期,对比之前传统标签的,看有什么不同之处

  • 标签处理器类:

 	public class Demo1 extends SimpleTagSupport { 	 	 	    String format = null; 	 	    @Override 	    public void doTag() throws JspException, IOException { 	        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); 	 	        this.getJspContext().getOut().write(simpleDateFormat.format(new Date())); 	    } 	 	    public String getFormat() { 	        return format; 	    } 	 	    public void setFormat(String format) { 	        this.format = format; 	    } 	}  
  • tld文件:
    <tag>         <name>formatDate</name>         <tag-class>tag.Demo1</tag-class>         <body-content>tagdependent</body-content>         <attribute>             <name>format</name>             <required>true</required>             <rtexprvalue>true</rtexprvalue>         </attribute>     </tag>  
  • 效果:

 

 

 

 

  • 简单标签的好处就是不用去理会doStartTag、doEndTag、SKIP_BODY以及一系列的方法和属性!
  • 简单标签一个doTag()方法走天下!

SimpleTagSupport类的执行顺序:

  • ①WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象
  • ②WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。【注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法】
  • ③如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
  • ④如果简单标签有标签体,容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来
  • ⑤执行标签时:容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

 

 

深入简单标签

在我们讲解传统标签的时候,配合着SKIP_BODY、SKIP_PAGE等变量可以实现如下的功能:

  1. 控制jsp页面某一部分内容(标签体)是否执行
  2. 控制整个jsp页面是否执行
  3. 控制jsp页面内容重复执行
  4. 修改jsp页面内容输出
  • 简单标签可没有这些变量呀,那它怎么才能实现上面那些功能呢?

  • 在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况,我们来测试一下,在上面例子的代码中添加

 	throw new SkipPageException();  
  • 效果:

 

 

  • 至于其他的功能下面会讲到

带标签体的简单标签

  • SimpleTagSupport也可以带标签体,但是处理方法和传统标签完全不同

    • 传统标签是这样子的:将标签体的内容通过setBodyContent()注入到BodyContent对象中。
    • 简单标签是这样子的:通过JspFragment对象实现!
  • 我们来看一下JspFragment对象的源码吧

  	public abstract class JspFragment { 	    public JspFragment() { 	    } 	 	    public abstract void invoke(Writer var1) throws JspException, IOException; 	 	    public abstract JspContext getJspContext(); 	}   

JspFragment对象十分简单,重要的只有invoke(Writer var1)方法(获取JspContext对象并不重要,在标签描述器上就可以获取到了)

public abstract void invoke(java.io.Writer out) :

  • 用于执行JspFragment对象所代表的JSP代码片段
  • 参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)
  • 下面是标签处理器类的代码
 	public class Demo1 extends SimpleTagSupport { 	    @Override 	    public void doTag() throws JspException, IOException { 	 	        //得到代表标签体的对象 	        JspFragment jspFragment = getJspBody(); 	 	        //invoke方法接收的是一个Writer,如果为null,就代表着JspWriter(),将标签体的数据写给浏览器! 	        jspFragment.invoke(null); 	         	    } 	}  
  • 效果:

 

 

  • 既然标签体的内容是通过JspFragment对象的invoke()方法写给浏览器的,那么那么那么,我只要控制好invoke()方法,我想干什么就干什么!

  • 也就是说:

    • 不调用invoke()方法,标签体内容就不会输出到浏览器上
    • 重复调用invoke()方法,标签体内容就会被重复执行
    • 若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的
  • 来来来,我们来试验一下:

    • 不调用invoke()方法
         public void doTag() throws JspException, IOException {          //得到代表标签体的对象         JspFragment jspFragment = getJspBody();                  //jspFragment.invoke(null);      }  
    • 标签体的内容没有输出

       

    • 调用两次invoke()方法

         public void doTag() throws JspException, IOException {          //得到代表标签体的对象         JspFragment jspFragment = getJspBody();          jspFragment.invoke(null);         jspFragment.invoke(null);      }   
    • 标签体的内容被输出了两次

    • invoke()方法指定别的输出流(StringWriter),将标签体的内容写到流对象中,再通过流对象把数据取出来,达到修改的目的。
             //得到代表标签体的对象         JspFragment jspFragment = getJspBody();          //创建可以存储字符串的Writer对象         StringWriter stringWriter = new StringWriter();          //invoke()方法把标签体的数据都写给流对象中         jspFragment.invoke(stringWriter);          //把流对象的数据取出来,流对象的数据就是标签体的内容         String value = stringWriter.toString();          //将数据改成是大写的,写到浏览器中         getJspContext().getOut().write(value.toUpperCase());  
    • 标签体的内容被改成了大写!

     

     

我们可以发现,传统标签能完成的功能,简单标签都可以完成,并且更为简单!

 

自定义标签的应用

既然我们学了简单标签,我们就用简单标签来做开发吧!

防盗链

在讲解request对象的时候,我们讲解过怎么实现防盗链的功能。现在我们使用标签来进行防盗链

模拟下场景:1.jsp页面是海贼王资源,2.jsp页面提示非法盗链,index1.jsp是我的首页。别人想要看我的海贼王资源,就必须通过我的首页点进去看,否则就是非法盗链!

  • 标签处理器的代码
     @Override     public void doTag() throws JspException, IOException {          //如果想要做成更加灵活的,就把站点设置和资源设置成标签属性传递进来!          //等会我们要获取得到request对象,需要使用到JspContext的子类PageContext         PageContext pageContext = (PageContext) this.getJspContext();          //获取request对象         HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();          //获取到referer         String referer = httpServletRequest.getHeader("Referer");          //获取到response对象,等会如果是非法盗链,就重定向别的页面上         HttpServletResponse httpServletResponse = (HttpServletResponse) pageContext.getResponse();                  //非法盗链!         if (referer == null || !referer.startsWith("http://localhost:8080/zhongfucheng")) {  			//2.jsp提示了非法盗链!             httpServletResponse.sendRedirect("/zhongfucheng/2.jsp");              //不执行页面下面的内容了,保护页面             throw new SkipPageException();         }     }  
  • 1.jsp代码:
 	<zhongfucheng:Referer/> 	<html> 	<head> 	    <title></title> 	</head> 	<body> 	 	海贼王最新资源 	 	</body> 	</html>  
  • index1.jsp代码
 	<h1>这是首页!</h1> 	<a href="${pageContext.request.contextPath}/1.jsp"> 海贼王最新资源</a>  
  • 2.jsp代码
 	<body> 	你是非法盗链的!!!!!! 	</body>  
  • **第一次我是直接访问1.jsp,Referer是为空的,所以是非法盗链。第二次我是通过从首页点进去看的,所以可以访问1.jsp。**效果图:

 

 

if标签

在JSTL中,我们已经使用过了<c:if/>标签了,现在我们学习了自定义标签,可以开发类似于JSTL的if标签了!

既然是if标签,那么就需要编写带属性和带标签体的标签(需要判断是true还是false呀!,通过判断是否为真值来决定是否执行标签体的内容)

  • 标签处理器代码
 	public class Demo1 extends SimpleTagSupport { 	     	    //定义一个Boolean类型的变量 	    boolean test ; 	 	    @Override 	    public void doTag() throws JspException, IOException { 	 	        //获取到代表标签体内容的对象 	        JspFragment jspFragment = this.getJspBody(); 	 	        //如果为真值才执行标签体的内容 	        if (test == true) { 	            jspFragment.invoke(null); 	        } 	    } 	 	    public boolean isTest() { 	        return test; 	    } 	 	    public void setTest(boolean test) { 	        this.test = test; 	    } 	}   
  • tld文件的代码
     <tag>         <name>if</name>         <tag-class> tag.Demo1</tag-class>         <body-content>scriptless</body-content>         <attribute>             <name>test</name>             <required>true</required>             <rtexprvalue>true</rtexprvalue>         </attribute>     </tag>  
  • 本来就没有user这个域对象属性,所以user就是为null

 

 

  • 将user改成不为null,浏览器就没有输出了

 

 

forEach标签

forEach标签最基本的功能:遍历集合、数组

  • 首先,我先写一个可以遍历List集合的标签,可能我们会这样设计
 public class Demo2 extends SimpleTagSupport {      //遍历的是List集合,于是标签的属性就为List     private List items;      //遍历出来的对象就用Object存着,因为我们不知道List集合保存的是什么元素     private Object var;       @Override     public void doTag() throws JspException, IOException {          //获取到迭代器         Iterator iterator = items.iterator();          //遍历集合         while (iterator.hasNext()) {              //获取到集合的元素             var = iterator.next();              //.....var属性代表的就是集合的元素,现在问题来了,好像在标签体内无法获取到这个对象....             //做到这里完成不下去了....         }     }      public void setItems(List items) {         this.items = items;     }      public void setVar(Object var) {         this.var = var;     } }  

上面的思路是正常的,但是做不下去!我们换一个思路呗。上面的问题主要是在标签体获取不到被遍历出来的对象

 

我们这样做:把var定义成String类型的,如果遍历得到对象了,就设置PageContext的属性,var为关键字,对象为值。在标签体用EL表达式搜索以var为关键字的对象!每遍历出一个对象,就执行一次标签体!

  • 于是就有了以下的代码!
 public class Demo1 extends SimpleTagSupport {      //遍历的是List集合,定义List集合成员变量     private List items;      //以var为关键字存储到PageContext     private String var;        @Override     public void doTag() throws JspException, IOException {          //获取到集合的迭代器         Iterator iterator = items.iterator();          //获取到代表标签体内容的对象         JspFragment jspFragment = this.getJspBody();          //遍历集合         while (iterator.hasNext()) {             Object o = iterator.next();              //把遍历出来的对象存储到page范围中,关键字为标签的属性var(在标签体中使用EL表达式${var},就能够获取到集合的对象了!)             this.getJspContext().setAttribute(var, o);  			//每设置了一个属性,我就执行标签体             jspFragment.invoke(null);         }     }      public void setItems(List items) {         this.items = items;     }      public void setVar(String var) {         this.var = var;     } }   
  • tld文件如下
     <tag>         <name>forEach</name>         <tag-class>tag.Demo1</tag-class>         <body-content>scriptless</body-content>         <attribute>             <name>var</name>             <rtexprvalue>true</rtexprvalue>             <!--字符串即可,不需要EL表达式的支持-->             <required>false</required>         </attribute>          <attribute>             <name>items</name>             <required>true</required>             <rtexprvalue>true</rtexprvalue>         </attribute>     </tag>   
  • 测试的jsp代码如下
 	<% 	    List list = new ArrayList(); 	    list.add("zhongfucneng"); 	    list.add("1"); 	    list.add("2"); 	    list.add("3"); 	 	    request.setAttribute("list",list); 	%> 	<zhongfucheng:forEach items="${list}" var="str"> 	 	    ${str} 	 	</zhongfucheng:forEach>   
  • 效果:

 

 

上面写的仅仅能够遍历List集合,做一个通用的forEach标签麻烦的是在:不知道传进来的是什么类型的数组、什么类型集合!,需要逐一去判断

  • 我们的实现思路就是将所有的集合或数组都转成是Collection类型的!

  • 我们来尝试着写一下

         //如果items是Collection类型的,就强转为Colletion         if (items instanceof Collection) {             collection = (Collection) items;         }          //如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的!         if (items instanceof Map) {             Map map = (Map) items;             collection = (Collection) map.entrySet();         } 		 		//对象数组         if (items instanceof Object[]) {             Object[] objects = (Object[]) items;             collection = Arrays.asList(objects);         } 		 		//int[],Byte[],char[]等八大基本数据类型..... 		   

还有int[],byte[],char[]等八大基本数据类型,这八大基本数据类型就不能用Arrays.asList()把引用传进去了。因为JDK5以后会把引用自动装箱成Interger[]、Byte[]等等,而不是获取到数组的元素数据。

  • 测试代码如下:
     public static void main(String[] args) {          int[] ints = new int[]{1, 2, 3};         Object[] objects = new Object[]{"1", "2", "3"};          if (objects instanceof Object[]) {             Collection collection = Arrays.asList(objects);             System.out.println(collection);         }         if (ints instanceof int[]) {              Collection collection1 = Arrays.asList(ints);             System.out.println(collection1);         }     }  
  • 效果:

 

 

  • 对于八大基本类型数据我们就可以这样干
          if (items instanceof int[]) {             int[] ints = (int[]) items;             collection = new ArrayList();             for (int anInt : ints) {                 collection.add(anInt);             }                      }         //......这里还要写7个  
  • JSTL的forEach标签类似就是这样干的

 

由于JDK5的新特性,我们又有另外的解决方案,Class对象能够判断是否为数组类,reflect反射包下Array类

  • 其实,无论Map集合、还是任何类型的数组、都可以使用Colletion进行遍历!。

  • 如果是八大基本数据类型的数组,我们就使用反射来进行构建出Collection集合。

  • 标签处理器的代码

 public class Demo1 extends SimpleTagSupport {      //遍历的是未知的集合或数组,定义成Object     private Object items;      //每次被遍历的对象存储关键字     private String var;      //Colletion     private Collection collection;      //在WEB容器设置标签的属性的时候,判断是什么类型的数组和集合     public void setItems(Object items) {         this.items = items;          //如果items是Collection类型的,就强转为Colletion         if (items instanceof Collection) {             collection = (Collection) items;         }          //如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的!         if (items instanceof Map) {             Map map = (Map) items;             collection = (Collection) map.entrySet();         }          //可以这样解决,Class对象判断是否是一个数组类         if (items.getClass().isArray()) {              //创建Collection集合添加数组的元素!             collection = new ArrayList();              //再利用reflect包下的Array类获取到该数组类的长度             int len = Array.getLength(items);              //遍历并添加到集合中             for (int i = 0; i < len; i++) {                 collection.add(Array.get(items, i));             }         }     }      public void setVar(String var) {         this.var = var;     }     @Override     public void doTag() throws JspException, IOException {          //获取到代表标签体内容的对象         JspFragment jspFragment = this.getJspBody();         Iterator iterator = collection.iterator();           //遍历集合         while (iterator.hasNext()) {             Object o = iterator.next();              //把遍历出来的对象存储到page范围中(在标签体中使用EL表达式${var},就能够获取到集合的对象了!)             this.getJspContext().setAttribute(var, o);              jspFragment.invoke(null);         }     } }  
  • tld文件和上面是一样的,下面是测试代码
  <%     /*list集合*/     List list = new ArrayList();     list.add("zhongfucneng");     list.add("1");     list.add("2");     list.add("3");     request.setAttribute("list",list);      /*基本数据类型数组*/     int[] ints = new int[]{1, 2, 3, 4, 5};     request.setAttribute("ints", ints);      /*对象数组*/     Object[] objects = new Object[]{2, 3, 4, 5, 6};     request.setAttribute("objects", objects);      /*map集合*/     Map map = new HashMap();     map.put("aa", "aa");     map.put("bb", "bb");     map.put("cc", "cc");     request.setAttribute("map",map); %>  	List集合: 	<zhongfucheng:forEach items="${list}" var="str"> 	    ${str} 	</zhongfucheng:forEach> 	 	<hr> 	<hr> 	基本数据类型数组: 	<zhongfucheng:forEach items="${ints}" var="i"> 	    ${i} 	</zhongfucheng:forEach> 	 	<hr> 	<hr> 	对象数组: 	 	<zhongfucheng:forEach items="${objects}" var="o"> 	    ${o} 	</zhongfucheng:forEach> 	 	<hr> 	<hr> 	 	map集合: 	<zhongfucheng:forEach items="${map}" var="me"> 	    ${me.key} = ${me.value} 	</zhongfucheng:forEach>    
  • 效果:

 

 

HTML转义标签

要开发这个标签就很简单了,只要获取到标签体的内容,再通过经过方法转义下标签体内容,输出给浏览器即可

  • 标签处理器代码:
public class Demo1 extends SimpleTagSupport {       @Override     public void doTag() throws JspException, IOException {         //获取到标签体的内容再修改        StringWriter stringWriter = new StringWriter();        JspFragment jspFragment = this.getJspBody();        jspFragment.invoke(stringWriter);        String content = stringWriter.toString();         //经过filter()转义,该方法在Tomcat可以找到        content = filter(content);         //再把转义后的内容输出给浏览器        this.getJspContext().getOut().write(content);       }      private String filter(String message) {          if (message == null)             return (null);          char content[] = new char[message.length()];         message.getChars(0, message.length(), content, 0);         StringBuffer result = new StringBuffer(content.length + 50);         for (int i = 0; i < content.length; i++) {             switch (content[i]) {                 case '<':                     result.append("&lt;");                     break;                 case '>':                     result.append("&gt;");                     break;                 case '&':                     result.append("&amp;");                     break;                 case '"':                     result.append("&quot;");                     break;                 default:                     result.append(content[i]);             }         }          return (result.toString());     } }   
  • 测试代码
	 	<zhongfucheng:filter><a href="2.jsp">你好啊</a> </zhongfucheng:filter> 	 	<br> 	<a href="2.jsp">你好啊   
  • 效果:

 

 

if else标签

在JSTL中并没有if else的标签,JSTL给予我们的是choose,when,otherwise标签,现在我们模仿choose,when,otherwise开发标签

思路:when标签有个test属性,但otherwise怎么判断标签体是执行还是不执行呢?这时就需要choose标签的支持了!choose标签默认定义一个Boolean值为false,。当when标签体被执行了,就把Boolean值变成true,只要Boolean值为false就执行otherwise标签体的内容。

看程序就容易理解上面那句话了:

  • choose标签处理器
 	public class Choose extends SimpleTagSupport { 	 	    private boolean flag; 	 	    @Override 	    public void doTag() throws JspException, IOException { 	 	        this.getJspBody().invoke(null); 	    } 	 	    public boolean isFlag() { 	        return flag; 	    } 	 	    public void setFlag(boolean flag) { 	        this.flag = flag; 	    } 	}   
  • When标签处理器
 	public class When extends SimpleTagSupport { 	 	    private boolean test ; 	 	    @Override 	    public void doTag() throws JspException, IOException { 	 	 	        Choose choose = (Choose) this.getParent(); 	 	        //如果test为true和flag为false,那么执行该标签体 	        if (test == true && choose.isFlag() == false) { 	 	            this.getJspBody().invoke(null); 	 	            //修改父标签的flag 	            choose.setFlag(true); 	        } 	 	    } 	    public void setTest(boolean test) { 	        this.test = test; 	    } 	}  
  • OtherWise标签处理器
  	public class OtherWise extends SimpleTagSupport { 	 	 	    @Override 	    public void doTag() throws JspException, IOException { 	 	 	        Choose choose = (Choose) this.getParent(); 	 	        //如果父标签的flag为false,就执行标签体(如果when标签没执行,flag值就不会被修改!when标签没执行,就应该执行otherwise标签!) 	        if (choose.isFlag() == false) { 	            getJspBody().invoke(null); 	 	            //改父标签的flag为false 	            choose.setFlag(true); 	 	        } 	    } 	}   
  • 测试代码:
 	<zhongfucheng:choose> 	    <zhongfucheng:when test="${user!=null}"> 	        user为空 	    </zhongfucheng:when> 	 	    <zhongfucheng:otherwise> 	        user不为空 	    </zhongfucheng:otherwise> 	 	</zhongfucheng:choose> 

 

  • 效果:

 

 

DynamicAttribute接口

此接口的主要功能是用于完成动态属性的设置!前面我们讲解属性标签的时候,属性都是写多少个,用多少个的。现在如果我希望属性可以动态的增加,只需要在标签处理器类中实现DynamicAttribute接口即可!

现在我要开发一个动态加法的标签

  • 标签处理器
 	public class Demo1 extends SimpleTagSupport implements DynamicAttributes { 	 	    //既然有动态属性和动态的值,那么我们就用一个Map集合存储(1-1对应的关系),做的加法运算,值为Double类型的。 	    Map<String, Double> map = new HashMap<>(); 	 	    @Override 	    public void doTag() throws JspException, IOException { 	 	        //定义一个sum变量用于计算总值 	        double sum = 0.0; 	 	        //获取到Map集合的数据 	        Iterator iterator = map.entrySet().iterator(); 	        while (iterator.hasNext()) { 	            Map.Entry<String, Double> entry = (Map.Entry<String, Double>) iterator.next(); 	 	            sum += entry.getValue(); 	        } 	 	        //向浏览器输出总和是多少 	        this.getJspContext().getOut().write(String.valueOf(sum)); 	    } 	 	 	    //对于这个要实现的方法,我们只要关注第2个参数和第3个参数即可 	    //第二个参数表示的是动态属性的名称,第三个参数表示的是动态属性的值 	    @Override 	    public void setDynamicAttribute(String s, String localName, Object value) throws JspException { 	 	        //将动态属性的名字和值加到Map集合中 	        map.put(localName, Double.valueOf(Float.valueOf(value.toString()))); 	    } 	}  
  • tld文件,注意要把dynamic-attribute设置为true
      <tag>         <name>dynamicAttribute</name>         <tag-class> tag.Demo1</tag-class>         <body-content> empty</body-content>          <!--这个必须要设置为true-->         <dynamic-attributes>true</dynamic-attributes>     </tag>  
  • 测试代码
 	<zhongfucheng:dynamicAttribute num="1.1" num2="2.2" num3="1"/>  
  • 效果,double在运算的时候会丢失精度的,现在只是测验下动态属性,这里就不详细说了!

 

 

开发自定义函数

至于怎么开发自定义函数,在EL表达式的博客中有

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章的同学,可以关注微信公众号:Java3y

 

本文发表于2018年02月07日 10:31
(c)注:本文转载自https://my.oschina.net/u/3777556/blog/1619444,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1873 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1