zoukankan      html  css  js  c++  java
  • Java EE 的核心技术规范(介绍)

    JAVA EE简介

      Java 平台企业版(Java Platform Enterprise Edition),java EE平台旨在帮助开发人员创建大规模,多层,可伸缩,可靠和安全的网络应用程序。此类应用程序的简称是“企业应用程序”,之所以这么称呼是因为这些应用程序旨在解决大型企业遇到的问题。但是,企业应用程序不仅对大型公司,代理机构和政府有用。对于日益联网的世界中的个人开发人员和小型组织,企业应用程序的好处是有益的,甚至是必不可少的。

    Java EE服务器

      Java EE服务器是实现Java EE平台API并提供标准Java EE服务的服务器应用程序,例如:tomcat,weblogic,jboss等等。

    JAVA EE规范

      个人认知,java EE规范 为软件开发提供了一系列的解决方案。屏蔽实际提供者之间的差异,统一了软件开发规范,使开发者可以专注于业务开发。例如:jdbc规范屏蔽了mysql,oracle等不同数据库厂商之间的差异。

    1. 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);

      /*返回服务器相关参数信息,具体可参考ServletConfig
      Returns 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

      <?xml version="1.0" encoding="UTF-8"?>
      
      <!-- 样例web.xml -->
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               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">
      
        <!-- 一个servlet节点对应一个servlet-mapping,它们的servlet-name要保持一致 --> <servlet> <servlet-name>hello-world</servlet-name> <servlet-class>org.example.TestServlet</servlet-class> <init-param> <param-name>param</param-name> <param-value>hello-world</param-value> </init-param> <!-- 1)load-on-startup元素标记容器是否在启动的时候就加载这个servlet(实例化并调用其init()方法)。 2)它的值必须是一个整数,表示servlet应该被载入的顺序 2)当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet; 3)当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。 4)正数的值越小,该servlet的优先级越高,应用启动时就越先加载。 5)当值相同时,容器就会自己选择顺序来加载。 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hello-world</servlet-name> <url-pattern>/hello-world</url-pattern> </servlet-mapping> <servlet> <servlet-name>hello-world-2</servlet-name> <servlet-class>org.example.TestServlet</servlet-class> <init-param> <param-name>param</param-name> <param-value>hello-world-2</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>hello-world-2</servlet-name> <url-pattern>/hello-world-2</url-pattern> </servlet-mapping> </web-app>

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

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

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

      测试数据:
      SELECT id,name,age FROM student;
      id,name,age
      13,李四,16
      14, 张三,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:张三:13
      13:李四:16

      执行结果二:

      SELECT id, name, age FROM student ORDER BY IF(1=2, age, id)
      13:李四:16
      14:张三: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取值问题。
    3. 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_HOMEworkCatalinalocalhost{域名一般是localhost}{项目名称}orgapachejsp{文件目录}{jsp文件名}.java


      小结

      Servlet,JDBC, JSP可以说是JAVAEE中最核心的三个技术了,一般我们学习javaWeb开发主要都是学习这三种技术,使用他们基本上可以满足各式各样的功能需求,但是在今天为了提高开发效率,提高性能,提高维护性等,我们还要学习一些框架技术作为辅助,例如上边提到的,Spring Mvc, 数据连接池,mybatis,以及很重要但没有提及的 Spring框架,学会这些框架常用功能的使用,了解框架核心的技术思想,基本上可以成为一名入门级的java Web程序员啦。
    4. 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因为没有约束文件,所以变动,扩展较为简单,但同时也无法提供详细的语意(节点是否必须存在,节点数量限制,数据的类型约束等)规范。
    5. 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消息发送成功,即可返回成功,而不需要关系后续所包含的的一些业务及其结果,例如发送邮件通知,赠送积分等等其他业务。

    6. 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的修改问题。各种服务的拆分问题?单人维护多个服务的问题?各位有什么好的建议,或者书籍推荐欢迎在评论留言。

    7. 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日志模式进行事务处理,降低了事务代码的侵入。

       说明

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

    8. JTS

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

    9. 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服务器等。

    10. 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连接池服务。

    11. 其他类 

      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):数据持久化相关。

    参考资料

    邮箱:quiet_learn@163.com
    本文版权归作者和博客园共有,欢迎转载,转载请在文章明显位置注明作者及出处,谢谢!
  • 相关阅读:
    C# 泛型的逆变与协变
    C# 元组
    DNS服务原理与搭建自己的DNS服务器
    浅析DNS域名解析过程
    Python turtle.circle()函数
    Python 实现点名系统
    PyCharm Debugger中Step Over、Step Into、Step Into My Code、Force Step Into、Step Out、Run to Cursor意思区别
    TypeScript与JavaScript比较(区别)
    微信小程序开发环境搭建
    Windows.edb 文件占据巨大的硬盘存储空间
  • 原文地址:https://www.cnblogs.com/LonelyTraveler/p/12116285.html
Copyright © 2011-2022 走看看