Java EE 的核心技术规范(介绍)

Java EE 的核心技术规范(介绍)

Java Servlet(Server Applet)

Servlet 称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态web内容。 这个规范可以说是javaEE最核心的一个规范,是web服务的基础,用它就可以实现web服务功能。接下来介绍下该接口的使用以及现在在spring mvc框架下的使用。

简单看下Servlet接口提供的几个方法

public interface Servlet {

/*该servlet初始化时调用,默认首次访问时初始化,可通过web.xml load-on-startup控制初始化时机,注意,同一个servlet类,配置n个servlet,会被初始化n次,从此点可以看出Servlet是单例模式的一般用于在初始化时获取web.xml内servlet节点下init-param参数,或者一些web公共参数Called by the servlet container to indicate to a servlet that the servlet is being placed into service.*/void init(ServletConfig config);/*返回服务器相关参数信息,具体可参考ServletConfigReturns a ServletConfig object, which contains initialization and startup parameters for this servlet.*/ServletConfig getServletConfig();/*基本不用,按照sun规范是应该返回servlet的作者,版本,版权等相关信息Returns information about the servlet, such as author, version, and copyright.*/String getServletInfo();/*servlet核心方法,当web容器接收到请求后会调用该方法,通过该方法即可实现HTTP1.1规范的8种请求方法一般使用时直接继承自javax.servlet.http.HttpServlet类,重写要处理的HTTP请求方法即可,例如GET,POST,HEAD等方法Called by the servlet container to allow the servlet to respond to a request.*/void service(ServletRequest req, ServletResponse res);/*一般情况下是当web服务器退出时调用该方法,一般用于非java管理的相关资源释放。如:Jdbc连接池。Called by the servlet container to indicate to a servlet that the servlet is being taken out of service.*/void destroy();

}

样例:web.xml

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">

hello-world

org.example.TestServlet

param

hello-world

1

hello-world

/hello-world

hello-world-2

org.example.TestServlet

param

hello-world-2

hello-world-2

/hello-world-2

基本上来讲就是这个样子了,具体的实现在不同的web服务器之间可能会略有差异,但接口一定是保持一致的。因为这个只是一个介绍性资料,所以只是简单的介绍了servlet的生命周期,主要的service方法,略过了filter,request,response,ServletContext,cookie,session 等相关资料 。

Spring MVC 框架结构图,spring官网扣出来的哈。

controller:这个即为日常中需要开发的控制器,Front controller: DispatcherServlet 就是一个servlet实现类,只不过现在不在service方法中直接做业务处理,而是增加了一层controller,通过请求路径与注解配置实现之间的映射,把实际的业务转发给controller,业务完成后返回modelAndView给前置控制器。

Spring MVC 有什么好处呢? 框架么,一般来讲都是帮我们简化了开发,提供了一系列方便好用的工具,以及强大的灵活性,使工程师可以更专注于业务开发,提高开发效率。如:原始的Servlet需要每个资源都需要创建一个servlet类,需要手工处理请求的参数解析,参数校验等繁琐操作,还有就是原始的Servlet生命周期是在servlet容器的掌管之下,而controller可以使用spring容器,由spring管理类的创建销毁,业务service的注入等。

JDBC(Java Database Connectivity)

jdbc是用来与数据库建立连接的,提供了操作数据库的各种接口,jdbc设计了统一的数据库连接规范,以分层的思想屏蔽了不同数据库厂商之间的差异,使程序人员可以更专注于实际业务功能的实现。数据库有什么作用?略过……,简单看下jdbc的使用吧。

普通拼接SQL

