Beetl模板创建自定义标签来仿造jsp 中<c:import>功能

现在在用beetl模板渲染画面,有时需要加载其他模板的内容,但是又不想使用beetl模板里的内嵌其他模板方式,想直接通过url请求的方式进行渲染,所以就扒一下c:import的源码改造成beetl的自定义标签,对于我这种扒代码的行为,我想说我站在了前辈的肩膀上,大家不要喷,哈哈


目的是要在btl模板文件中使用如下标签就可在画面渲染时将此url渲染的画面加载到当前画面中。
image.png

那么开始搞代码

首先在resouces目录下创建beetl.properties,在程序启动时,beetl模板会自动加载其中内容。

# 自定义配置
DELIMITER_STATEMENT_START=<%
DELIMITER_STATEMENT_END=%>

TAG.liyi.import=com.xxx.xxx.xxx.tag.beetl.BeetlImportTag

这里定义好Tag的名称以及扫描类的位置

然后在相应包下面创建对应的类BeetlImportTag,在这里我们直接继承beetl的GeneralVarTagBinding类。
继承此类是因为我们可以用getAttributeValue方法直接获取到url
image.png

然后就是核心了,使用什么方式可将发送内部url请求渲染画面,答案就是RequestDispatcher,使用dispatcher.include(request, response);方法即可实现请求,小伙伴们可以百度此方法详细了解下~

直接贴上具体代码

/**
 * 自定义btl的import标签 (仿制c:import)
 * 
 * @author li_yi_neu
 */
public class BeetlImportTag extends GeneralVarTagBinding {

    public static final String VALID_SCHEME_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";

    public static final String DEFAULT_ENCODING = "ISO-8859-1";

    private String url; // 'url' attribute
    private boolean isAbsoluteUrl; // is our URL absolute?
    private String varReader; // 'varReader' attribute
    private String charEncoding; // 'charEncoding' attrib.
    private ParamManager params; // parameters
    private String urlWithParams; // URL with parameters, if applicable
    private String context; // 'context' attribute

    private ServletRequest request;
    private ServletResponse response;

    private void init() {
        url = varReader = context = charEncoding = urlWithParams = null;
        params = SpringBootUtil.getBean(ParamManager.class);
    }

    @Override
    public void render() {

        init();

        url = (String) getAttributeValue("url");

        if (request == null) {
            request = BootServletUtils.getRequest();
        }
        if (response == null) {
            response = BootServletUtils.getResponse();
        }

        if (url == null || url.equals("")) {
            throw new BeetlTagException("error");
        }

        isAbsoluteUrl = isAbsoluteUrl();

        try {
            if (varReader == null) {
                String str = acquireString();
                ctx.byteWriter.writeString(str);
            }
        }
        catch (IOException ex) {
            throw new BeetlTagException(ex.toString(), ex);
        }

    }

    private boolean isAbsoluteUrl() throws BeetlTagException {
        return isAbsoluteUrl(url);
    }

    public static boolean isAbsoluteUrl(
        String url) {
        // a null URL is not absolute, by our definition
        if (url == null)
            return false;

        // do a fast, simple check first
        int colonPos;
        if ((colonPos = url.indexOf(":")) == -1)
            return false;

        // if we DO have a colon, make sure that every character
        // leading up to it is a valid scheme character
        for (int i = 0; i < colonPos; i++)
            if (VALID_SCHEME_CHARS.indexOf(url.charAt(i)) == -1)
                return false;

        // if so, we've got an absolute url
        return true;
    }

    private String acquireString() throws IOException, BeetlTagException {

        if (isAbsoluteUrl) {
            // for absolute URLs, delegate to our peer
            BufferedReader r = new BufferedReader(acquireReader());
            StringBuffer sb = new StringBuffer();
            int i;

            // under JIT, testing seems to show this simple loop is as fast
            // as any of the alternatives
            // 
            // gmurray71 : putting in try/catch/finally block to make sure the
            // reader is closed to fix a bug with file descriptors being left open
            try {
                while ((i = r.read()) != -1)
                    sb.append((char) i);
            }
            catch (IOException iox) {
                throw iox;
            }
            finally {
                r.close();
            }

            return sb.toString();
        }
        else {
            // handle relative URLs ourselves

            // URL is relative, so we must be an HTTP request
            if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse))
                throw new BeetlTagException("error");

            // retrieve an appropriate ServletContext
            ServletContext c = null;
            String targetUrl = targetUrl();
            if (context != null)
                c = request.getServletContext().getContext(context);
            else {
                c = request.getServletContext();

                // normalize the URL if we have an HttpServletRequest
                if (!targetUrl.startsWith("/")) {
                    String sp = ((HttpServletRequest) request).getServletPath();
                    targetUrl = sp.substring(0, sp.lastIndexOf('/')) + '/' + targetUrl;
                }
            }

            if (c == null) {
                throw new BeetlTagException();
            }

            // from this context, get a dispatcher
            RequestDispatcher rd = c.getRequestDispatcher(stripSession(targetUrl));

            if (rd == null)
                throw new BeetlTagException();

            // include the resource, using our custom wrapper

            //ImportResponseWrapper irw = new ImportResponseWrapper();

            // spec mandates specific error handling form include()
            try {
                rd.include(request, response);
                //rd.include(request, irw);
            }
            catch (Exception ex) {
                throw new BeetlTagException(ex);
            }

            // disallow inappropriate response codes per JSTL spec

