Maven
Maven是一款帮助我们管理项目的依赖关系并快速构建打包的一款工具。
pom.xml
Maven作为一款依赖包管理工具,项目信息、模块信息、依赖信息等元数据必不可少,而pom.xml就是一个通过xml格式来告诉Maven这些元数据的文件。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>
<properties>
<spring.version>3.2.9.RELEASE</spring.version>
</properties>
<parent/>
<packaging/>
<groupId>per.jw</groupId>
<artifactId>hello-spring-pigeon</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency />
</dependencies>
<build>
<plugins>
<plugin></plugin>
</plugins>
</build>
</project>
其中,也被称为GAV信息,Maven通过GAV可以唯一标识具体版本的某一项目,因此不管是咱们的项目本身,抑或添加的依赖,都需要有GAV信息。可以定义一些变量,通过${varname}
就可以访问到。是指明父级的pom.xml,子级的pom会继承父级pom添加的依赖。表示工程项目的打包方式,默认为jar,还有pom、war等等。标签即为工程项目所有的依赖内容。标签用来标识打包时的插件等属性,用不同的插件打包有不同的效果,常用插件有maven-jar-plugin、maven-shade-plugin、maven-ensemble-plugin。
可运行Jar包
在这边插入讲一下打Jar包的事情。当我们完成我们项目的业务代码时,要怎么将其运行起来呢?
“打Jar包”,也就是俗称的“打包”,指的是将我们的Java源代码编译完成,并且生成一个.jar压缩文件。而由于我们的源代码中使用了很多第三方依赖,那么在生成.jar文件时是否把这些第三方依赖加入进去就有了不同的策略。第一种,加入第三方的包,这样的jar文件会比较大,但是我们可以直接在不同的使用环境下执行java -jar someclass.jar
即可运行文件。第二种,不加入第三方包,这样的.jar文件不能直接运行,一般作为第三方包提供出去给别的开发者使用。
可运行的Jar包必须在其中的META-INF/MANIFEST.MF文件(Jar包元数据文件)中指定主类的名字,这样执行java -jar someclass.jar
时直接执行主类中的静态main方法。若不指定,或者有多个主类,抑或主类中有多个方法,可在执行时加上参数java -jar someclass.jar xx.xx.className [args]
指定类与方法。
Jar包不是打包java文件的唯一选择,另有一些War包、Ear包等,其区别与应用场景另述。
仓库设置
我们只需在pom中指定依赖的GAV信息,Maven就会帮我们在***本地仓库***中寻找对应依赖包,若未找到,就会从***远程仓库***下载到本地仓库。一般而言,Maven会默认从自己的中央仓库来下载这些依赖。所谓的中央仓库,就是Maven的一个公开仓库,开源作者将自己的开源项目不同的版本Jar都上传至此,一般常用的开源Jar包这里都是有的。默认的中央仓库存在两个问题:
- 服务器在国外,网速可能不大稳定。
- 一些私有的项目并未上传到中央仓库。
因此,远程仓库的位置是可以手动配置的。Maven本地仓库位置为${user.home}/.m2/repository
,只需在该目录下新建一个setting.xml
文件就自定义远程仓库位置等属性。
依赖传递
当Maven帮我们引入依赖包的时候,也会引入依赖包的依赖包,这种属性就叫做依赖传递。当我们依赖包的数量十分巨大的时候,就会产生以下***依赖冲突***问题,即存在多个相同GA却不同V的依赖包。例如有这样的依赖关系:A->B->C,A->C,那么如果两个C的版本不一就会带来问题。
不过Maven给我们提供了一个解决方案,那就是短路径依赖优先策略,运用到上例中则Maven只会引入A->C的C而忽略依赖链更长的C。那么如果依赖链一样长呢,就很容易出现冲突问题。对此的通用解决方案是在pom中对应依赖包的标签下使用忽略掉该包的一些指定依赖包。对此,另一种包管理工具gradle似乎有更为优雅的解决方案。
SpringFramwork
简要介绍
在使用Spring之前,我们先理清几个概念:Spring、SpringMVC、SpringBoot。
结合网络资料、官方文档,个人理解如下:Spring是最先提出的管理POJO相互依赖、提供面向切面编程功能的Java开发框架,官网的项目名称为SpringFramwork,其特色就是我们熟知的IoC和AOP,其作用在于管理项目中各种各样的Bean以及互相之间的依赖关系、并且提供AOP功能。后来这个框架十分好用,Spring团队就推出了一个子项目SpringMVC。SpringMVC是一个基于Servelet与MVC设计模式的JavaWEB开发框架,其作用在于通过抽象Dispatcher Servlet、ModelAndView、View Resolver等处理模块,使得开发WEB应用更为简便。再后来,由于Spring原本通过xml配置的繁琐操作,Spring基于“约定优于配置”这一思想提出了基于注解的配置方式,同时在WEB开发框架上成功应用,由此又提出了简化的SpringBoot框架。参考
主要配置
与Maven类似地,Spring是一个管理Bean的框架,那么就需要Bean的各种元数据。一般有三种方法可以完成配置:基于xml的方法、基于Annotation的方法和基于Java语言的方法。基于xml的方法是Spring最早提出的一种方法,很多大型项目仍然使用这种方法进行配置,缺点是xml文件会比较繁琐。因此目前在SpringBoot等较新的框架中会比较推荐使用annotation的方式。
<beans>
<bean id="timeService" class="per.jw.TimeService" init-method="init">
<property name="processorChain">
<list>
<ref bean="generalProcessorNode"/>
</list>
</property>
<property name="bizName" value="feeds"/>
<property name="conditionMap">
<map>
<entry key="searchFeeds"><ref bean="searchFeedsCondition"></ref></entry>
</map>
</property>
</bean>
</beans>
如上所示,表示bean节点,其中的标签为bean的标识号,在命名空间中不能重复,可以使用标签来为bean增加别名,标识该bean具体代表的类,标识Spring在构建bean以后回调的初始化函数。整个xml文件的作用就是为这些指定来源,并指定用来初始化他们的参数,这些参数用来标识。的标签内容对应于Java的数据格式,可以有对应的,
依赖注入
Spring在管理bean方面是十分方便的,个人认为包含几个方面:
- 初始化。不需要手动new对象,在类名等发生改变的时候不需要修改代码。
- 对象的属性注入,通过xml的配置方法比较方便,减少hard coding。
- 提供了bean整个生命流程中多个回调接口。
主方法注入Bean
一个可运行的Jar包必须指定一个主类,而整个Jar包的入口就是这个主类的Main方法。作为入口的Main方法必须是public static void属性的,即公开静态方法。由于类的静态方法只能引用静态变量或者静态方法,因此需要在Main方法中使用bean的时候,就会遇到一些问题。
applicationContext = new ClassPathXmlApplicationContext("hello-spring-pigeon.xml");
currentTimeService = applicationContext.getBean("currentTimeService",CurrentTimeService.class);
System.out.println(currentTimeService.parseDate(new Date()));
以上提供了一个解决方案。就是在静态方法中第一次需要bean的时候,手动获取bea上下文applicationContext,然后就可以加载需要的bean。
拓展:
看到这样一个case,在init()中为静态变量进行赋值。
public class AutoLoginUtil {
@Autowired
private IUserService userService;
private static AutoLoginUtil autoLoginUtil;
@PostConstruct
public void init() {
autoLoginUtil = this;
autoLoginUtil.userService = this.userService;
}
public static void autoLogin() {
autoLoginUtil.userService.queryUserAutoLogin();
}
}
这个代码并不能解决直接使用静态类名方法名的问题。因为@PostConstruct 只会在对象被创建以后才会回调。
杂项
日志系统
推荐使用slf4j,在阿里的编程准则中强制使用slf4j。Simple Logging Facade是简单日志门面系统,它提供了一种Java中所有日志实现框架的抽象,注意仅仅是抽象,单独它本身并无法完成日志存储的工作。使用它的好处在于,你只需要按照统一的格式书写日志代码,而无须关系具体环境使用的是日志的何种实现。
slf4j需要与日志核心实现包一起配合使用,这样的包有log4j、java.util.logging、logback、log4j2等等实现,可以参考博客,以及日志桥接器相关。