public class TestServlet extends HttpServlet {

//旧版本jdbc驱动--com.mysql.jdbc.Driver

private static final String DRIVER_CLASS_NAME ="com.mysql.cj.jdbc.Driver";

private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false";

private static final String NAME = "root";

private static final String PSD = "root";

public void service(ServletRequest req, ServletResponse res){

String name = req.getParameter("name");

String age = req.getParameter("age");

String sex = req.getParameter("sex");

Connection conn = null;

try {

//注册数据库驱动

Class.forName(DRIVER_CLASS_NAME);

//获取数据库连接

conn = DriverManager.getConnection(URL, NAME, PSD);

} catch (ClassNotFoundException e) {

e.printStackTrace();

writerResponse(res, "未提供对应的数据库驱动:" + DRIVER_CLASS_NAME);

return;

} catch (SQLException e) {

e.printStackTrace();

writerResponse(res, "获取数据库连接发生异常!");

return;

}

//存在sql注入--不应该使用

String sql = "INSERT INTO student (name, age, sex) VALUES ('" + name + "', " + age + ",'" + sex + "')";

try (Statement statement = conn.createStatement();){

//该方式无法获取insert结果 result 为false

//boolean result = statement.execute(sql);

//result为null

//ResultSet resultSet = statement.getResultSet();

//获取执行成功条数

int row = statement.executeUpdate(sql);

writerResponse(res, 1==row);

return;

} catch (SQLException e) {

e.printStackTrace();

writerResponse(res, "执行sql发生异常!" + DRIVER_CLASS_NAME);

return;

} finally {

//关闭数据库连接

if(null != conn) {

try {

conn.close();

} catch (SQLException e) {

e.printStackTrace();

}

}

}

}

private void writerResponse(ServletResponse res, Object msg){

try {

res.setCharacterEncoding("utf-8");

res.getWriter().print(msg);

} catch (IOException e) {

e.printStackTrace();

}

}

}

上边的代码演示了一个添加学生的功能,从request中获取请求参数,拼接sql然后执行,最后返回执行结果。此方式存在SQL注入,不应该使用该方式,不应该使用该方式,不应该使用该方式,重要的事情说三遍。

SQL注入问题:SQL注入是什么?额…… 请自行百度。

预编译SQL

//解决普通sql拼接产生的sql注入

String prepareSql = "INSERT INTO student (name, age, sex) VALUES (?, ?, ?)";

PreparedStatement pStatement = conn.prepareStatement(prepareSql)

pStatement.setString(1, name);

pStatement.setInt(2, Integer.valueOf(age));

pStatement.setString(3, sex);

int row = pStatement.executeUpdate();

writerResponse(res, 1==row);

预编译sql可以解决普通sql拼接产生的sql注入,使用中不应该再手动拼接不可信来源的数据,不然还是会造成sql注入。还有一点:预编译sql无法正确的处理order by子句,接下来简单看下吧。

order by 子句

基本信息:

数据库版本:mysql 5.7jdbc驱动:

mysql mysql-connector-java 6.0.6测试数据:

SELECT id,name,age FROM student;id,name,age13,李四,1614, 张三,13

测试一:正常测试

//程序编写//String sql = "SELECT id, name, age FROM student ORDER BY" + orderColumn;//实际传参String sql= "SELECT id, name, age FROM student ORDER BY " + "IF(1=1, age, id)";Statement statement = conn.createStatement();ResultSet resultSet = statement.executeQuery(sql);System.out.println(statement.toString());

while (resultSet.next()) { System.out.println(resultSet.getString("id") + ":" + resultSet.getString("name") + ":" + resultSet.getInt("age"));}执行结果一:

SELECT id, name, age FROM student ORDER BY IF(1=1, age, id)14:张三:1313:李四:16

执行结果二:

SELECT id, name, age FROM student ORDER BY IF(1=2, age, id)13:李四:1614:张三:13

结果说明:

此时存在sql注入

orderColumn 可以修改为其他更有意义的sql,如实现拒绝服务,表名,字段名猜解,数据库用户名,密码猜解等。

测试二:预编译测试

//测试order By 排序问题, 此时可以正常执行,但是数据未进行排序

String prepareSql = "SELECT id, name, age FROM student ORDER BY ?"; //正常拼接

//String prepareSql = "SELECT id, name, age FROM student ORDER BY" + orderColumn;

PreparedStatement pStatement = conn.prepareStatement(prepareSql);

