Jasper

3.1 Jasper简介

对于基于JSP的web应用来说,我们可以直接在JSP页面中编写Java代码,添加第三方的标签库,以及使用EL表达式。但是无论经过何种形式的处理,最终输出到客户端的都是标准的HTML页面(包含js,css…),并不包含任何的Java相关的语法。也就是说,我们可以把jsp看做是一种运行在服务端的脚本。那么服务器是如何将jsp页面转换为HTML页面的呢?

Jasper模块是Tomcat的JSP核心引擎,我们知道JSP本质是一个Servlet。Tomcat使用Jasper对JSP语法进行解析,生成Servlet并生成Class字节码,用户在进行访问jsp时,会访问Servlet,最终将访问的结果直接响应在浏览器端。另外,在运行的时候,Jasper还是会检测JSP文件是否修改,如果修改该,则会重新编译JSP文件。

3.2 JSP编译方式

3.2.1 运行时编译

Tomcat并不会在启动Web应用的时候自动编译JSP文件,而是在客户端第一次请求时,才编译需要访问的JSP文件。

创建一个web项目,并编写JSP代码:

<%@ page import="java.text.DateFormat" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<%
DateFormat dateFormat = new SimpleDateFormat("yyyy‐MM‐dd
HH:mm:ss");
String format = dateFormat.format(new Date());
%>
Hello , Java Server Page 。。。。
<br/>
<%= format %>
</body>
</html>
3.2.1.1编译过程

Tomcat在默认的web.xml中配置了一个org.apache.jasper.servlet.JspServlet,用于处理所有的.jsp或.jspx结尾的请求,该Servlet实现即时运行时编译的入口。
在这里插入图片描述
JspServlet处理流程图:
在这里插入图片描述

3.2.1.2 编译结果

1)如果在tomcat/conf/web.xml中配置了参数scratchdir,则jsp编译后的结果,就会存储在该目录下。
在这里插入图片描述
2)如果没有配置该选项,则会将编译后的结果,存储在Tomcat安装目录下的work/Catalina(Engine名称)/localhost(Host名称)/Context名称。

3)如果使用的时IDEA开发工具集成Tomcat访问web工程中的jsp,编译后的结果,存放在:

C:\Users\Administrator\.IntelliJIdea2019.1\system\tomcat\_project_tomcat\work\Catalina\localhost\jsp_demo_01_war_exploded\org\apache\jsp

3.2.2 预编译

除了运行时编译,我们还可以直接在Web应用启动时,一次性将Web应用中的所有的JSP页面一次性编译完成。在这种情况下,Web应用运行过程中,便可以不必再进行实时编译,而是直接调用JSP页面对应的Servlet完成请求处理,从而提升系统性能。

Tomcat提供了一个Shell程序JspC,用于支持JSP预编译,而且再Tomcat的安装目录下提供了一个catalina-task.xml文件声明了Tomcat支持的Ant任务,因此,我们很容易使用Ant来执行JSP预编译。(要想使用这种方式,必须的确保在此之前已经下载并安装了Apache Ant)。

3.3JSP编译原理

3.3.1代码分析

编译后的.class字节码文件及源码:

public final class index_jsp extends
org.apache.jasper.runtime.HttpJspBase
implements
org.apache.jasper.runtime.JspSourceDependent,org.apache.jasper.runtime.JspSourceImports {
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long>
_jspx_dependants;
static {
_jspx_dependants = new
java.util.HashMap<java.lang.String,java.lang.Long>(2);
_jspx_dependants.put("jar:file:/D:/DevelopProgramFile/apache‐tomcat‐
8.5.42‐windows‐x64/apache‐tomcat‐8.5.42/webapps/jsp_demo_01/WEB‐
INF/lib/standard.jar!/META‐INF/c.tld", Long.valueOf(1098682290000L));
_jspx_dependants.put("/WEB‐INF/lib/standard.jar",
Long.valueOf(1490343635913L));
} 
private static final java.util.Set<java.lang.String>
_jspx_imports_packages;
private static final java.util.Set<java.lang.String>
_jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = new java.util.HashSet<>();
_jspx_imports_classes.add("java.util.Date");
_jspx_imports_classes.add("java.text.SimpleDateFormat");
_jspx_imports_classes.add("java.text.DateFormat");
} 
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager
_jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
} 
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
} 
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
} 
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory =
_jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
} 
return _el_expressionfactory;
} 
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager =
org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
} 
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest
request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) &&!"HEAD".equals(_jspx_method) &&
!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType()))
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPsonly permit GET POST or HEAD");
return;
} 
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html;charset=UTF‐8");
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\n");
out.write("\n");
out.write("\n");
out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" ");
DateFormat dateFormat = new SimpleDateFormat("yyyy‐MM‐ddHH:mm:ss");
String format = dateFormat.format(new Date());
out.write("\n");
out.write(" Hello , Java Server Page 。。。。\n");
out.write("\n");
out.write(" <br/>\n");
out.write("\n");
out.write(" ");
out.print( format );
out.write("\n");
out.write("\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null)
_jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

由编译后的源码解读,可以分析出以下几点:

1)其类名为index_jsp,继承自org.apache.jasper.runtime,HttpJspBase,该类是HttpServlet的子类,所以jsp本质就是一个Servlet。

2)通过属性_jspx_dependant保存了当前JSP页面依赖的资源,包含引入的外部的JSP页面、导入的标签、标签所在的jar包等,便于后续处理过程中使用(如重新编译检测,因此它以Map形式保存了每个资源的上次修改时间)。

3)通过属性_jsp_imports_packages存放导入的Java包,默认导入Javax.servlet,javax.servlet.http,javax.servlet.jsp。

