共计 29472 个字符,预计需要花费 74 分钟才能阅读完成。
导读
我一直相信这句话,他山之石可以攻玉。在自己能力不够时,多学习别人的东西。这样,对自己只有好处,没有坏处。因而,经过将近一年的工作,研读了公司所使用的框架。我本想往架构师的方向靠近,但,自己的能力可能还不够,因而,不断地给自己充电。
公司的项目是前后端分离的,前端使用 HTML5,css3、jquery、vue.js、bootstrap 等,以 SVN 做代码托管。后端采用 maven 构建项目,以 git lab 做代码托管。肯定有人会问,这两个都是版本库,但它们有什么区别?如果想要了解的话,可以参考该文档:Svn 与 Git 的区别。
现在几乎所有的公司都采用 maven 构建项目,很少会采用导入 jar 包的方式依赖第三方框架。
maven 介绍
maven 构建的项目有很多好处,首先其可以统一管理 jar 包,也就是说,我们在项目中不用手动导入 jar 包,我们只要添加依赖即可,如代码所示:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${jdbc.version}</version>
</dependency>
添加依赖之后,maven 就会导入该 jar 包,导入 jar 包的顺序为:
首先查找本地仓库,如果本地仓库没有,进入下面步骤。
maven settings profile 中的 repository;
pom.xml 中 profile 中定义的 repository。profile 激活配置文件,比如正式环境的配置文件 prd.properties 和开发环境的 Dev.properties 文件。这也是打包的依据,是打开发环境的包,还是打正式环境的包,如图所示:
pom.xml 中的 repositorys(定义多个 repository,按定义顺序找);
如果经过上面的步骤,没有找到相应的 jar 包,最后到我们的镜像(mirror)中查找。如果 mirror 中存在该 jar 包,从 mirror 中拷贝下来,存储到本地仓库中,进入到最初一步。如果 mirror 中也没有,maven 就会报相应的错误。
maven 报出相应的错误时,也许,是我们本地没有该 jar 包,远程仓库也没有该 jar 包,我们可以参考这篇博客:在 maven 的 pom.xml 中添加本地 jar 包。它会教你如何创建本地仓库,并导入创建好的本地仓库。
【备注】这篇博客以架构师的角度来讲解 maven,所以,不具体讲解 maven 各个标签的含义。如果你想了解 pom 的各个标签的含义,可以参考这篇文档:pom.xml 详解,或者,参考这篇教程:maven 教程 | 菜鸟教程
上面解说只是配置 jar 文件,但是,maven 的功能远不止这些,从我们创建 maven 项目时,就已经进入到 maven 的开发环境中。
maven 项目
有人和我说过,学一项知识,什么方式最快?那就是通过做项目。你在做项目的过程中,肯定会遇到很多问题,而你不得不去查找资料,从根源上认识到这个问题。因而,我以公司所做的某个项目为例,来讲解 maven 的依赖、继承、聚合等关系。
我们所说的 maven 中的关系,其实就是 pom 的关系,即项目对象模型 (Project Object Model) 的简称。
maven 聚合
首先,我们在创建 cloudCodeSale 项目时,就已经创建了父 pom 文件,如图所示:
上图就是我们的父 pom 文件,你很清楚的看到 gav 坐标。同时,你从这张图上,也能得到其他信息,其打包方式是 pom,其还关联其他 module,module 名称和左边的列表名称一样。这就是我们所说的 maven 的聚合。父类同时聚合其子类。
聚合的条件有两个:
修改被聚合项目的 pom.xml 中的 packaging 元素的值为 pom
在被聚合项目的 pom.xml 中的 modules 元素下指定它的子模块项目
既然所有的子模块的 pom 都继承父 pom,为什么父 pom 要聚合子模块的 pom 文件?这个问题很好。
因为对于聚合而言,当我们在被聚合的项目上使用 Maven 命令时,实际上这些命令都会在它的子模块项目上使用。这就是 Maven 中聚合的一个非常重要的作用。在实际开发的过程中,我们只需要打包(mvn install)父 pom 文件。
我们在父 pom 上使用 mvn celan、mvn compile 和 mvn package,其会自动对子模块:platform-core、platform-core-controller、portal/member-portal、portal/platform-portal、platform-cms、platform-cms-controller、platform-custom 执行 mvn celan、mvn compile 和 mvn package。没必要一个一个地打包,这样极容易出现错误,如图所示:
maven 继承机制
如果很多模块都需要相同的 jar 包,我们可以单独写在一个 pom 中,其他模块使用该模块的公共部分,这就是我们常说的父类。不论是 java 语言,还是 c ++ 语言,或者现在的 pom,其都体现这个思想。
我们在上文也提到了子模块,现在模块 platform-core 讲解。继承父类的结构一般是这样的:
<parent>
<groupId>parent.groupId</groupId>
<artifactId>parent.artifactId</artifactId>
<version>parent.version</version>
<relativePath>../pom.xml</relativePath>
</parent>
relativePath 是父 pom.xml 文件相对于子 pom.xml 文件的位置,针对被继承的父 pom 与继承 pom 的目录结构是不是父子关系。如果是父子关系,则不用该标签;如果不是,那么就用该标签。因为在当前项目中,platform-core 模块的目录的 pom 在父目录的 pom 中,其和父 pom 的目录结构是父子关系,因而可以省略 relativePath 该标签,如图所示:
parent 标签中的 groupId、artifactId、version 要和父 pom 中的标签中一致。
maven 的依赖关系
正如我们所知道的,maven 构建项目,不仅是因为其能够管理 jar 包,其还使模块化开发更简单了。因而,我们在开发的过程中,一般都分模块化开发。模块与模块之间的信息是不通的,但是,我们想要模块间的之间能够通信,这时,我们就想到了 java 中的依赖关系。
比如,我们有一个模块,这个模块封装好了微信支付、支付宝支付、处理 json 格式、操作文件、ResultUtil、lambdaUtil、commonUtil 等工具类,还有附件、头像、用户等实体类。这些工具类在任何项目中不会轻易改变,如果为了满足某些需求而不得不得修改,需要得到架构师的同意。
因而,我们可以把它拿出来,单独定义为一个模块,也就是 platform-core 模块。但是,我们还有一个模块,在这个模块中,根据不同的项目,其定义不同的实体类、dao 层类、事务层类、枚举类、接收前端传来参数封装成的 query 类、从数据库中取出的数据封装成的 data 类,到事务层可能会调用模块 plateform-core 中的方法,比如调用第三方系统接口的 HTTPClientUtil.doPost(String url, Map<String, String> param),判断处理 lambda 表达式的 LambdaUtil.ifNotBlankThen(String value, Consumer<String> function),等等。
这个自定义类的模块,我们可定义为 plateform-custom。plateform-custom 需要用到 platform-core 中的方法,因而,这时,我们就需要考虑依赖关系,怎么添加对 platform-core 的依赖呢?如代码所示:
<dependency>
<groupId>com.zfounder.platform</groupId>
<artifactId>platform-core</artifactId>
</dependency>
我们这边是前后台分离的,后台用来录入数据,前台用来展示数据,因而,我们有一个 portal 目录,该目录下有两个子模块。一个是 member-portal 模块,一个是 platform-portal 模块,前者接收前台的接口参数,后者接收后台的接口参数。但不论哪个模块,都需要依赖 plateform-custom 中的事务层方法,同时,我们传的参数,可能信息分发的 platform-cms-controller 中的接口,也可能是核心接口 platform-core-controller 中的接口。因而,我们这里以 member-portal 模块来举例,依赖其他模块的代码如下:
<dependencies>
<dependency>
<groupId>com.zfounder.platform</groupId>
<artifactId>platform-core-controller</artifactId>
</dependency>
<dependency>
<groupId>com.zfounder.platform</groupId>
<artifactId>platform-cms-controller</artifactId>
</dependency>
<dependency>
<groupId>com.zfounder.platform</groupId>
<artifactId>platform-custom</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
这些模块你会在上面的图片找得到。同时,我们来看 member-portal 的 pom 文件继承的父 pom 是怎么写的:
补充上面的继承关系。这里面用到了 <relativePath>../../pom.xml</relativePath> 你会奇怪的是,为什么这里面用到了呢?其和父 pom 不是父子关系,而是孙子关系。这里使用到了两次点点,这是什么意思呢?.. 表示上级目录。举个例子说明:
比如,在我的服务器上的 www 目录中,有三个文件,分别是 rsa_private_key.pem, rsa_private_key_pkcs8.pem, rsa_public_key.pem,还有一个 testDir 目录,testDir 目录中还有目录 testDir,现在我们通过 cd ../testDir/testDir 进入到子目录中,现在,我们想返回到 www 的根目录中,并查看 rsa_public_key.pem 文件的内容,因而,我们可以用 cat ../../rsa_public_key.pem 命令,其首先返回两级目录,然后找到 rsa_public_key.pem 文件并打开该文件。
“被继承的父 pom 与继承 pom 的目录结构是不是父子关系”也不是绝对的,主要是选择继承者的 pom 中的子目录和父目录之间的关系,其中间隔了几层目录。
maven 激活文件
激活文件在上文也提到了,我们为什么需要激活文件?如下面的两个配置文件,一个是测试环境的配置文件,名为 platform-dev.properties,一个是正式环境的配置文件,名为 platform-prd.properties。两个配置文件中都存在与数据库的连接,但是呢,数据库的 ip 地址是不一样的。如一下的代码所示:
正式服的 platform-prd.properties 配置文件
jdbc.url=jdbc:mysql://localhost/prd_database
jdbc.username=prd_username
jdbc.password=prd_password
jdbc.validationQuery=select 1 from dual
jdbc.removeAbandonedTimeout=180
jdbc.initialSize=10
jdbc.minIdle=30
jdbc.maxActive=100
jdbc.maxWait=30000
。。。
测试服的 platform-dev.properties 配置文件
jdbc.url=jdbc:mysql://intranet_ip/dev_database
jdbc.username=dev_username
jdbc.password=dev_password
jdbc.validationQuery=select 1 from dual
jdbc.removeAbandonedTimeout=180
jdbc.initialSize=10
jdbc.minIdle=30
jdbc.maxActive=100
jdbc.maxWait=30000
。。。
我们的在配置文件中配置好了数据项,但是呢,我们怎么切换不同的配置文件呢?换句话说,我们怎么想要打正式服的包放到正式服上,怎么选择 platform-prd.properties 的配置文件呢?反之,怎么选择 platform-dev.properties 配置文件?
这时,我们就用到了 maven 当中的 profile 标签,如下代码所示:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<filters>
<filter>../../platform-dev.properties</filter>
</filters>
</build>
</profile>
<profile>
<id>prd</id>
<build>
<filters>
<filter>../../platform-prd.properties</filter>
</filters>
</build>
</profile>
</profiles>
这些配置文件时写在 member-portal、platform-portal、plateform-core 和 plateform-cms、plateform-customer 模块的 pom 中的。但是,plateform-core 和 plateform-cms 的配置中的 filter 和上面连个略有差异,其 filter 是这样的 <filter>../platform-dev.properties</filter> 和 <filter>../platform-prd.properties</filter>,这就涉及到目录点的问题。
maven 依赖第三方包
maven 项目除了依赖本项目的,其还会依赖第三方包,比如自动生成 set 和 get 方法的 lombok 包,处理 json 格式的阿里巴巴下的 fastjson 包等等,我们也可以使用这种格式的依赖:
<!–mysql jdbc 驱动包 开始 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${jdbc.version}</version>
</dependency>
<!–mysql jdbc 驱动包 结束 –>
开发常用的 jar 包
lombok
<!– lombok 驱动包 开始 –>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!– lombok 驱动包 开始 –>
我们在没有使用 lombok 之前,经常手动创建 javabean 的 set 个 get 方法,使用这个框架之后,其以注解的方式,可以自动生成 set 和 get 方法。同时,其强大的功能远不止这些,也可以生成无参构造器、全参构造器,指定参数构造器、重写 toString 方法、重写 Hashcode、equals 方法等等。
如代码所示:
/**
* Created By zby on 17:37 2019/1/30
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class Address {
/**
* 收货人
*/
private String consignee;
/**
* 手机号码
*/
private String phone;
/**
* 所在地区
*/
private String area;
/**
* 详细地址
*/
private String detail;
/**
* 标签
*/
private AddressTagEnum addressTag;
}
想要更深层次的了解这个框架,可以参考这个博客:Lombok 使用详解
fastjson
<!– fastjson 驱动包 开始 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<!– fastjson 驱动包 结束 –>
fastjson 是阿里巴巴开源的框架,其用来处理服务器端的 json 格式的数据。比如,我们需要将服务端的对象以 json(JavaScript object Notation,js 对象标记)格式传输到前端,但是,如果自己手动创建的话,势必会非常的麻烦,于是,我们借助这个框架,帮助我们生成 json 格式的对象。
同时,如果我们要调用第三方接口,比如调用连连绑定银行卡的接口,其返回给我们的也是 json 对象的数据。但是,我们需要将其转化为我们定义的对象,调用其 save 方法,保存到数据库中,对账所用。
对于,将对象转化为 json 格式的对象,如代码所示:
@Test
public void test() {
// 地址
Address address = new Address();
address.setAddressTag(AddressTagEnum.ADDRESS_TAG_COMPANY);
address.setArea(“ 杭州市 ….”);
address.setConsignee(“zby”);
// 用户
User user = new User();
user.setHobby(HobbyEnum.HOBBY_DANCING);
user.setGender(“ 男 ”);
user.setUserName(“ 蒋三 ”);
// 订单
OrderSnapshot orderSnapshot = new OrderSnapshot();
orderSnapshot.setAddress(address);
orderSnapshot.setId(1L);
orderSnapshot.setName(“ 复读机 ”);
orderSnapshot.setOrderNo(Long.valueOf(System.currentTimeMillis()).toString() + “1L”);
orderSnapshot.setUser(user);
System.out.println(JSON.toJSON(orderSnapshot));
}
其输出结果如图所示:
但是,类似于解析 json 格式的数据,不只有 fastjson 框,还有 org.json 框架、Jackson 框架。但经过有人验证呢,还是 fastjson 的效率更高一些。可以参考这篇博客:Gson、FastJson、org.JSON 到底哪一个效率更高,速度更快
org.json 也是通过 maven 配置的,如代码所示:
<!– json 驱动包 开始 –>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20140107</version>
</dependency>
<!– json 驱动包 开始 –>
如果想要深层次了解 org.json,可以参考这篇博客:Java 使用 org.json.jar 构造和解析 Json 数据
想要更深层次的了解 fastjson,可以参考这篇博客:Fastjson 简明教程
spring 相关配置
如果从事 java-web 开发,一般会用到 spring 框架,这方面的教程太多了,笔者就不在这介绍,但我们会用到 spring 的这些框架:
<!–spring 相关配置开始 –>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
spring 会集合很多框架,比如具有拦截效果的 shiro 框架,持久层的 hibernate 和 mybatis 框架等等。spring 通过配置文件通过注解或者配置文件的方式,实现依赖注入(dependency injection)和控制反转(inversion of control)。通过 @controller 遍历相应的接口,实现前后端的接口对接。spring 可以实现面向切面编程,实现某个业务点的单一执行。比如,专门处理事务的业务点。
spring 并不难,很快就能掌握到其精髓。
如果想深入了解,可以参考这篇教程:Spring 教程
hibernate 框架
hibernate 框架就类似于 mybatis 框架,其专门处理持久层的技术。我们将瞬时状态的对象存储到数据库中,变成持久状态的对象。我们也可以从数据库中取数据,以瞬时态的对象返回到前端。这就是一存一取的框架。
其可以使用注解的方式创建数据表,也可以通过配置文件创建瞬时态的对象。但就目前为止,在很多情况下,我们都是通过注解的方式,实现数据表的创建。导入 hibernate 相关的框架,如下所示:
<!–hibernate 相关配置 开始 –>
<!–hibernateh 核心框架 –>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
<!–hibernateh 验证器 –>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!–hibernateh 缓存技术 –>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
</dependency>
<!–Java Persistence API ORM 映射元数据 查询语言 –>
<dependency>
<groupId>org.hibernate.java-persistence</groupId>
<artifactId>jpa-api</artifactId>
</dependency>
<!–hibernate 相关配置 结束 –>
hibernate 和 mybatis 具有同样的功能,如果想要了解 mybatis,可以参考这篇教程:mybatis 教程
想要深入理解 hibernate,可参考这篇教程:hibernate 教程_w3cschool
jbdc 驱动包
我们上面说了 hibernate 框架,但前提是,我们需要导入 jdbc 的框架包,如代码所示:
<!– 数据库驱动包相关 开始 –>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!– 数据库驱动包相关 结束 –>
这个驱动包主要处理 java 与数据库的连接,实现数据的增、删、改、查。我们没有用到这个包,但是 hibernate 用到了这个包,因而,我们需要导入这个包,以免数据库报错。
alibaba 的 Druid 包
这个包有什么用吗?我们既然通过 hibernate 实现与数据库的交互,那么就需要在初始化时创建连接池。现在连接池有很多种,我们为什么选择了它 Druid。
<!– Druid 驱动包相关 开始 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!– Druid 驱动包相关 结束 –>
它是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括 DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid 已经在阿里巴巴部署了超过 600 个应用,经过一年多生产环境大规模部署的严苛考验。Druid 是阿里巴巴开发的号称为监控而生的数据库连接池!
时代在变化,我们也应该应世而生,与时俱进,才不会和时代脱轨。
我们使用 Druid 来实现数据的配置,如代码所示:
<bean id=”dataSource” class=”com.alibaba.druid.pool.DruidDataSource”
init-method=”init” destroy-method=”close”>
<property name=”driverClassName” value=”com.mysql.jdbc.Driver”/>
<property name=”url” value=”${jdbc.url}”/>
<property name=”username” value=”${jdbc.username}”/>
<property name=”password” value=”${jdbc.password}”/>
<property name=”maxActive” value=”${jdbc.maxActive}”/>
<property name=”initialSize” value=”${jdbc.initialSize}”/>
<property name=”removeAbandoned” value=”true”/>
<property name=”removeAbandonedTimeout” value=”${jdbc.removeAbandonedTimeout}”/>
<property name=”testOnBorrow” value=”true”/>
<property name=”minIdle” value=”${jdbc.minIdle}”/>
<property name=”maxWait” value=”${jdbc.maxWait}”/>
<property name=”validationQuery” value=”${jdbc.validationQuery}”/>
<property name=”connectionProperties” value=”clientEncoding=UTF-8″/>
</bean>
你们可以看到,value 值是形参,而不是具体的值。因为我们根据不同的打包方式,其传入形参对应的实参不同。这也就是我们上文提到的,platform-dev.properties 和 platform-prd.properties 配置文件,以及 maven 配置的激活文件。
如果想要深入了解阿里巴巴的 Druid 框架,可以参考这篇博客:DRUID 连接池的实用 配置详解
阿里云短信
短信业务一般固定不变,由架构师封装好,其他人直接调用即可,因而,该框架可以写进 plateform-core 模块中,其配置的代码如下所示:
<!– 阿里短息驱动包配置 开始 –>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<!– 阿里短息驱动包配置 结束 –>
日志相关配置
我们在开发的过程中,经常会使用到日志,来记录相应的错误、警告、信息。比如,我在使用连连支付做提现业务时,提现成功后其会回调我们的接口,从而显示在服务端的 Tomcat 页面中。再比如,我们在登录时,其会在 Tomcat 中显示相关信息,如图所示:
我们都知道日志分为几种级别。这里就不再赘述了。日志分为好多种,我们推荐使用 slf4j+logback 模式。因为 logback 自身实现了 slf4j 的接口,无须额外引入适配器,另外 logback 是 log4j 的升级版,具备比 log4j 更多的优点,我们可以通过如下配置进行集成:
<!– 日志驱动包配置 开始 –>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.7</version>
</dependency>
<!– 日志驱动包配置 结束 –>
我们这时就用到了 plateform-prd.properties 文件和 plateform-dev.properties 文件,因为,我们需要在这里面配置日志的输出位置。然后,在 logback.xml 中以参数的形式,调用文件中的输出位置,如图所示:
如果想要了解更多的配置文件信息,请参考这篇博客:使用 logback + slf4j 进行日志记录
commons 家族
我们在开发的过程中,经常用到 Commons 家族的驱动包,比如文件操作的 io 包,MD5 加密和解密用的 codec 包。当然,我们也会用到 java 自带的 local_policy 驱动包,但有时需要替换替换该驱动包,否则,就会报出 Illegal Key Size 的错误。文件上传下载的 fileupload 驱动包,操作字符串类型的 lang3 包,配置的驱动包如下所示:
<!–comon 包相关配置 –>
<commons-io.version>2.4</commons-io.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<!– apache common 开始 –>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!– apache common 结束 –>
lang3 包
我们可以用其分割字符串,判断字符串是否为空格,判断字符串是否为空等等。如以下代码所示:
public static void main(String[] args) {
String keyword = “1-1-2”;
if (StringUtils.isNotBlank(keyword)) {
System.out.println(“keyword = ” + keyword);
}
String[] keys = StringUtils.split(keyword, “-“);
for (String key : keys) {
System.out.println(“key=” + key);
}
}
我们有时还会用其操作时间类,比如格式化时间等等,入一下代码:
@Test
public void testDate(){
String date1= FastDateFormat.getInstance(“yyyy-MM-dd”).format(System.currentTimeMillis());
System.out.println(“System.currentTimeMillis:”+date1);
String date2= FastDateFormat.getInstance(“yyyy-MM-dd”).format(new Date());
System.out.println(“new Date:”+date2);
}
其功能远不止这些,具体可以参考这篇博客:commons-lang3 工具包
io 包
见名知意,IO 即 input 和 output 的简写,即输入流和输出流。因而,我们经常使用到 java 自带的 InputStream 或 FileInputStream 的字节输入流,以及 OutputStream 或 FileOutputStream 的输出流。如果更高级的话,那么,就使用到了带有缓存效果的 bufferReader 输入流和 bufferWrite 输出流。这里面用到了装饰设计模式。什么是装修设计模式,可以自行学习。
上面的操作比较复杂,我们就用到了 apache 下的 io 驱动包。这里就当做抛砖引玉了,想要有更深的了解,可以参考这篇博客:io 包工具类
codec 包
codec 包是 Commons 家族中的加密和解密用的包,这里不做任何解释,具体可以参考这篇博客:Commons Codec 基本使用
fileupload 包
我们如果做 java-web 开发,经常会有文件上传和文件下载的功能。这时,我们就考虑到了 Apache 下面的 fileupload 包,这可以完成文件的上传和下载。这里的文件不单单是指 doc 文件,也会指图片和视频文件。
具体想要有更多的理解,可以参考这篇文档:commons-fileupload 上传下载
shiro 包
我们在 web 开发时,经常会涉及到权限问题,比如哪些页面不需要登录就能看,而哪些页面只能登录才能看。当用户在打开该页面之前,就进入到相应的过滤器中,来做相关业务的判断。如果通过,就进入到 controller 层;不通过,则抛出相应的异常给前端。这里就需要相应的权限控制。
说到权限控制,我们不得不提到 shiro 框架。其有三大核心组件 Subject, SecurityManager 和 Realms。这个百度百科上也说了,可以查看其解说内容:java 安全框架
<!– shiro 驱动包 开始 –>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
</dependency>
<!–shiro 驱动包 结束 –>
公司也会做相应的配置,配置如下:
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns:util=”http://www.springframework.org/schema/util”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns=”http://www.springframework.org/schema/beans”
xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd”>
<!– 缓存管理器 –>
<bean id=”cacheManager” class=”com.*.shared.framework.SpringCacheManagerWrapper”>
<property name=”cacheManager” ref=”springCacheManager”/>
</bean>
<bean id=”springCacheManager” class=”org.springframework.cache.ehcache.EhCacheCacheManager”>
<property name=”cacheManager” ref=”ehcacheManager”/>
</bean>
<bean id=”ehcacheManager” class=”org.springframework.cache.ehcache.EhCacheManagerFactoryBean”>
<property name=”configLocation” value=”classpath:ehcache.xml”/>
</bean>
<!– 凭证匹配器 –>
<bean id=”credentialsMatcher”
class=”com.*.RetryLimitHashedCredentialsMatcher”>
<constructor-arg ref=”cacheManager”/>
<property name=”hashAlgorithmName” value=”md5″/>
<property name=”hashIterations” value=”2″/>
<property name=”storedCredentialsHexEncoded” value=”true”/>
</bean>
<!– Realm 实现 –>
<bean id=”userRealm” class=”com.**.shared.web.listener.MemberSecurityRealm”>
<!–<property name=”credentialsMatcher” ref=”credentialsMatcher”/>–>
<property name=”cachingEnabled” value=”false”/>
<!–<property name=”authenticationCachingEnabled” value=”true”/>–>
<!–<property name=”authenticationCacheName” value=”authenticationCache”/>–>
<!–<property name=”authorizationCachingEnabled” value=”true”/>–>
<!–<property name=”authorizationCacheName” value=”authorizationCache”/>–>
</bean>
<!– 会话 ID 生成器 –>
<!–<bean id=”sessionIdGenerator” class=”org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator”/>–>
<!– 会话 Cookie 模板 –>
<bean id=”sessionIdCookie” class=”org.apache.shiro.web.servlet.SimpleCookie”>
<constructor-arg value=”platform-portal-sid”/>
<property name=”httpOnly” value=”true”/>
<property name=”maxAge” value=”7200″/>
</bean>
<!– 会话管理器 –>
<bean id=”sessionManager” class=”org.apache.shiro.web.session.mgt.DefaultWebSessionManager”>
<property name=”globalSessionTimeout” value=”43200000″/>
<property name=”deleteInvalidSessions” value=”true”/>
<property name=”sessionIdCookieEnabled” value=”true”/>
<property name=”sessionIdCookie” ref=”sessionIdCookie”/>
</bean>
<!– 安全管理器 –>
<bean id=”securityManager” class=”org.apache.shiro.web.mgt.DefaultWebSecurityManager”>
<property name=”realm” ref=”userRealm”/>
<property name=”sessionManager” ref=”sessionManager”/>
<property name=”cacheManager” ref=”cacheManager”/>
</bean>
<!– 相当于调用 SecurityUtils.setSecurityManager(securityManager) –>
<bean class=”org.springframework.beans.factory.config.MethodInvokingFactoryBean”>
<property name=”staticMethod” value=”org.apache.shiro.SecurityUtils.setSecurityManager”/>
<property name=”arguments” ref=”securityManager”/>
</bean>
<!– Shiro 的 Web 过滤器 –>
<bean id=”shiroFilter” class=”org.apache.shiro.spring.web.ShiroFilterFactoryBean”
depends-on=”securityManager,memberShiroFilerChainManager”>
<property name=”securityManager” ref=”securityManager”/>
</bean>
<!– 基于 url+ 角色的身份验证过滤器 –>
<bean id=”urlAuthFilter” class=”com.zfounder.platform.core.shared.web.filter.UrlAuthFilter”>
<property name=”ignoreCheckUriList”>
<list>
<value>/**/common/enums/**</value>
<value>/**/security/**</value>
<value>/**/common/dd/**</value>
<value>/**/pictures/**</value>
<value>/**/common/sms/**</value>
<value>/**/wx/**</value>
</list>
</property>
</bean>
<bean id=”memberFilterChainManager”
class=”com.zfounder.platform.core.shared.web.listener.CustomDefaultFilterChainManager”>
<property name=”customFilters”>
<util:map>
<entry key=”roles” value-ref=”urlAuthFilter”/>
</util:map>
</property>
</bean>
<bean id=”memberFilterChainResolver”
class=”com.**.shared.web.listener.CustomPathMatchingFilterChainResolver”>
<property name=”customDefaultFilterChainManager” ref=”memberFilterChainManager”/>
</bean>
<bean class=”org.springframework.beans.factory.config.MethodInvokingFactoryBean” depends-on=”shiroFilter”>
<property name=”targetObject” ref=”shiroFilter”/>
<property name=”targetMethod” value=”setFilterChainResolver”/>
<property name=”arguments” ref=”memberFilterChainResolver”/>
</bean>
<!– Shiro 生命周期处理器 –>
<bean id=”lifecycleBeanPostProcessor” class=”org.apache.shiro.spring.LifecycleBeanPostProcessor”/>
</beans>
想要对其有更深的理解,请参考这篇博客:Shiro 讲解
工具类
<!– 汉字转拼音开源工具包 –>
<dependency>
<groupId>com.github.stuxuhai</groupId>
<artifactId>jpinyin</artifactId>
</dependency>
<!– 网络爬虫的驱动包 –>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<!– 验证码生成工具包 –>
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
</dependency>
<!– 发送邮件 –>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</dependency>
因为篇幅的限制,这里就不再细说了,如果想要更深层次的了解,可以参考以下博客:
汉字转拼音开源工具包 Jpinyin 介绍
爬虫 +jsoup 轻松爬知乎
使用 kaptcha 生成验证码
使用 javax.mail 发送邮件
图片验证码的配置文件如下:
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns=”http://www.springframework.org/schema/beans”
xsi:schemaLocation=”http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd”
default-lazy-init=”true”>
<bean id=”captchaProducer” class=”com.google.code.kaptcha.impl.DefaultKaptcha”>
<property name=”config”>
<bean class=”com.google.code.kaptcha.util.Config”>
<constructor-arg>
<props>
<prop key=”kaptcha.border”>${kaptcha.border}</prop>
<prop key=”kaptcha.border.color”>${kaptcha.border.color}</prop>
<prop key=”kaptcha.textproducer.font.color”>${kaptcha.textproducer.font.color}</prop>
<prop key=”kaptcha.textproducer.char.space”>${kaptcha.textproducer.char.space}</prop>
<prop key=”kaptcha.textproducer.font.size”>${kaptcha.textproducer.font.size}</prop>
<prop key=”kaptcha.image.width”>${kaptcha.image.width}</prop>
<prop key=”kaptcha.image.height”>${kaptcha.image.height}</prop>
<prop key=”kaptcha.textproducer.char.length”>${kaptcha.textproducer.char.length}</prop>
<prop key=”kaptcha.textproducer.char.string”>1234567890</prop>
<prop key=”kaptcha.textproducer.font.names”> 宋体, 楷体, 微软雅黑 </prop>
<prop key=”kaptcha.noise.color”>${kaptcha.noise.color}</prop>
<prop key=”kaptcha.noise.impl”>com.google.code.kaptcha.impl.NoNoise</prop>
<prop key=”kaptcha.background.clear.from”>${kaptcha.background.clear.from}</prop>
<prop key=”kaptcha.background.clear.to”>${kaptcha.background.clear.to}</prop>
<prop key=”kaptcha.word.impl”>com.google.code.kaptcha.text.impl.DefaultWordRenderer</prop>
<prop key=”kaptcha.obscurificator.impl”>com.google.code.kaptcha.impl.ShadowGimpy</prop>
</props>
</constructor-arg>
</bean>
</property>
</bean>
</beans>
里面的占位符来源于 plateform-dev.properties 或者 plateform-prd.properties,这就是我们 maven 激活的配置文件的作用。
测试依赖包
我们在开发完一个功能后,首先会想到测试它走不走得通。我们可能会在 main 方法中测试,一个项目类中可以写多个 main 方法。如果每个功能类中都写一个 main 方法,未免会造成代码的混乱,一点都不美观和儒雅。
java 为什么一直推崇面向对象,任何在现实中真实的、虚拟的事物,都可以将其封装为为 java 中的对象类。对象与对象之间以方法作为消息传递机制,以属性作为数据库存储的机制。
如果我们在每个功能中都写一个 main 方法,势必会破坏这种对象的美观性。因而,我们把测试的数据以对象的方式操作,这样,将其封装为一个测试包,比如,在我写的 spring 框架中,就把测试类单独拿出来,如图所示:
<!– 测试依赖包 开始 –>
<!– spring test –>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!– 路径检索 json 或设置 Json –>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>${jsonpath.version}</version>
<scope>test</scope>
</dependency>
<!– testng –>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
</dependency>
<!– 单元测试的 powermock –>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
</dependency>
<!– 测试相关 结束 –>
以上是几种测试包的依赖,一个是 spring 的测试包,这里由于篇幅的限制,就不做详细的介绍了,网上有很多这方面的教程,想要深入的了解,可参考这篇博客:Spring-Test(单元测试)
我们有时也会用到 TestNG 框架,它是 Java 中的一个测试框架,类似于 JUnit 和 NUnit,功能都差不多,只是功能更加强大,使用也更方便。测试人员一般用 TestNG 来写自动化测试,开发人员一般用 JUnit 写单元测试。如果你是测试人员,想对其有更全面的了解,可以参考这篇教程:TestNG 教程,或者这篇博客::testNG 常用用法总结
如果想要更深层次的了解 powermock,可以参考这篇博客:PowerMock 从入门到放弃再到使用
如果想要更深层次的了解 JsonPath,可以参考这篇博客:JsonPath 教程
图片处理
我们在开发的过程中,会把图片存放到服务器的某个文件夹下,即某个磁盘上。如果图片过大,会占用服务器的磁盘,因而,我们需要将图片缩略,来减少对内存的占用。这时,我们如果使用 java 原生的图片缩略图,是非常复杂的,因而,我们可以使用以下框架对图片进行操作。
<!– 图片处理驱动包 开始 –>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
</dependency>
<!– 图片处理驱动包 结束 –>
这里不再细说,想要有更多的了解,可以参考这篇博客:Thumbnailator 框架的使用
Excel 操作
我们在工作的过程中,经常会将数据导出到 Excel 表,或将 Excel 表的数据导入数据库。我们以前使用 poi 框架,但是,超过一定量的时候,会占用大量的内存,从而降低导入的效率。阿里巴巴现在开放出操作 Excel 表的 easyexcel 框架,对百万级的导入影响不是很大。
以下是 maven 配置两个驱动依赖:
<!– 阿里巴巴的 easyexcel 驱动包 –>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>{latestVersion}</version>
</dependency>
<!–poi 驱动包 –>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi-ooxml.version}</version>
</dependency>
这两个就不再细说,如果想要对 easyexcel 更深的了解,可以参考这篇博客:alibaba/easyexcel 框架使用。如果想要对 poi 有更深的了解,可以参考这篇博客:Apache POI 使用详解
guava 包
我们在开发的过程中,有时会用到 guava 驱动包。它是为了方便编码,并减少编码错误,用于提供集合,缓存,支持原语句,并发性,常见注解,字符串处理,I/ O 和验证的实用方法。
使用它有以下好处:
标准化 – Guava 库是由谷歌托管。
高效 – 可靠,快速和有效的扩展 JAVA 标准库
优化 -Guava 库经过高度的优化。
同时,又有增加 Java 功能和处理能力的函数式编程,提供了需要在应用程序中开发的许多实用程序类的,提供了标准的故障安全验证机制,强调了最佳的做法等等。它的宗旨就是:提高代码质量、简化工作,促使代码更有弹性、更加简洁的工具。
我们在项目中的配置包为:
<!–guava 驱动包 开始 –>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!–guava 驱动包 结束 –>
如果想要对其有更深的了解,可以参考这篇教程:guava 入门教程
freemarker 包
我们在开发的过程中,也许会用到这个框架。为什么要用到这个框架呢?我们有时需要动态地将 xml 文件转为 doc 文件,这个时候,就用到了 freemarker 包,如图所示:
截图不是很全面,你会看到画红框的部分,这是一种占位符的标记,就相当于 java 中的形参一样。当用户点击前端的下载按钮时,有些数据是无法直接转换成 doc 的,因为我们先把数据写进 xml 中,再将 xml 转化为 doc。具体如何转换的可以参考该博客:Java 将 xml 模板动态填充数据转换为 word 文档
我们可以引用这个包:
<!–freemarker 驱动包 开始 –>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
<!–freemarker 驱动包 结束 –>
由于篇幅限制,想要详细了解,可以参考这篇手册:freemarker 在线手册
servlet 驱动包
我记得当时在学 java-web 开发时,最开始用的就是 servlet。接收客户端的输入,并经过一系列 DB 操作,将数据返回给客户端。但使用纯 servlet 不利于可视化界面。后来,使用了 JSP 开发,其是可视化界面。但是,当我们启动 Tomcat 后,JSP 通过 JSP 引擎还是会转为 servlet。从本质上来说,JSP 和 servlet 是服务端语言。
我最初用 servlet 和 JSP 开发的源码地址:图书馆项目
后来,工作了以后。后端就用了 springMVC,hibernate 框架等,前端使用的是 HTML、jQuery 等。慢慢地脱离了 JSP 和 servlet。但是,并没与完全与 servlet 分隔开,我们还时不时会用到 servlet 的一些类,比如 HttpServletRequest,HttpServletResponse 等类。
既然使用了 spring MVC 框架,为什么还要用 servlet 的东西,比如,我们在导入和导出时,一个是接收前端导入的请求,一个是响应前端导出的请求。
响应前端导出的代码,这里就用到了响应
private static void downloadExcel(HttpServletResponse response, File newFile, String fileName) throws IOException {
InputStream fis = new BufferedInputStream(new FileInputStream(
newFile));
String substring = fileName.substring(fileName.indexOf(“/”) + 1);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
response.setContentType(“text/html;charset=UTF-8”);
OutputStream toClient = new BufferedOutputStream(
response.getOutputStream());
response.setContentType(“application/x-msdownload”);
String newName = URLEncoder.encode(
substring + System.currentTimeMillis() + “.xlsx”,
“UTF-8”);
response.addHeader(“Content-Disposition”,
“attachment;filename=\”” + newName + “\””);
response.addHeader(“Content-Length”, “” + newFile.length());
toClient.write(buffer);
toClient.flush();
}
接收前端导入的请求
public static LinkedHashMap<String, List<JSONObject>> importMultiSheetExcel(HttpServletRequest request, LinkedHashMap<Integer, Integer> sheetDataStartRowMap, LinkedHashMap<Integer, String> sheetDataEndColMap) {
LinkedHashMap<String, List<JSONObject>> resMap = new LinkedHashMap<>();
try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
ifNullThrow(multipartRequest, ResultCodeEnum.ILLEGAL_PARAM);
MultipartFile file = multipartRequest.getFile(“file”);
Workbook work = getWorkbook(file.getInputStream(), file.getOriginalFilename());
ifNullThrow(work, ResultCodeEnum.ILLEGAL_PARAM);
。。。
}
虽然我们现在使用了 spring MVC,还是用到了 servlet,而且 shiro 里面要使用到,以下是代码的配置:
<!–servlet 开始 –>
<!–shiro 里面要使用到 –>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
</dependency>
<!–servlet 结束 –>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<!–servlet 结束 –>
如果想要了解 servlet 的话,可以参考该文档:Java Servlet API 中文说明文档
Lucene 全文检索
有时,我们在开发的过程中,需要做全文检索数据,就比如,我在 Word 文档中,全文检索某个词、某句话等,如图所示:
这就是 web 端的全文检索。但是我做 Java,当然,也需要全文检索。因而,我们就想到了 Lucene。
它是一套用于全文检索和搜寻的开源程式库,由 Apache 软件基金会支持和提供。提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在 Java 开发环境里,它是一个成熟的免费开源工具。就其本身而言,它是当前以及最近几年最受欢迎的免费 Java 信息检索程序库。
我们在 java 的 maven 库中的配置为:
<!– lucene 开始 –>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
<!– lucene 结束 –>
想要对其有更深的了解,可以参考这篇笔记:Lucene 学习笔记
Quartz 任务调度
我们在开发的过程中,总想着要在某个时间,执行什么样的事情,于是呢,我们就相当了任务调度,比如:
每天八点按时起床
每年农历什么的生日
每个星期都要爬一次山
我们就可以用到 Quartz 这个框架,我们需要做一些配置,如图所示:
我们可以在 maven 中的配置为:
<!– quartz 驱动包 开始 –>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>${quartz.version}</version>
</dependency>
<!– quartz 驱动包 结束 –>
想要对其有根深多的了解,可参考这篇博客:Quartz 使用总结
zxing 二维码
我们经常使用到二维码,比如,添加微信好友的二维码,支付二维码、扫一扫二维码等等,那么,这是怎么实现的呢,其实,这有一个工具包,就是 zxing 工具包。它是谷歌旗下的工具类,我们可以用它来生成我们想要的二维码,但是,我们先要在 maven 项目中配置它。如代码所示:
<!– 二维码驱动包 开始 –>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${zxing.se.version}</version>
</dependency>
<!– 二维码驱动包 开始 –>
想要对其有根深的了解,可以参考这篇博客:zxing 实现二维码生成和解析
WSDL 包
这个我也不大懂,也没有操作过,如果想要了解的话,可以参考这篇文档:WebService 中的 WSDL 详细解析
我们在 maven 中的怕配置为:
<!– WSDL 驱动包 开始 –>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>${wsdl4j.version}</version>
</dependency>
<!– WSDL 驱动包 结束 –>
配置文件
配置文件来源于框架中的以下文件,如图所示:
所有的配置文件都来源于资源包。这里就不再细说。
总结
要想成为架构师,首先学习别人的东西,他山之石,可以攻玉。