pStatement.setString(1, "IF(1=1, age, id)");

ResultSet resultSet = pStatement.executeQuery();

System.out.println(pStatement.toString());

while (resultSet.next()) {

System.out.println( resultSet.getString("id") + ":" + resultSet.getString("name") + ":" + resultSet.getInt("age"));

}

/*

测试结果:

com.mysql.cj.jdbc.PreparedStatement@598c19fb: SELECT id, name, age FROM student ORDER BY 'IF(1=1, age, id)'

13:李四:16

14:张三:13

*/结果说明:可以防止SQL注入,但同时也使order by 无法生效。如果想生效只能把占位符 “?” 修改成字符串拼接方式。

对于两种测试结果的说明:如果需要进行排序时只能使用SQL拼接方式,如何避免SQL注入了,不要使用外部不可信来源的参数,可以事先以枚举类型的名字进行约定,取到参数时进行枚举类型转换,转换成功传入,不成功返回错误。

数据连接池在计算机性能飞速发展今天,程序执行速度一般不再是性能问题的主要原因,磁盘IO、网络IO的性能问题越来越突出。在上边的示例代码中,每一次的servlet请求处理中都创建了一个新的connection连接,使用后把连接给关闭,这是一种较为耗时的操作,所以出现了数据连接池,用于提供对数据库连接的管理。使用时从池子中获取,使用后还给池子,以便其他线程可以再次使用,用以提高性能。当前时间2020-1,在16,17年以及之前用的比较多的是c3p0以及dbcp连接池,最近这两年阿里巴巴的Druid(德鲁伊)数据连接池被越来越多的公司所采用的。据说在性能,扩展性等方面都优于其他链接池,同时还提供了了日志监控功能。orm框架(object relation mapping)在上边的代码中可以看出,对于sql的预编译操作,sql返回的结果集处理都是些重复性的工作,所以呢就出现了一些框架帮我们解决这些重复的体力劳动,提高了代码可读性,同时还提供了一些如缓存之类的优化措施,提高开发效率。主要的有遵循sun JPA规范的 hibernate, 以及半orm的mybatis框架,其中hibernate 基本上不需要写sql,但同时灵活性上就差一些,也较为复杂,学习成本较高,而mybatis中是通过mapper中配置sql语句的方式实现,所以较为灵活,可控性更强,同时学习成本低,所以现在mybatis在中国的使用较为广泛。mybatis

简单提一下mybatis两种取值方式的差别:#{},${}, "#" 井号mybatis会把其处理成预编译的方式进行查询,对应着PreparedStatement 方式,而$方式会进行原值拼接,对应Statement方式,所以要注意$取值时的sql注入问题,以及order by取值问题。

JSP(JavaServer Pages)

JSP(全称JavaServer Pages)是由Sun公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。(百科)

jsp本质上还是servlet,还是通过重写service方法来实现的,但是为什么会有jsp这个东西呢?其实主要是为了解决servlet中不便处理返回的动态网页问题,在Servlet时代,想返回一个动态网页,需要在servlet中使用字符串拼接的方式进行返回,书写的代码不易阅读、维护,且在代码编写过程中极易出错,不易验证,只能通过实际运行程序来确认是否正确。所以就出现了jsp技术,通过分离静态网页代码部分与动态数据部分,在一定程度上提高了可读性与减少了无用代码的干扰。

在最初时,jsp中的动态数据部分都是通过直接写java代码的方式实现的,在循环处理数据时,编写的代码就相对繁杂,需要自行处理好html静态代码与java代码的组合问题,代码阅读与维护也是相对困难,所以之后又发展出了EL表达式技术,避免在jsp中直接写java代码,以一种更可读,更易维护的方式来提高开发效率与降低维护成本。

其他的一些模板引擎技术:

Freemarker, Velocity, Thymeleaf。

jsp编译后的源文件位置:tomcat下目录位置:$TOMCAT_HOME\work\Catalina\localhost{域名一般是localhost}\{项目名称}\org\apache\jsp\{文件目录}\{jsp文件名}.java

