许是年龄大了,总是回想起以前的点点滴滴。翻看当初的代码,如同偶遇多年未见的前女友,曾经一起深入交换的情谊在颔首之间消散,令人烦躁。
本日就来聊聊老生常谈的 Java Web 开发。缘于一个简朴的Spring Boot项目改造,笔者看着一坨注解和配置,苦于拾掇记忆的痛苦,择其一二记录,纪念逝去的芳华。
本文对新手有一定帮助,大家笑过勿喷。
JSP + JavaBean
笔者学生时代打仗了JSP,作为远古产物,现在已难觅踪迹,但与它一同出现的JavaBean,却一直留传了下来。
在任何开发模式下,都须要一套规范,JavaBean 就是符合这些规范的类/对象,比如:
- 全部字段为 private(不允许外部直接访问,克制以后重命名/删除等操作引发依赖故障)
- 提供默认构造方法(方便外部实例化)
- 提供 getter 和 setter(自定义属性的读写逻辑)
- 实现 serializable 接口(序列化支持)
注意,JavaBean 不是 POJO,因为它须要方法、事件等处置处罚和响应业务。它包含全部的数据和业务逻辑,开发时在 HTML 中嵌入后端代码调用它们,如下所示:- <%@ page language="java" import="java.util.*,com.cy.bean.*" pageEncoding="utf-8"%>
- <%
- String path = request.getContextPath();
- %>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <head>
- <base href="<%=path%>">
- </head>
- <body>
- <%CheckUserBean cub=new CheckUserBean(); %>
- <jsp:useBean id="user" scope="request"></jsp:useBean>
- <jsp:getProperty property="name" name="user"/>
- <jsp:setProperty property="password" name="user"/>
- <%if(cub.checkUser(user)) {%>
- <jsp:forward page="success.jsp"></jsp:forward>
- <%}else{%>
- <jsp:forward page="fail.jsp"></jsp:forward>
- <%} %>
- </body>
- </html>
复制代码 上述有 UserBean 和 CheckUserBean 两个 JavaBean,其中 UserBean 用于展示数据及吸收用户输入,CheckUserBean 用于判定用户是否正当。
后来,JavaBean 的一些特征被开发人员相沿下来,同时概念简化为Bean,推广至更多的框架。对大部门后起的语言(比如 C#)来说,因为有 Java 帮忙踩的坑,它们每每在语言计划之初就提供了语言特性来更方便天然地贴合这些规范。
Servlet
JSP + JavaBean 的模式有一个显着的缺点,即隐性的页面跳转(数据流转),进步了开发过程中的出错概率,比如同一个页面可能由多个差异页面跳转过来,而相应的数据布局并不相同,开发人员要考虑全部可能的环境,并提供相应的 JavaBean 承接这些数据。同样随着业务发展,这种跳转或数据布局都会经常发生变更,开发维护成本极高。
于是增长了Servlet(一样平常继承自HttpServlet,该类定义了几个简朴明白的方法,此处不赘述)来处置处罚哀求、添补 JavaBean/调用 JavaBean 方法、选择返回哪个视图等,并且加上了路由的配置,形成了基础的MVC模式。
路由的配置在web.xml中,如下:- <?xml version="1.0" encoding="UTF-8"?>
- <web-app>
-
-
- <servlet>
- <servlet-name>login</servlet-name>
- <servlet-class>com.cy.servlet.LoginServlet</servlet-class>
- </servlet>
-
- <servlet-mapping>
- <servlet-name>login</servlet-name>
- <url-pattern>/login</url-pattern>
- </servlet-mapping>
- </web-app>
复制代码 值得一提的是,出现了Filter(过滤器)的概念,即在 servlet 处置处罚哀求之前和返回响应之后的中心处置处罚器,可以提供与业务无关的通用功能,比如身份校验、限流、异常处置处罚等。这种 AOP 理念非常好,也一直保留至今。
同样, Filter 也须要配置,如下:- <web-app>
-
- <filter>
- <filter-name>jsp</filter-name>
- <filter-class>com.cy.filter.DemoFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>jsp</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
复制代码 注意,Servlet 须运行于 Servlet 容器(如Tomcat)中。
Struts
为了进步开发效率,在 Servlet 基础上,提供了一些通用模块和工具,订定一套规范,形成一个框架,最知名的当属Struts,它有 1、2 两个版本。这两个版本并非简朴的升级,而是整个计划的更替。
Struts1
Struts1 使用一个单例焦点ActionServlet吸收全部哀求,哀求数据转化为ActionForm,然后依据配置(struts-config.xml中的ActionMapping)分发给差异的Action。Action 一样平常只包含一个 excute 方法用于处置处罚业务。
Struts1 很显着的缺点导致现在根本没人会去用:
- 配置繁琐
- ActionServlet 单例模式,须考虑线程安全
- 依赖 Web 容器,单元测试不方便
Struts2
于是Struts2被推出。
它使用Interceptor(拦截器) + Controller(即 Struts1 中的 Action)的模式,使得整个处置处罚流程扩展性大大进步了。
同时它摈弃了单例模式,每次都会实例化新的 Controller 处置处罚哀求(其中可包含任意多的方法用以执行差异业务),不消担心线程安全问题,缺点是并发量高的时候对象实例激增内存吃紧。
框架借助自己的拦截机制,将哀求和响应数据映射为 POJO,实现了 Controller 对HttpServletRequest和HttpServletResponse这样的原生 Servlet 对象的剥离,即 Controller 不依赖于 Web 容器,可以方便地单元测试了。
还记得上面 Servlet 的过滤器吗,Struts2 拦截器和它的原理一样,只不外前者面对全部哀求,后者针对的是某个具体的 Controller。当然,Struts2 同时使用了两者。
相比 Struts1,Struts2 有了质的飞跃,然而没过几年,它的荣光也被后起之秀所掩盖。
Spring MVC
说起Spring MVC,不得不先说说Spring。
Spring
Spring是 Java 平台流行的 IOC 和 AOP 框架,虽然它自己不针对特定的使用场景,但是 Java 平台的 Web 基因一开始就影响着它,所以我们惯常使用它来开发后端服务。Spring 官方有专门的子项目Spring Web,Spring MVC 就是 Spring Web 的子模块。Spring Web 包含很多其它模块,如Spring WebFlux、Spring Web Service、Spring WebSocket等。
Java 后半程在移动端大放异彩,有另一个 IOC 框架Dagger在背后默默支持,可参看笔者写的从零开始撸一个App-Dagger2,此处不赘述。
IOC
我们可以通过在 XML 文件(使用ClassPathXmlApplicationContext加载)中配置 Bean,然后在代码中使用@Autowired或@Resource(来自 JSR-250,JDK 内置)注入 Bean 实例(作用域可通过scope设置,默认是单例)。
XML 配置稍显繁琐,Sping2.5 开始支持注解注入,只要在 XML 中配置(对应的有@ComponentScan注解),Spring 便会自动扫描指定包中的全部类,查找如@Component,@Service,@Repository,@Controller等注解修饰的类,并创建相应的 Bean。当然,这种方式只能配置本项目内的类。
为了使注解方式可以注入第三方类,从 3.0 开始,Spring 引入了@Configuration。使用 @Configuration 注解修饰的类(使用AnnotationConfigApplicationContext加载)中,可使用@Bean注解修饰返回 Bean 的方法。我们若要复用它处定义的配置类,可使用@Import注解,它的作用雷同于将多个 XML 配置文件导入到单个文件。
XML 配置和注解配置也可以混用,比如使用@ImportResource注解引入 XML 文件。
AOP
Spring 照旧提供了 AOP 功能。
AOP 分为静态 AOP 和动态 AOP。静态 AOP 是将切面代码直接编译到源代码中,如 Java 平台的AspectJ实现;动态 AOP 是指将切面代码运行时动态织入。Spring 的 AOP 为动态 AOP,实现的技术为 JDK 提供的动态代理技术和CGLIB(动态字节码增强技术),两者区别如下:
- JDK 动态代理使用拦截器(必须实现 InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处置处罚;CGLIB 使用ASM框架,将目标类生成的 class 文件加载进来,通过修改其字节码生成子类来处置处罚。
- JDK 动态代理的目标类必须实现某个接口,只有接口中的方法才可以或许被代理;CGLIB 无此限制,但是因为采用的是继承模式,所以目标类或方法不能为 final。
- 在 Java1.8 之后,大部门场景下,JDK 动态代理的效率都要优于 CGLIB。
两者尽管实现技术不一样,但都是基于代理模式,都是生成一个代理对象。
Spring 会根据目标类是否实现接口来决定使用 JDK 动态代理照旧 CGLIB,当然在符合条件时也可以强制使用 CGLIB()。
Spring AOP 涉及到的注解包罗@Aspect、@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing、@Around、@EnableAspectJAutoProxy等,此处不详述。
Spring MVC 同样是基于 Servlet,像是 IOC 版的 Struts2,当然由于 IOC 的引入,两者的概念和组件大相径庭,但是处置处罚哀求的主干是一致的。
Spring MVC 支持的页面渲染实现,并不包含 JSP。而是Thymeleaf、Freemarker等。
Spring Boot
最后来谈谈 Spring Boot,它是创建在 Spring 之上的一个快速开发框架,旨在简化 Spring 应用的初始搭建以及开发过程。它通过提供默认配置、Starter dependencies等特性,极大地减少了项目标配置工作。
同样的,它不独属于 Web 开发,但我们主要照旧在 Web 范畴使用它。
@ConfigurationProperties
在 Spring Boot 项目中,我们常将大量的参数配置在 application.properties(Spring) 或 application.yml 文件中,然后通过@Value取值,如下:- @Value("${db.userName}")
- private String userName;
复制代码 着实通过@ConfigurationProperties注解,我们可以更清新地获取这些参数值:- //@Component 注入
- @ConfigurationProperties(prefix="db")
- public class DbConfiguration{
- public String userName;
- }
复制代码 @ConfigurationProperties 并不表示成为 Spring Bean,除非配置类同时标注 @Component 之类的注解,或者在使用方标注@EnableConfigurationProperties注解(发起后者,即按需索取,而非全局可见):- @EnableConfigurationProperties(DbConfiguration.class)
- public class Invoker{
- @Autowired
- DbConfiguration dbConfiguration;
- }
复制代码 spring.factories
如果你正在编写一个基于 Spring 的类库,其中很多对象都是以 Bean 的形式注入使用的,所以你当然希望使用这个类库的第三方项目可以将这些对象事先加载到容器中。
你可以在 ReadMe 中写明“XX 类及 XXX 类 及……必须在项目启动时实例化到容器中”,如此使用方知道他必须采用 XML 或 @Configuration 等方式写上一大段和业务无关的配置代码。
或者你可以使用 spring.factories 方案。spring.factories 着实是 Spring boot 提供的SPI机制,使用方的项目(须要在入口类中标注@EnableAutoConfiguration注解)会基于SpringFactoriesLoader检索ClassLoader中全部 jar(包罗ClassPath下的全部模块)引入的META-INF/spring.factories文件,基于文件中的接口自动加载对应的 @Configuration 修饰的类并且注册到容器中。
spring.factories 为模块化、配置化提供了基石,我们经常引用的诸如“xxx-spring-boot-starter”的类库,根本上就是使用了该方案。
ps:自 Spring Boot 3.0 始,由META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports替代 META-INF/spring.factories,内容格式有所变化,原理不变。
Spring Boot 3.0 是一个比力大的改版,影响最大的改动是必须使用 JDK17 及以上版本。
由于我们常将 @ComponentScan、@SpringBootConfiguration(同 @Configuration)、@EnableAutoConfiguration 一起使用,Spring Boot 干脆出了一个@SpringBootApplication注解,将三者合一。
Spring Boot 对 AOP 的使用进行了一些改动,此处不赘述。
内置常见的服务器(如 Tomcat、Jetty),无需单独部署。
Spring Boot 虽然是一个非常成熟的拆箱即用框架,但在微服务场景下就显得过于笨重了。后续有缘的话笔者会再来聊聊 Java 平台更得当微服务运行的几个框架。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |