1. EJB
1.1. 背景
功能日趋复杂的软件,如果把所有的功能实现都放在客户端,不仅代码没有安全性,部署及发布运维都会变的很复杂,所以将软件的功能实现分为客户端和服务端,服务端和客户端之间通过网络调用进行功能实现。
客户端只做界面展示和数据交互,功能的实现统统交给服务端去做,但这不是前后端分离的意思,而是部署及功能实现的问题。
整个的这个实现方案对应的技术就是Java EE(Java Platform,Enterprise Edition),JavaEE提供了广泛的企业级特性和服务,由Sun公司为企业级应用推出的标准平台,也可以说是一种开发思路或者模范。包含了我们现在java开发的众多标准技术-13项核心技术规范
- JDBC(Java Database Connectivity):Java数据库连接。
- JNDI(Java Naming and Directory Interface):Java的命名和目录接口。
- EJB(Enterprise JavaBean):企业级JavaBean。(早已被spring替代了)
- RMI(Remote Method Invoke):远程方法调用。
- Java IDL(Interface Description Language)/ CORBA(Common Object Broker Architecture):接口定义语言/公用对象请求代理程序体系结构。
- JSP(Java Server Pages):Java服务端页面,一种动态网页技术标准。
- Sevlet(Server Applet):Java服务端程序。
- XML(Extensible Markup Language):可扩展标记语言。
- JMS(Java Messages Service):Java消息服务。
- JTA(Java Transaction API):Java事务API。
- JTS(Java Transaction Service):Java事务服务。
- JavaMail:Java处理电子邮件的相关接口。
- JAF(JavaBean Activation Framework):数据处理框架。
在JavaEE的开发思路下,整个软件实现就会分为三层:
- 视图层或表现层:提供用户界面,接收用户输入,数据输出。Web应用的情况,JSP以及Servlet属于视图层组件。
- 业务逻辑层:实际的业务逻辑处理。根据视图层传送过来的数据,进行实际的业务逻辑处理(包括数据库的查询,更新等),再把处理后的结果返回给视图层。EJB,以及不使用EJB的情况下担当业务逻辑处理部分的JavaBean等归属于业务逻辑层组件。
- 数据持久化层:多指用于保存业务数据的数据库,也可以是文件等。
商务软件的核心部分是它的业务逻辑。业务逻辑抽象了整个商务过程的流程,并使用计算机语言将他们实现。J2EE 对于这个问题的处理方法是将业务逻辑从客户端软件中抽取出来,封装在一个组件中。这个组件运行在一个独立的服务器上,客户端软件通过网络调用组件提供的服务以实现业务逻辑,而客户端软件的功能单纯到只负责发送调用请求和显示处理结果。在J2EE 中, 这个运行在一个独立的服务器上,并封装了业务逻辑的组件就是EJB(Enterprise Java Bean)组件。所以EJB是属于J2EE(Java EE)体系结构中的业务逻辑层部分。
题外话:Java刚开始的时候,因为各种应用和生态不成熟,很多东西需要有人牵头制定强制规范引导Java的发展,于是JavaEE曾经引领了企业级应用的开发。但随着时代的进步越来越多的公司和组织参与到Java世界,出现了各种各样的JavaEE组件的代替者,比如Hibernate、Spring就是其中两个典型。相反,Java官方制定的各种JavaEE规范反而不太受欢迎,他们制定了JSF规范,但实际企业开发喜欢用Struts 2、Spring MVC;他们制定了EJB规范,但实际企业开发还是喜欢用Spring;他们制定了JPA规范,但实际企业开发往往还是喜欢直接用Hibernate、MyBatis。现代企业级应用常用的各种框架和工具,比如Struts 2、Spring、Hibernate、jBPM、Activiti、Lucene、Hadoop、Drools、CXF等这些大家耳熟能详的组件,全部都不是来自Oracle官方,但是却在企业应用中开发经常用到的。现在企业里面,真正常用的JavaEE规范有什么?Servlet、JSP、JMS、JNDI。这些技术都只是充当了一个程序的入口而已。
1.2. 详解
以上是EJB出现的原因,现在还需要详细看看EJB实现的功能,EJB组件运行在EJB容器之中,EJB容器是一个EJB引擎,它提供了EJB组件运行的环境,并对EJB组件进行管理。EJB容器一般包含在EJB服务器(或应用服务器)中,EJB服务器可以拥有一到多个EJB容器。比较有名的支持EJB的服务器有Sun One,Interstage,Websphere,Weblogic,JBoss,JRun等。
调用EJB组件的一方被称为EJB客户端。EJB客户端可以为运行在WEB容器中的JSP,SERVLET;或者一般的Java Application,Applet;或者Web Service;也可以是别的EJB组件。
EJB客户端与EJB服务器可处于同一JVM环境中,也可处于不同计算机的不同JVM环境。
EJB提供了一种简化复杂性的机制,使开发人员可以关注业务逻辑,而不是底层的复杂性,例如事务管理,安全性,远程访问等。
1.2.1. EJB的存在原因
- 简化开发:EJB将复杂的事务和安全管理交给了服务器,使开发人员能够专注于开发业务逻辑。
- 标准化:EJB提供了一种标准化的方式来开发分布式应用,这使得不同的开发者和团队之间的协作变得更加简单。
- 可移植性:由于EJB是基于Java EE规范的,所以EJB应用程序可以在任何符合Java EE规范的服务器上运行,这增加了应用的可移植性。
1.2.2. EJB解决的问题
- 事务管理:EJB容器自动处理了事务的开始、提交和回滚,开发者不必自己编写这些代码。
- 安全性:EJB容器可以处理用户的认证和授权,为应用程序提供安全性保障。
- 并发处理:EJB容器能够管理并发访问,为开发者处理多线程问题。
- 分布式计算:EJB容器提供了远程方法调用(RMI)的支持,使得在分布式环境中进行通信变得简单。
- 生命周期管理:EJB容器负责EJB对象的创建和销毁,开发者不必关心这些底层的细节。
1.2.3. EJB的基本组成部分:
- 会话Bean(Session Beans):会话Bean用于执行服务器上的业务逻辑,它可以是有状态的(Stateful),也可以是无状态的(Stateless)。
- 实体Bean(Entity Beans):实体Bean用于封装在数据库中存储的业务对象的数据和业务逻辑。实体Bean在EJB 3.0之后被JPA(Java Persistence API)取代。
- 消息驱动Bean(Message-Driven Beans):消息驱动Bean用于异步处理由JMS(Java Message Service)发送的消息。
1.3. 示例
Session Beans是EJB的一种类型,主要用于执行业务逻辑。它们不直接代表数据库中的数据,而是封装了一组业务操作,这些操作可以被客户端程序调用。Session Beans有两种类型:无状态(Stateless)和有状态(Stateful)。
-
无状态会话Bean(Stateless Session Beans):不保持任何客户端特定的会话状态。当一个方法调用完成后不会保留任何关于客户端的信息。这意味着你可以在多个客户端之间共享一个无状态Bean的实例,因为它不维护任何关于客户端的状态信息。它的一个主要优点是它们可以被高效地池化和复用,可以处理大量的请求,而不需要为每个客户端创建一个新的Bean实例。
-
有状态会话Bean(Stateful Session Beans):会保持与特定客户端相关的会话状态。这个状态会在客户端和Bean之间的多个方法调用之间保持,并在会话结束时被销毁。它的一个主要优点是它们可以保存客户端的状态,这使得它们非常适合在需要维护会话状态的应用中使用。例如,如果你正在开发一个在线购物应用,你可能需要一个有状态Bean来保存用户的购物车信息。然而,有状态Bean的一个主要缺点是它们不能被共享和复用,因为它们保存了客户端特定的状态。这意味着如果有大量的客户端,你可能需要创建大量的有状态Bean实例,这可能会消耗大量的资源。
项目目录树
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>EJBAnHour</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><properties><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>javax</groupId><artifactId>javaee-web-api</artifactId><version>8.0</version></dependency></dependencies><build><finalName>EJBAnHour</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.2.3</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin></plugins></build></project>
Hello2Servlet.java
package org.example;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.logging.Logger;@WebServlet("/hello2")
public class Hello2Servlet extends HttpServlet {private static final Logger LOGGER = Logger.getLogger(Hello2Servlet.class.getName());public Hello2Servlet() {LOGGER.info("Hello2Servlet is created");}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {LOGGER.info("doGet method is called");resp.getWriter().write("Hello from Hello2Servlet!");}
}
HelloEJB.java
package org.example;import javax.ejb.Stateless;@Stateless
public class HelloEJB {public String sayHello() {return "Hello, EJB!";}
}
HelloServlet.java
package org.example;import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@WebServlet("/hello")
public class HelloServlet extends HttpServlet {@EJBprivate HelloEJB helloEJB;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write(helloEJB.sayHello());}
}
运行结果
1.4. 总结
- EJB实现原理: 就是把原来放到客户端实现的代码放到服务器端,并依靠RMI进行通信。
- RMI实现原理 :就是通过Java对象可序列化机制实现分布计算。
- 服务器集群: 就是通过RMI的通信,连接不同功能模块的服务器,以实现一个完整的功能。
2. Spring
2.1. Spring Framework
早期的 J2EE(Java EE 平台)推崇以 EJB 为核心的开发方式,但这种开发方式在实际的开发过程中存在种种弊端,例如使用复杂、代码臃肿、代码侵入性强、开发周期长、移植难度大等。
Rod Johnson 在其 2004 年编著的畅销书《Expert One-on-One J2EE Development without EJB》中,针对 EJB 各种臃肿的结构进行了逐一的分析和否定,并分别以更加简洁的方式进行了替换。这本书影响甚远,后来 Rod Johnson 将 com.interface21 的代码开源,并把这个新框架并命名为“Spring”,含义为:Spring 像一缕春风一样,扫平传统 J2EE 的寒冬。
- 广义spring:以 Spring Framework 为核心的 Spring 技术栈,经过十多年的发展,Spring 已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、Spring MVC、SpringBoot、Spring Cloud、Spring Data、Spring Security 等,其中 Spring Framework 是其他子项目的基础。
- 狭义spring:特指Spring Framework,通常我们将它称为 Spring 框架
Spring 框架是一个分层的、面向切面的 Java 应用程序的一站式轻量级解决方案,它是 Spring 技术栈的核心和基础,是为了解决企业级应用开发的复杂性而创建的。Spring 有两个核心部分: IoC 和 AOP。
- IOC:Inverse of Control 的简写,译为“控制反转”,指把创建对象过程交给 Spring 进行管理。
- AOP:Aspect Oriented Programming 的简写,译为“面向切面编程”。 AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外,AOP 还解决一些系统层面上的问题,比如日志、事务、权限等。
spring框架的特点:
-
方便解耦,简化开发
Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。 -
方便集成各种优秀框架
Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。 -
降低 Java EE API 的使用难度
Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。 -
方便程序的测试
Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。 -
AOP 编程的支持
Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。 -
声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无须手动编程。
4 个 jar 文件是 Spring 框架的基础包,分别对应 Spring 容器的四个模块:
- spring-core-x.x.xx.jar 包含 Spring 框架基本的核心工具类,Spring 其他组件都要用到这个包中的类,是其他组件的基本核心。
- spring-beans-x.x.xx.jar 所有应用都要用到的,它包含访问配置文件、创建和管理 Bean 以及进行 Inversion of Control(IoC)或者 Dependency Injection(DI)操作相关的所有类。
- spring-context-x.x.xx.jar Spring 提供在基础 IoC 功能上的扩展服务,此外还提供许多企业级服务的支持,如邮件服务、任务调度、JNDI 定位、EJB 集成、远程访问、缓存以及各种视图层框架的封装等。
- spring-expression-x.x.xx.jar 定义了 Spring 的表达式语言。
- 需要注意的是,在使用 Spring 开发时,除了 Spring 自带的 JAR 包以外,还需要一个第三方 JAR 包 commons.logging 处理日志信息。
传统的spring项目只能使用xml文件来注入管理bean,Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。Spring 通过注解实现自动装配的步骤如下:
- 引入依赖:spring-aop-5.3.13.jar
- 开启组件扫描:使用 context:component-scan 元素开启自动扫描功能
- 使用注解定义Bean:@Component、@Repositort、@Service、@Controller
- 依赖注入:@Autowired、@Resource
spring的核心:1. 标注bean代表让spring管理,2. 工厂模式+反射机制提前初始化到spring容器中。而不是等需要用时使用者主动new。
2.2. Spring MVC
Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。
MVC 模式将应用程序划分成模型(Model)、视图(View)、控制器(Controller)等三层,如下图所示:
- Model(模型):一个模型可以为多个视图(View)提供数据,一套模型(Model)的代码只需写一次就可以被多个视图重用,有效地减少了代码的重复性,增加了代码的可复用性。
可以理解为 service和dao部分代码 - View(视图):指在应用程序中专门用来与浏览器进行交互,展示数据的资源。在 Web 应用中,View 就是我们常说的前台页面,通常由 HTML、JSP、CSS、JavaScript 等组成。
可以理解为前端展示界面 - Controller(控制器):通常指的是,应用程序的 Servlet。它负责将用户的请求交给模型(Model)层进行处理,并将 Model 层处理完成的数据,返回给视图(View)渲染并展示给用户。
可以理解为controller部分代码
和 MVC 模式类似,三层架构同样将系统划分成了 3 层:
-
表示层(UI):用来实现与用户的交互,接收用户请求,并将请求交给业务逻辑层(BLL)和数据访问层(DAL)进行处理,最后将处理结果返回给用户。
-
业务逻辑层(BLL):起到承上启下的作用,接收表示层传递来的请求,并针对业务对数据进行处理,以实现业务目标。
-
数据访问层(DAL):用于实现与数据库的交互和访问,例如从数据库中获取数据、保存或修改数据库中的数据等。
-
依赖注入复杂,需要自己手动把需要的jar一个个添加到pom文件
-
配置复杂,applicationContext.xml文件用来管理springbean及DispatcherServlet(视图解析器)
-
安全校验等各种都是通过xml文件进行配置
Spring MVC 主要的执行流程:
- 浏览器发送一个请求,若请求地址与 web.xml 中配置的前端控制器(DispatcherServlet)的 url-pattern 相匹配,则该请求就会被前端控制器 DispatcherServlet 拦截;
- 前端控制器(DispatcherServlet )会读取 SpringMVC 的核心配置文件,通过组件扫描获取到所有的控制器(Contorller);
- 将请求信息和控制器中所有控制器方法标识的 @RequestMapping 注解的 value、method 等属性值进行匹配。若匹配成功,则将请求交给对应的 @RequestMapping 注解所标识的控制器方法处理;
- 处理请求的方法会返回一个字符串类型的视图名称,该视图名称会被 Spring MVC 配置文件中配置的视图解析器(ViewResolver)解析真正的视图(View)对象,最终展示给客户端。
2.3. Spring Boot
Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程。
说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。
SpringBoot的简便提现在哪里?
- 自动配置:约定大于配置,可以简化spring的配置流程,注意不是注入而是配置
- 简化启动:以前web应用要使用到tomat服务器启动,而springboot内置服务器容器,通过@SpringBootApplication中注解类中main函数启动即可
所以整个springboot的执行依赖于其启动类及启动类上的注解
自动配置的原理依赖注解@SpringBootApplication-@EnableAutoConfiguration- @Import({AutoConfigurationImportSelector.class})-selectImports方法-spring.factories。
SpringBoot的自动装配也就是通过@EnableAutoConfiguration注解,加载AutoConfigurationImportSelector类中的selectImports方法,进而扫描spring.factories文件下的自动配置类,并将其装配到IOC容器的过程。