小结

Servlet,JDBC, JSP可以说是JAVAEE中最核心的三个技术了,一般我们学习javaWeb开发主要都是学习这三种技术,使用他们基本上可以满足各式各样的功能需求,但是在今天为了提高开发效率,提高性能,提高维护性等,我们还要学习一些框架技术作为辅助,例如上边提到的,Spring Mvc, 数据连接池,mybatis,以及很重要但没有提及的 Spring框架,学会这些框架常用功能的使用,了解框架核心的技术思想,基本上可以成为一名入门级的java Web程序员啦。

XML(EXtensible Markup Language)

XML 可扩展标记语言,这个东西是W3C组织设计出来用于数据传输使用的,不太清楚为什么跑进了JAVA EE的技术规范(是因为JAXB的xml注解么?)。XML是一个与编程语言平台无关的技术规范,XML被应用到了很多地方,比如WebService的数据传输,spring,mybatis等框架的配置文件。XML的约束文件:在接口交互之前,需要接口双方对接口交互的数据格式进行约定,而这个约定的文件就是XML的约束文件。xml主要有两种约束方式,一种是DTD(Documnet Type Definition),一种是XSD(XML Schema Definition),DTD主要通过预定义的方式实现的,所以不够灵活,无法进行扩展,所以后来出现了XSD方式,采用XML语言的方式,提供了更高的灵活性,且语意较DTD方式也更为清晰,现在基本上都应该是XSD方式进行约束。

java中的xml解析技术

SAX:JDK提供的简单工具,主要是通过逐行的方式处理xml文件,所以适合大文件的解析,但是据说是有bug。

DOM4j:据说是java中xml解析工具中效率最好的,但是使用上不是特别方便(没用过)。

JAXB:JDK1.7版本中已内置了JAXB实现。jaxb提供了一些相对好用的工具,例如xjc工具可以通过xml的xsd约束文件直接创建出对应javaBean对象。通过通过注解或xml配置文件,实现xml与javaBean对象的互相转换,同时还提供了了对xml数据的有效性校验,性能上好像不如DOM4j。

JSON(JavaScript Object Notation)

json也是一种数据传输的格式规范,但是相较于xml来讲更加轻量级,数据量更小,所以在如今使用的也非常广泛。json因为没有约束文件,所以变动,扩展较为简单,但同时也无法提供详细的语意(节点是否必须存在,节点数量限制,数据的类型约束等)规范。

JMS(Java Message Service)

JMS java消息服务,是软件组件或应用程序之间进行通信的一种方法。Java消息服务是一种Java API,允许应用程序创建,发送,接收和读取消息。JMS API定义了一组通用的接口和关联的语义,这些接口和关联的语义允许以Java编程语言编写的程序与其他消息传递实现进行通信。JMS的主要作用是降低系统之间的耦合度。JMS也是一项技术规范,与JDBC一样屏蔽了不同消息队列(MQ)提供者之间的差异,实现提供者有:weblogic提供了JMS的实现,ActiveMQ,RabbitMQ(通过插件封装的方式支持)。

JMS提的两种模式

P2P(Point to Point)

点对点概念主要在于消息生产者,queue,消息消费者之上,主要是说明,消息生产者将消息发送至特定的队列,有一个特定的消息消费者会去消费队列中的消息,一条消息仅被消费一次。

Topic(publish/subscribe)

发布订阅模式,主要说明同一个消息可以被多个消费者所消费。例如:网页聊天群的实现。

应用场景

流量削峰(超大流量),例如:在抢购业务中,为了避免大量的请求超过服务器处理的能力,可以设置一定长度的消息队列,超出后直接返回错误。

异步,应用解耦(不关心后续业务结果),例如:用户注册后,只要数据保存成功、JMS消息发送成功,即可返回成功,而不需要关系后续所包含的的一些业务及其结果,例如发送邮件通知,赠送积分等等其他业务。

RMI(Remote Method Invocation)

