首先说说大潮流的 Spring Boot。因为项目强制要求的缘故,我不得不将 Spring 升级到 Spring Boot,但我个人觉得,这不但没有必要(不是做微服务),而且有以下几个问题:
- Boot 采用新的的打包机制,默认生成一个体积非常大的 jar 包。这样导致更新很麻烦,每次上次那么大的 jar 耗时间久(lib 下面的依赖 jar)。以前我更新项目有个技巧,也不是用 war 包(第一次的时候可以是),而是同步本地的到远程的即可,因为本地事实的就是最终编译结果,通过 SFTP 同步两边即可。或者不同步,你知道更新哪个 class/jsp 上传文件即可,名副其实的“增量更新”。现在可好了——上传完整的 jar 包就是“全量更新” 。
- 同样也正是这个原因,更加网页的静态文件非常不方便。——难道我改 HTML 里面的一个字符就要重新打包 jar 并上传?太不科学了吧!?
- 由于 Boot 整合了 Tomcat 服务器,那些日志我都不知道哪里看,有肯定是有——那就要重新学习、改变习惯呗。以前的话,用 Tomcat 自带的脚本:
catalina.sh run 就可以观察日志,非常方便。
当然,鼎鼎大名的 Boot 不是没有对应的解决方法,百度一下就有了。但鄙人就觉得不太优雅去处理咯。首先 lib/*.jar 可外置游离于 WEB-INF/lib 下面,依靠 Java 层面去指定某个目录,一个项目没啥事,多个项目呢?——这个就花费了人们去管理这个目录的“心智”;其次,前台页面的,也可以游离出去,不是放在 webapps 下面,所以管理维护成本又上升了——我何必那样子呢——老的方法不是很好么。最后更新的问题,Boot 项目允许你打包为 war 包而不是 jar 包,你解压一下不就可以上传了么。
还有个不痛不痒的问题,Boot 不能设置全局的 Welcome 默认页,例如(index.htm/index.jsp 等)。尽管可以单独设置某个 path 下面的,但全局不行。看来 Boot 不打算为传统页面模式提供支持。
说了那么多不好的话,但此时此刻,得说说好话。不过的确也是实事求是地说,Boot 使用起来很方便,简化了很多,但是无非仍是 Spring + Spring MVC 的高层封装,对于搞微服务的 Spring Cloud,那真是刚需。但话又说回来,你不是搞微服务,就是普通的 Web 开发,是不是 Boot 真的差异不大。
话归正题,让我们重新学习 Spring & MVC 吧!
Spring 最小引用
比起我初学 Spring 那时候,Spring 5.x 对依赖简化了不少,我那时候还需要 Common-lang 和 Common Log 之类的,现在不用了。下面是使用 Spring MVC 的最小引用
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.6</version>
</dependency>
webmvc 自动依赖 web、context 等的包。
各种配置
为传统的模式的 Spring MVC 和 Boot 的都提供配置支持。
启动 Spring
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<page-encoding>UTF-8</page-encoding>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
</jsp-property-group>
</jsp-config>
在 /WEB-INF 下配置 applicationContext.xml ,指定扫描的包:
<context:component-scan base-package="net.bingosoft" />
<bean class="com.ajaxjs.spring.DiContextUtil" />
Spring MVC 配置
在指定的包之中新建一个 Spring MVC 配置类,继承 BaseWebInitializer 。
import com.ajaxjs.spring.BaseWebInitializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
public class WebInitializer extends BaseWebInitializer {
}
于是要为 MVC 设一个配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<mvc:default-servlet-handler />
</beans>
对 Request、Response 的扩展
在 web.xml 加入一个过滤器。
<filter>
<filter-name>InitMvcRequest</filter-name>
<filter-class>com.ajaxjs.web.InitMvcRequest</filter-class>
</filter>
<filter-mapping>
<filter-name>InitMvcRequest</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Boot 下使用:继承 InitMvcRequest 并声明为组件即可。
@Component
@Order(1)
InitMvcRequestFilter extends InitMvcRequest {
}
如果不自动扫描包,那么加入下面 bean:
<bean class="com.ajaxjs.web.InitMvcRequest" />
Spring 工具类
就地取材,Spring 本身包含不少优秀的工具类,既然 Spring 为底层必定依赖的库,那么请大胆使用其工具栏吧~我知道的是,有些团队约束(或禁止)成员去写自己的工具类,例如 StringUtil.isEmpty() 之类的。
StringUtils.hasText();
StringUtils.capitalize(Str);
ObjectUtils.isEmpty([]);
CollectionUtils.isEmpty(List/Map);
Base64Utils.encodeToString();
Base64Utils.decodeFromString();
StreamUtils.copyToByteArray();
UriUtils.encode(v.toString(), "utf-8");
public static String urlEncode(String str) {
try {
return URLEncoder.encode(str, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
LOGGER.warning(e);
return null;
}
}
public static String urlDecode(String str) {
try {
return URLDecoder.decode(str, StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
LOGGER.warning(e);
return null;
}
}
以前写的
public static String[] split(String str) {
return str.split(",|/|-|\\\\|\\||;");
}
@Test
public void testSplit() {
assertEquals(2, split("a,b").length);
assertEquals(3, split("a/b/c").length);
assertEquals(4, split("a\\b\\c\\d").length);
assertEquals(5, split("a|b|c|d|e").length);
assertEquals(5, split("a;b;c;d;e").length);
}
public static String repeatStr(String str, String div, int repeat) {
StringBuilder s = new StringBuilder();
int i = 0;
while (i++ < repeat) {
s.append(str);
if (i != repeat)
s.append(div);
}
return s.toString();
}
@Test
public void testRepeatStr() {
assertEquals(repeatStr("Hi", ",", 3), "Hi,Hi,Hi");
}
public static boolean containsIgnoreCase(String a, String b) {
return a.toLowerCase().contains(b.toLowerCase());
}
@Test
public void testContainsIgnoreCase() {
assertTrue(containsIgnoreCase("abc", "A"));
}
private static final String STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public static String getRandomString(int length) {
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(62);
sb.append(STR.charAt(number));
}
return sb.toString();
}
public static boolean isEmptyString(String str) {
return str == null || str.isEmpty() || str.trim().isEmpty();
}
public static String firstLetterUpper(String str) {
return Character.toString(str.charAt(0)).toUpperCase() + str.substring(1);
}
public static boolean isNull(Object[] arr) {
return arr == null || arr.length == 0;
}
assertTrue(isNull(new Object[] {}));
assertFalse(isNull(new Object[] { null }));
public static boolean isNull(Collection<?> collection) {
return collection == null || collection.isEmpty();
}
public static boolean isNull(Map<?, ?> map) {
return map == null || map.isEmpty();
}
@Test
public void testCollection() {
assertTrue(isNull(new HashMap<String, String>() {
private static final long serialVersionUID = 1L;
}));
assertTrue(isNull(new ArrayList<String>() {
private static final long serialVersionUID = 1L;
}));
List<Object> list = null;
assertTrue(isNull(list));
}
public static String base64Encode(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}
public static String base64Encode(String str) {
return base64Encode(str.getBytes(StandardCharsets.UTF_8));
}
public static byte[] base64DecodeAsByte(String str) {
try {
return Base64.getDecoder().decode(str);
} catch (IllegalArgumentException e) {
LOGGER.warning("BASE64 解码失败", e);
return null;
}
}
public static String base64Decode(String str) {
byte[] b = base64DecodeAsByte(str);
return b == null ? null : byte2String(b);
}
public static byte[] inputStream2Byte(InputStream in) {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();) {
write(in, out, true);
return out.toByteArray();
} catch (IOException e) {
LOGGER.warning(e);
return null;
}
}
|