            // recover the response String from our wrapper
            return "";
        }

    }

    private Reader acquireReader() throws IOException, BeetlTagException {
        if (!isAbsoluteUrl) {
            // for relative URLs, delegate to our peer
            return new StringReader(acquireString());
        }
        else {
            // absolute URL
            String target = targetUrl();
            try {

                // handle absolute URLs ourselves, using java.net.URL
                URL u = new URL(target);
                URLConnection uc = u.openConnection();
                InputStream i = uc.getInputStream();

                // okay, we've got a stream; encode it appropriately
                Reader r = null;
                String charSet;
                if (charEncoding != null && !charEncoding.equals("")) {
                    charSet = charEncoding;
                }
                else {
                    // charSet extracted according to RFC 2045, section 5.1
                    String contentType = uc.getContentType();
                    if (contentType != null) {
                        charSet = getContentTypeAttribute(contentType, "charset");
                        if (charSet == null)
                            charSet = DEFAULT_ENCODING;
                    }
                    else {
                        charSet = DEFAULT_ENCODING;
                    }
                }
                try {
                    r = new InputStreamReader(i, charSet);
                }
                catch (Exception ex) {
                    r = new InputStreamReader(i, DEFAULT_ENCODING);
                }

                // check response code for HTTP URLs before returning, per spec,
                // before returning
                if (uc instanceof HttpURLConnection) {
                    int status = ((HttpURLConnection) uc).getResponseCode();
                    if (status < 200 || status > 299)
                        throw new BeetlTagException(status + " " + target);
                }
                return r;

            }
            catch (IOException ex) {
                throw new BeetlTagException();
            }
            catch (RuntimeException ex) { // because the spec makes us
                throw new BeetlTagException();
            }
        }
    }

    private String targetUrl() {
        if (urlWithParams == null)
            urlWithParams = params.aggregateParams(url);
        return urlWithParams;
    }

    public static String stripSession(
        String url) {
        StringBuffer u = new StringBuffer(url);
        int sessionStart;
        while ((sessionStart = u.toString().indexOf(";jsessionid=")) != -1) {
            int sessionEnd = u.toString().indexOf(";", sessionStart + 1);
            if (sessionEnd == -1)
                sessionEnd = u.toString().indexOf("?", sessionStart + 1);
            if (sessionEnd == -1) // still
                sessionEnd = u.length();
            u.delete(sessionStart, sessionEnd);
        }
        return u.toString();
    }

    public static String getContentTypeAttribute(
        String input,
        String name) {
        int begin;
        int end;
        int index = input.toUpperCase().indexOf(name.toUpperCase());
        if (index == -1)
            return null;
        index = index + name.length(); // positioned after the attribute name
        index = input.indexOf('=', index); // positioned at the '='
        if (index == -1)
            return null;
        index += 1; // positioned after the '='
        input = input.substring(index).trim();

        if (input.charAt(0) == '"') {
            // attribute value is a quoted string
            begin = 1;
            end = input.indexOf('"', begin);
            if (end == -1)
                return null;
        }
        else {
            begin = 0;
            end = input.indexOf(';');
            if (end == -1)
                end = input.indexOf(' ');
            if (end == -1)
                end = input.length();
        }
        return input.substring(begin, end).trim();
    }

}

这里还需要另外两个辅助类ParamManager和SpringBootUtil

@Component
public class ParamManager {

    //*********************************
    // Private state

    private List names = new LinkedList();
    private List values = new LinkedList();
    private boolean done = false;

    //*********************************
    // Public interface

    /** Adds a new parameter to the list. */
    public void addParameter(
        String name,
        String value) {
        if (done)
            throw new IllegalStateException();
        if (name != null) {
            names.add(name);
            if (value != null)
                values.add(value);
            else
                values.add("");
        }
    }

    public String aggregateParams(
        String url) {
        /*
         * Since for efficiency we're destructive to the param lists, we don't
         * want to run multiple times.
         */
        done = true;

        //// reverse the order of our two lists
        // Collections.reverse(this.names);
        // Collections.reverse(this.values);

        // build a string from the parameter list 
        StringBuffer newParams = new StringBuffer();
        for (int i = 0; i < names.size(); i++) {
            newParams.append(names.get(i) + "=" + values.get(i));
            if (i < (names.size() - 1))
                newParams.append("&");
        }

        // insert these parameters into the URL as appropriate
        if (newParams.length() > 0) {
            int questionMark = url.indexOf('?');
            if (questionMark == -1) {
                return (url + "?" + newParams);
            }
            else {
                StringBuffer workingUrl = new StringBuffer(url);
                workingUrl.insert(questionMark + 1, (newParams + "&"));
                return workingUrl.toString();
            }
        }
        else {
            return url;
        }
    }

}
/**
 * springboot工具类
 * 
 * @author li_yi_neu
 * @version 1.0 May 25, 2020
 */
@Component
public class SpringBootUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(
        ApplicationContext applicationContext) throws BeansException {
        if (SpringBootUtil.applicationContext == null) {
            SpringBootUtil.applicationContext = applicationContext;
        }
    }

    //获取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通过name获取 Bean.
    public static Object getBean(
        String name) {
        return getApplicationContext().getBean(name);
    }

    //通过class获取Bean.
    public static <T> T getBean(
        Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(
        String name,
        Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    /**
     * 获取当前Request
     * 
     * @return HttpServletRequest
     */
    public static HttpServletRequest getHttpServletRequest() {
        if ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes() != null) {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
        else {
            return null;
        }
    }

}

以上就是完整的东西了,大家可以试一试,我觉得这种方式的加载有时还是挺好用的~


Start_liyi
29 声望3 粉丝

I'm 小兵