4)通过属性__jspx_imports_classes存放导入的类,通过import指令导入的DateFormat,SimpleDateFormat、Date都会包含在该集合中。

_jspx_imports_packages和__jspx_imports_classes属性主要用于配置EL引擎上下文。

5)请求处理方法_jspService完成,而在父类HttpJspBase中的service方法通过模板方法模式,调用了子类的__jspService方法。
在这里插入图片描述
6)_jspService方法中定义了介个重要的局部变量:pageContext、Session、application、config、out、page。由于整个页面的输出有__jspService方法完成,因此这些变量和参数会对整个JSP页面生效。这也是我们为什么可以在JSP页面使用这些变量的原因。

7)指定文档类型的指令(page)最终转换为response.setContentType()方法调用。

8)对于每一行的静态内容(HTML),调用out.write输出。

9)对于<%…%>中的Java代码,将直接转换为Servlet类中的代码。如果在Java代码中嵌入了静态文件,则同样调用out.write输出。

3.3.2 编译流程

JSP编译过程如下:
在这里插入图片描述
Compiler编译工作主要包含代码生成和编译两部分:

代码生成

1)Compiler通过一个PageInfo对象保存JSP页面编译过程中的各种配置,这些配置可能来源于Web应用初始化参数,也可能来源于JSP页面的指令配置(如page,include)。

2)调用ParseController解析指令节点,验证其是否合法,同时将配置信息保存到PageInfo中,用于控制代码生成。

3)调用ParseController解析整个页面,由于JSP是逐行解析,所以对于每一行会创建一个具体的Node对象。如静态文本(TemplateText)、Java代码(Script)、定制标签(CustomTag)、Include指令(IncludeDirective)。

4)验证除指令外其它所有节点的合法性,如脚本、定制标签、EL表达式等。

5)收集除指令外其它节点的页面配置信息。

6)编译并加载当前JSP页面依赖的标签。

7)对于JSP页面的EL表达式,生成对应的映射函数。

8)生成JSP页面对应的Servlet类源码

编译

代码生成完后,Compiler还会生成SMAP信息。如果配置生成SMAP信息,Compiler则会在编译阶段将SMAP信息保存到class文件中。

在编译阶段,Compiler的两个是实现AntCompiler和JDTCompiler分别调用相关框架的API进行源码编译。

对于AntCompiler来说,构造一个Ant的Javac的任务完成编译。

对于JDTCompiler来说,调用org.eclipse.jdt.internal.compiler.Compiler完成编译。

08-08
JasperReports 是一个广泛使用的开源报表工具,它允许开发者将数据以美观的格式呈现,适用于多种应用场景,包括企业级报表生成。JasperReports 支持多种数据源,并能够将报表导出为 PDF、HTML、Excel、CSV 等格式,具备强大的可扩展性和灵活性。 在使用 JasperReports 时,通常会涉及到几个关键步骤:设计报表模板(.jrxml 文件)、编译模板为 .jasper 文件、填充数据、以及最终的报表渲染。Spring 框架通过 `JasperReportsUtils` 类简化了这些流程,尤其是在非 Web 应用中,使得开发者可以更便捷地完成报表的编译、数据填充和输出[^1]。 在设计报表时,iReport 是一个常用的图形化设计工具,用于创建和编辑 .jrxml 文件。然而,需要注意的是,不同版本的 JasperReports 编译出的 .jasper 文件可能不兼容。例如,使用 0.6.1 版本编译的文件在 0.6.4 版本中运行可能会导致错误。因此,建议仅使用 iReport 进行报表模板的设计,而不进行编译操作,而是在应用程序中动态编译 .jrxml 文件以确保兼容性[^2]。 ### Jasper AI 的功能介绍 尽管用户提到了“Jasper AI”,但目前没有明确的与 JasperReports 直接相关的 AI 产品称为“Jasper AI”。不过,如果我们将“Jasper AI”理解为 JasperReports 的智能化扩展或集成 AI 功能的报表系统,那么可以设想其可能具备以下特性: - **智能数据分析与可视化**:通过内置的机器学习算法,自动识别数据中的趋势和异常,并生成相应的可视化图表。 - **自然语言查询**:支持用户使用自然语言输入查询条件,系统自动解析并生成对应的报表。 - **自动生成报表**:基于历史数据和业务规则,AI 可以预测用户需求,自动生成并推送关键报表。 - **智能推荐**:根据用户的行为和偏好,推荐最合适的报表模板或数据展示方式。 - **自动化排版与样式优化**:AI 可以根据内容自动调整报表布局,确保视觉效果最佳。 ### 示例代码:使用 JasperReports 生成 PDF 报表 以下是一个简单的 Java 示例,展示如何使用 JasperReports 生成 PDF 报表: ```java import net.sf.jasperreports.engine.*; import net.sf.jasperreports.view.JasperViewer; import java.util.HashMap; import java.util.Map; public class JasperReportExample { public static void main(String[] args) { try { // 加载编译好的.jasper文件 String reportPath = "path/to/your/report.jasper"; // 创建参数对象 Map<String, Object> parameters = new HashMap<>(); parameters.put("ReportTitle", "Sample Report"); // 填充数据并生成报表 JasperPrint jasperPrint = JasperFillManager.fillReport(reportPath, parameters, new JREmptyDataSource()); // 查看报表 JasperViewer.viewReport(jasperPrint, false); } catch (JRException e) { e.printStackTrace(); } } } ``` 上述代码展示了如何加载一个预编译的 `.jasper` 文件,填充参数,并生成 PDF 报表。JasperReports 还支持多种数据源,如 JDBC、JavaBeans、XML 等,开发者可以根据实际需求选择合适的数据源类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值