RMI 远程方法调用,主要用于分布式系统之间的系统调用,RMI可以理解为一个专用于java语言的RPC(Remote Procedure Call )实现。RPC:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议,RMI也是同样的屏蔽了网络之间通讯的细节,使java中调用远程的一个方法就像调用本地的一个方法一样。

一些类似的技术对比: RMI: 与java平台耦合严重,无法与其他语言平台结合使用。

Dubbo : 基于dubbo协议,还有一些其他协议。使用上相对简单,dubbo协议采用长连接,异步IO的方式实现,适合小数据量的数据传输,传输效率高。dubbo采用接口API方式实现,各个服务之间的耦合性相对严重。

Spring Cloud:基于HTTP协议,spring cloud对分布式系统的各种业务提供了较为完整的组件,http协议数据量高于dubbo协议,各个服务之间耦合度较低。

思考:

分布式系统中,各种业务对象的维护问题?比如:接口之间的参数约定问题,增加,删除,修改参数时,两边系统的维护问题,这之间可能还牵连各种中间对象,VO,DTO,DAO等各种javaBean的修改问题。各种服务的拆分问题?单人维护多个服务的问题?各位有什么好的建议,或者书籍推荐欢迎在评论留言。

JTA(Java Transaction API)

JTA java事务api,主要用于解决分布式系统之间分布式事务问题,是一个基于X/A的两阶段提交的事务模型,JTA定义了一套接口规范,JTA中约定了几种主要的程序角色,分别是事务管理器、事务客户、应用服务器、资源管理器。然后由JTS约定这些角色之间的交互细节。

分布式事务技术 MQ:基于日志的最终一致性的柔性补偿性事务处理方案。 TCC:Try,Commit,Cancel。try阶段对资源进行预留,commit阶段进行提交,如果失败进入Cancel阶段,对数据进行try阶段预留的资源进行回滚。 GTX(seata): 解决MQ,和TCC模式下的事务处理代码与业务代码之间的高侵入问题,基于类似MySql的redo,undo日志模式进行事务处理,降低了事务代码的侵入。

说明

分布式事务是分布式系统中最核心的一个业务难题,上面仅列出了目前主流的一些解决方案,对于具体的技术细节,请自行搜索,研究。

JTS

对应用屏蔽,主要定义了JTA实现的细节,给JTA的提供者使用。JTS是一个组件事务监视器。JTS是CORBA OTS事务监控的基本实现。JTS规定了事务管理器的实现方式。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。

EJB(Enterprise JavaBean)

EJB 企业级javaBean。企业Bean用Java编程语言编写,是一种服务器端组件,封装了应用程序的业务逻辑。该业务逻辑是满足应用目的的代码。

EJB组件: Session Bean,Message-Driven。

EJB容器:管理各种EJB组件中的bean生命周期,提供了通过java目录服务(JNDI)的方式获取bean,注入bean等等,同时提供了RMI,JTA服务组件,可以方便的进行分布式系统开发,且不需关心通讯细节,以及分布式系统之间的事务处理。同时可以对比一下spring容器,spring容器也是管理了开发中的各种bean对象,提供了控制翻转(IOC)或者说是依赖注入(DI)的方式对spring管理bean的获取。但是spring容器不包含对分布式模块的支持。

EJB服务器:我的理解是类似提供了EJB实现的Weblogic,Jboss服务器等。

JNDI (Java Name and Directory Interface)

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。 简单的可以理解为一种通讯录,通过姓名可以查询联系人电话,通讯地址等信息。 weblogic中通过JNDI提供jdbc连接池服务。

其他类

JAVA MAIL: 处理邮件相关。

JAF(JavaBeans Activation Framework):也是与处理邮件相关

Java IDL(Interface Description Language)/CORBA(Common Object Broker Architecture):主要用于与其他语言平台的接口交互。

JCA(Java EE Connector Architecture)它注重的是将Java程序连接到非Java程序和软件包中间件的开发。

JPA(Java Persistence API):数据持久化相关。

相关推荐