一、基础 1、基本数据类型 整型
类型
存储
说明
byte
1 byte
short
2 byte
int
4 byte
默认整型
long
8 byte
浮点型
类型
存储
说明
float
4 byte
double
8 byte
默认浮点型
字符型
布尔型
Java
中没有无符号整型。
Java
中整型和布尔型之间不能互相转换。
1 2 3 4 5 double varDouble = 12.4d ;final double CONST_DOUBLE = 12.4d ;
2、字符串 Java
中 String
不是基本类型而是一个类。
String
是不可变字符串,每次对字符串的修改都会返回一个新的字符串。
String
的判等只能使用 equals
而不能使用 ==
,equals
判断的是字符串语义上是否相等,==
判断的是内存地址是否相同。
String
常用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int compareTo (String other) ; boolean equals (String other) ; boolean equalsIgnoreCase (String other) ; boolean startsWith (String prefix) ; boolean endsWith (String suffix) ; int indexOf (String str) ; int indexOf (String str, int fromIndex) ; int indexOf (int cp) ; int indexOf (int cp, int fromIndex) ; int lastIndexOf (String str) ; int lastIndexOf (String str, int fromIndex) ; int lastIndexOf (int cp) ;int lastIndexOf (int cp, int fromIndex) ;int length () ;String replace (CharSequence oldString, CharSequence newString) ; String subString (int beginIndex) ; String subString (int beginIndex, int endIndex) ;String toLowerCase () ;String toUpperCase () ;String trim () ; String join (CharSequence delimiter, CharSequence... elements) ;
因为字符串是不可变的,所以每次拼接字符串都会产生一个新的对象。可以使用 StringBuilder
和 StringBuffer
拼接字符串。StringBuilder
线程不安全性能更好,StringBuffer
线程安全但性能略差。
1 2 3 4 5 StringBuilder builder = new StringBuilder(); builder.append("1" ); builder.append("2" ); ... builder.toString();
3、控制流程 switch-case
中可以放三种类型:
整型(byte、short、int、long)
枚举类型
字符串
4、数组 数组创建之后不能在修改大小,如果要使用能动态修改大小的数组可以使用 ArrayList
。
1 2 3 4 5 6 int [] a = { 1 , 2 , 3 , 4 };int [] a = new int []{ 2 , 3 , 4 , 5 };Arrays.toString(a);
java.util.Arrays
是数组的伴随类,提供了数组的常见操作,基本都是静态方法:
1 2 Arrays.toString(); Arrays.sort();
5、枚举 所有的枚举类型都是 Enum
类的子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 enum Size { SMALL("S" ), MEDIUM("M" ), LARGE("L" ), EXTRA_LARGE("E" ); private String description; private Size (String description) { this .description = description; } private String getDescription () { return description; } }
定义了一个 Size
类,该类有四个实例。
枚举的判等必须使用 ==
而不能使用 equals
。
枚举常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Size size = Size.SMALL; size.toString() Size s = Enum.valueOf(Size.class, "SMALL" ); Size[] values = Size.values(); Size.SMALL.ordinal();
6、面向对象 私有域 + 公开的访问器与修改器方法。
方法重载:多个方法有相同的名字,不同的参数就叫方法重载。
方法覆盖:子类重写父类的方法叫方法覆盖,方法覆盖时子类必须比父类更开放。
方法签名:包括方法名称、参数类型,但不包括返回类型。
默认构造器:如果类没有提供构造器则会自动添加一个默认无参构造器,如果提供了则不会有默认无参构造器。
初始化一个类中的域可以有三个地方:
域声明中
初始化块(对象初始化块、静态域初始化块)
构造器
执行顺序是加载该类是就执行静态初始化块,构造对象时先执行域声明语句,然后执行对象初始化块,最后执行构造器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Employee { private int id; private String name; private static int level; static { Employee.level = 1000 ; } { id = 100 ; name = "ifangcy" ; } public Employee (int id, String name) { this .id = id; this .name = name; } }
子类必须在构造器第一行调用父类的构造器,如果没有主动调用就会自动调用父类的默认无参构造器,如果父类没有默认无参构造器则报错。
多态:一个对象变量可能指向多种实际的类型叫多态。(变量可以引用一个类型,也可以引用一个类型的子类)
动态绑定:在运行时自动决定使用哪个对象的方法叫动态绑定。
Java
中的方法调用默认是动态绑定的,使用 static
、final
、private
可以消除动态绑定使用静态绑定。
访问控制符:
public
:所以类都可见
protected
:本包及所有子类可见
默认:本包可见
private
:只有本类可见
超类 Object
常用方法:
1 2 3 4 5 6 7 8 equals() hashCode() toString()
7、抽象类 抽象类不一定有抽象方法,但有抽象方法就一定是抽象类。
抽象类中可以包含具体数据和方法:
1 2 3 4 5 6 7 8 9 10 11 12 abstract class Person { private String name; public Person (String name) { this .name = name; } public String getName () { return this .name; } public abstract String getDescription () ; }
8、反射 9、接口 接口中所有方法默认都是 public
的不用显式声明,但是实现接口类中必须显示声明 public
。
接口不能包含实例域,但可以包含常量,所有的常量默认是 public static final
。
1 2 3 4 5 6 7 8 9 public interface Comparable { int compareTo (Object other) ; double CAN_COMPARABLE = 10 ; } public interface Moveable extends Comparable { void move (double x, double y) ; }
接口中方法可以使用 default
默认实现方法,实现类不必一定实现默认实现方法:
1 2 3 4 5 6 public interface Comparable <T > { default int compareTo (T other) { return 0 ; } }
如果一个类中有很多静态方法,以前的解决办法是实现一个伴随类把这些静态方法都放到伴随类中,比如 Collection/Collections
、Path/Paths
等。现在接口中可以有静态方法,这是一个更好的解决办法:
1 2 3 4 5 6 public interface Path { public static Path get (String first, String... more) { return FileSystems.getDefault().getPath(first, more); } }
如果实现类的父类和它实现的接口中有方法冲突,则父类方法优先,叫类优先。
在没有 lambda
和内部类之前,回调的实现只能使用接口实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface ActionListener { void actionPerformed (ActionEven evnet) ; } class TimePrinter implements ActionListener { public void actionPerformed (ActionEvent event) { System.out.print("action event" ); } } ActionListener listener = new TimePrinter(); Timer t = new Time(1000 , listener); t.start();
10、lambda lambda
不需要指定返回类型。
lambda
可以指定参数类型,如果可以推导出参数类型也可以省略参数类型。
lambda
如果没有参数也不能省略小括号。
lambda
如果只有一个参数才可以省略小括号。
函数式接口:对于只有一个抽象方法(注意是只有一个抽象方法,可以有多个静态方法、多个默认实现方法)的接口叫函数式接口。
1 2 3 4 5 6 7 8 Timer t = new Timer(1000 , event -> { System.out.println(event); }); t.start();
java.util.function
中定义了很多常用的函数是接口,方便实用。
方法引用:如果已有方法就满足了函数式接口的需求,可以使用方法引用的方式把该方法直接作为 lambda
传递。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) { Timer t = new Timer(1000 , new Test()::print); t.start(); } public void print (ActionEvent event) { System.out.println(event); } }
方法引用有三种方式:
前两个形式中所有参数都作为引用方法的参数。
第三种方法中必须有多个参数,第一个参数作为该实例方法的调用者,其他参数作为实例方法参数。
1 2 3 4 5 String::compareToIgnoreCase (x, y) -> x.compareToIgnoreCase(y);
构造器也可以用在方法引用中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Person { private String name; public Person (String name) { this .name = name; } public static void main (String[] args) { String[] names = { "Jack" , "Tom" }; Stream<Person> persons = Arrays.stream(names).map(Person::new ); } }
lambda
中使用的 this
指的是创建该 lambda
表达式的方法中的 this
。
1 2 3 4 5 6 7 8 public class Person { public void print () { ActionListener listener = event -> { System.out.println(this .toString()); }; } }
lambda
会捕获外围变量,注意被捕获的变量不能被修改(无论 lambda
内部修改还是外部修改都不行)。
11、内部类 1 2 3 4 5 public class Outer { class Inner { } }
内部类所有的静态域都必须是 final
。
内部类不能有 static
方法。
在虚拟机中并没有内部类,内部类会被编译器编译成用 $
分隔的外部类与内部类:
1 2 3 4 5 6 7 public class Outer { class Inner { } }
内部类也可以定义在方法中,这时候称为局部内部类,局部内部类不能使用 private
或 public
修饰:
1 2 3 4 5 6 7 8 public class Outer { public void sayHello () { class Inner { } } }
和 lambda
一样,内部类中捕获的外部变量必须是不可变的 final
。
如果局部内部类只需要创建一次对象,可以直接 匿名内部类
直接创建对象而不用命名类:
1 2 3 4 5 6 7 8 ActionListener listener = new ActionListener() { @Override public void actionPerformed (ActionEvent e) { } }; Timer t = new Timer(1000 , listener); t.start();
如果内部类不需要使用外部类的变量,可以将内部类设置为 static
以便取消内部类对外部类的引用:
1 2 3 4 5 6 public class Outer { static class Inner { } }
12、异常 所有异常都派生自 Throwable
:
Error
:系统内部错误,发生时应该停止程序。
Exception
:分为 RuntimeException
、IOException
。程序本身错误是 RuntimeException
,IO
错误是 IOException
。
常见 RuntimeException
:
常见 IOException
:
试图在文件尾部读取数据
试图打开一个不存在的文件
给定字符串查找类时类不存在
Error
和 RuntimeException
是非受查异常,IOException
是受查异常,只有受查异常才能抛出。
异常的处理有两种方法:抛出异常、捕获异常。
try-catch
中可以 catch
多个异常:
1 2 3 4 5 try { }catch (FileNotFoundException | UnknownHostException) { }
finally
在不论是否捕获异常都会执行,一般用来释放资源,但如果资源实现了 AutoCloseable
接口,可以使用更好的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 try { }catch (Exception e) { }finally { } try (Scanner in = new Scanner(new FileInputStream("demo.txt" ))) { }catch (Exception e) { }
13、泛型 1 2 3 4 5 6 7 public class Pair <T > { public static <T> T getMiddle (T t) { } }
限定泛型类型:
1 2 3 4 public class Son <T extends Person & Comparable & Serialzable > { }
类型擦除
:虚拟机中没有泛型,泛型类在编译之后会被去除类型,使用限定类型替换,如果没有限定类型则使用 Object
类型替换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public clas Pair<T> { private T lower; public T getLower (T t) { } } public clas Pair { private Object lower; public Object getLower (Object t) { } } public clas Pair<T extends Comparable & Serializable> { private T lower; public T getLower (T t) { } } public clas Pair { private Comparable lower; public Comparable getLower (Comparable t) { } }
因为类型擦除,应该将没有方法的接口放到限定列表的后面。
桥方法。
几个注意点:
泛型的类型不能是基本类型,必须使用包装器:
1 2 3 4 5 6 7 8 9 public class Pair <double > { } public class Pair <Double > { }
不能创建泛型类型的数组:
1 2 Pair<String>[] table = new Pair<String>[10 ];
注意,无论 S
和 T
有什么关系,Pair<S>
和 Pair<T>
都没关系:
1 2 3 4 5 6 7 8 class Pair <T > {}class SuperClass {}class SonClass extends SuperClass {}Pair<SuperClass> pair = new Pair<SonClass>();
但是 Pair<SonClass>
一定是 Pair
的子类型:
1 2 air<Manager> managerPair = new Pair<>(); Pair pair = managerPair;
通配符类型
???
14、集合 for-each
循环可以用在任何实现了 Iterable
接口的类上。
1 2 3 4 5 6 7 Iterator<String> it = c.iterator(); if (it.hasNext()) { it.next(); it.remove(); }
15、并发 16、打包 传统的 Java 会打包成一个 applet
程序,可以通过网络下载在浏览器中运行,后来出现了打包成 jar
包。
applet
程序已经基本废弃不用,如今基本使用 jar
打包代码。
jar jar
其实就是一个使用 zip
格式压缩的文件,可以包含代码及资源文件。
打包成 jar
包:
1 2 3 4 5 jar cvf 打包后名称.jar *.class 资源文件名.gif jar cfm 打包后名称.jar manifest.mf *.class 资源名.gif
一般 jar
包中有一个描述文档特征的清单文件 META-INF/MANIFEST.MF
,文件分为多个节,开头主节作用于整个 jar
包,下面的命名条目(指定文件、包、URL 都可以作为命名条目)作用于指定条目:
1 2 3 4 5 6 7 8 9 10 Manifest-Version: 1.0 Sealed: true Name: Demo.class Name: com/ifangcy/demo/util Sealed: true
注意:清单文件最后一行必须以换行符结束。
要想直接启动 jar
包,在打包时必须指定程序入口或者在清单文件中指定主类:
1 2 3 4 5 jar cvfe 打包后名称.jar com.ifangcy.demo.MyMainClass Main-Class: com.ifangcy.demo.MyMainClass
启动 jar
包:
jar
包中的资源如何定位?通过可以访问的 class
文件来定位资源文件,一般可以把资源文件放到类文件文件夹中:
1 2 3 4 5 6 7 8 9 URL url = ResourceTest.class.getResource("about.gif" ); Image img = new ImageIcon(url).getImage(); URL url = ResourceTest.class.getResource("data/about.gif" ); Image img = new ImageIcon(url).getImage();
Properties
、Preferences
常用作配置功能,web 开发一般用不到就不详写了。
applet applet
是包含在 HTML 页面中的 Java 程序,基本不再使用。
Java Web Start 二、JDBC 三、JavaWeb JSP
与 Servlet
是一致的,JSP
最终会被web 服务器 编译为 Servlet
,web 服务器 上实际运行的是 Servlet
。第一次访问 JSP
文件时会比较慢,因为要等待 JSP
编译成 Servlet
文件。
一个标准的 Web 应用结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 webapp | -- META-INF | -- WEB-INF | | | -- web.xml | | | -- classes | | | -- lib | -- index.jsp | -- demo.html | __ index.js | -- css | __ img
Servlet 3.0
之前 web.xml
是必须的,之后 web.xml
不再是必需文件,因为可以使用注解。
项目中的这个 web.xml
文件是项目级的配置,只会影响该项目,web 服务器还会提供一个服务器级的 web.xml
(Tomcat
配置文件位于 conf/web.xml
)配置文件,作为所有项目共有配置。
web.xml
及注解主要可配置有:
JSP
Servlet
Listener
Filter
1、JSP 2、Servlet Servlet
生命周期由 web 服务器(比如 Tomcat)来管理,创建 Servlet
实例有两个时机:
第一次请求到该 Servlet
时
通过 load-on-startup
配置让启动 web 服务器时就创建该 Servlet
实例,load-on-startup
值越小越先创建
已创建的 Servelet
会放到容器中,后续再请求到该 Servlet
时不需要重新创建实例。
Servlet 3.0
增加了很多新特性,比如注解配置、web 模块支持、Servlet 异步处理。
注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.ifangcy.servlets;@WebServlet( loadOnStartup = 1, name = "helloServlet", urlPatterns = { "/hello" }, initParams = { @WebInitParam(name="driver", value = "com.mysql.cj.jdbc.Driver"), @WebInitParam(name = "url", value = "jdbc:mysql://localhost:3306?database") } ) public class HelloServlet extends HttpServlet { @Override protected void service (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8" ); resp.setContentType("text/html" ); PrintStream out = new PrintStream(resp.getOutputStream()); out.println("<html>" ); out.println("<body>" ); out.println("<div>Hello world</div>" ); out.println("</body>" ); out.println("</html>" ); } }
web.xml 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <servlet > <servlet-name > helloServlet</servlet-name > <servlet-class > com.ifangcy.servlets.HelloServlet</servlet-class > <load-on-startup > 1</load-on-startup > <init-param > <param-name > driver</param-name > <param-value > com.mysql.cj.jdbc.Driver</param-value > </init-param > <init-param > <param-name > url</param-name > <param-value > jdbc:mysql://localhost:3306?database</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > helloServlet</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping >
3、Filter Filter
是一种加强版的 Servlet
,用来对请求进行预处理、对响应进行后处理。Filter
一般用来先对请求预处理,然后把请求交给 Servlet
,最后拿到 Servlet
的响应在进行后处理,最后返回响应。
Filter
通常可以用来处理多个 Servlet
中的共有部分,比如记录日志、权限控制等。
Filter
可拦截多个请求或响应,一个请求或响应也可以被多个 Filter
拦截。
注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @WebFilter( filterName = "hello", urlPatterns = { "/*" } ) public class HelloFilter implements Filter { @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { filterChain.doFilter(servletRequest, servletResponse); } @Override public void destroy () { } }
web.xml 配置 1 2 3 4 5 6 7 8 <filter > <filter-name > hello</filter-name > <filter-class > com.ifangcy.filters.HelloFilter</filter-class > </filter > <filter-mapping > <filter-name > hello</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
4、Listener Listener
用来监听 web 容器内部的事件,比如 web 应用被启动、web 应用停止、用户 session 开始、用户 session 结束、用户请求到达等,这些内容本来对开发者是不可见的。
常用的 web 事件监听器接口有如下:
ServletContextListener:监听 web 容器的启动和关闭
ServletContextAttributeListener:监听 ServletContext 范围内属性改变
ServletRequestListener:监听用户请求,可以用来实现系统日志
ServletRequestAttributeListener:监听 ServletRequest 范围内属性改变
HttpSessionListener:监听用户 session 开始和结束,可以用来实现监听在线用户数
HttpSessionAttributeListner:监听 HttpSession 范围内属性改变
Listener
配置无需参数。
注解配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @WebListener public class HelloListener implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent sce) { } @Override public void contextDestroyed (ServletContextEvent sce) { } }
web.xml 配置 1 2 3 <listener > <listener-class > com.ifangcy.listeners.HelloListener</listener-class > </listener >
四、Maven 在 Maven
打包时不会把 src/test
目录下的测试代码打包进最终包。
Maven
默认打包成 jar
包,默认打包成的 jar
并不能直接运行,因为打包出的 MATA-INF/MANIFEST.MF
文件中没有指定 Main-Class
,如果想要直接运行该包需要配置在配置中使用 maven-shade-plugin
配置主类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-shade-plugin</artifactId > <version > 1.2.1</version > <executions > <execution > <phase > package</phase > <goals > <goal > shade</goal > </goals > <configuration > <transformers > <transformer implementation ="org.apache.maven.plugins.shade.resource.MainfestResourceTransformer" > <mainClass > com.ifangcy.demo.HelloWorld</mainClass > </transformer > </transformers > </configuration > </execution > </executions > </plugin >
1、基础 安装 Maven
安装目录:
1 2 3 4 5 6 |---bin |---boot |---conf |---settings.xml |---lib
安装之后,把 ${M2_HOME}/conf
下的 settings.xml
配置文件复制一份到用户目录下 ~/.m2/settings.xml
,作为用户级的配置文件。
1 2 ~/.m2/---repository |---settings.xml
在 settings.xml
添加阿里云镜像:
1 2 3 4 5 6 7 8 <mirrors > <mirror > <id > alimaven</id > <mirrorOf > central</mirrorOf > <name > aliyun maven</name > <url > http://maven.aliyun.com/nexus/content/repositories/central/</url > </mirror > </mirrors >
常用命令 1 2 3 mvn dependency:list mvn dependency:tree mvn dependency:analyze
当插件的 groupId
不是 org.apache.maven.plugins
和 org.codehaus.mojo
时,必须在 ~/.m2/settings.xml
中配置上插件的 groupId
才能通过命令行使用插件:
1 2 3 4 5 <settings > <pluginGroups > <pluginGroup > 插件的 groupId</pluginGroup > </pluginGroups > </settings >
Maven 项目结构 Maven
项目必须遵守固定的结构才能被 Maven
编译打包:
1 2 3 4 5 6 7 8 9 .---src |---main |---java |---resources |---webapp |---WEB-INF |---web.xml |---test |---pom.xml
源码目录:src/main/java/
包输出目录:target/
编译输出目录:target/classes/
默认打包方式:jar
一般不会手动去创建这样的项目结构,可以使用 Archetype
自动生成项目骨架,即使是 IDEA
也是通过 Archetype
命令生成的:
坐标 项目必须定义坐标,引入的第三方包也是通过坐标引入。
项目坐标包括:
groupId
:对应到项目,比如 com.ifangcy.mall
artifactId
:对应到项目的模块,比如 com.ifangcy.mall.account
version
:项目版本
packaging
:项目打包方式,默认(不填时)jar
,可选 war
、pom
classifier
:生成打包时的一些附属构件,需要配合插件
打包生成的包名为 artifactId-version[-classifier].packaging
。
引入的第三方包坐标包括:
groupId
:基本坐标,必填
artifactId
:基本坐标,必填
version
:基本坐标,非必填,如果不填则使用依赖最新发布版本
type
:对应 packaging
,项目类型,默认 jar
,非必填
scope
:依赖范围,非必填
optional
:是否可选,非必填
exclusions
:排除依赖,非必填
依赖 classpath 与依赖范围 classpath
可以看做运行环境,Maven
使用三套环境/classpath
隔离依赖:
测试 classpath
编译 classpath
运行 classpath
依赖通过指定 scope
来决定放到哪个 classpath
环境中:
compile
:编译依赖范围,默认值,三种 classpath
环境中都会放入依赖
test
:测试依赖范围,只会放入测试 classpath
环境中,例如 JUnit
provided
:依赖范围,编译、测试 classpath
环境中会放入依赖,例如 servlet-api
runtime
:运行时依赖范围,测试、运行 classpath
环境中会放入依赖,例如 JDBC
驱动
system
:系统依赖范围,与 provided
以来范围一致,区别是 system
需要指定 systemPath
从本地获取依赖而不是 Maven
仓库
import
:不影响三种 classpath
环境,在使用 dependencyManagement
时可能用到
传递依赖 直接依赖如果还有其他依赖,这时候依赖范围怎么处理?
最左边一列为第一直接依赖,第一行为第二直接依赖。
compile
test
provided
runtime
compile
compile
-
-
runtime
test
test
-
-
test
provided
provided
-
provided
provided
runtime
runtime
-
-
runtime
如果依赖范围不包括运行 classpath
,则该依赖不会被放到最终的打包中。
依赖调解 对于多个依赖最终依赖了一个包的不同版本时怎么处理?
两个原则:
路劲最近优先:A->B->C(1.0)
与 A->C(2.0)
因为第二个路径更近,所以 C
导入 2.0
版本
第一声明优先:同样长度的依赖,在 POM
中先声明的优先
2、仓库 依赖下载到本地的仓库下 ~/.m2/repository/
,路径为 groupId/artifactId/version/artifactId-version.packaging
。
仓库分为本地仓库、远程仓库,私服也是一种远程仓库。
国内一般需要配置远程仓库的镜像,在 ~/.m2/settings.xml
添加镜像:
1 2 3 4 5 6 7 8 <mirrors > <mirror > <id > alimaven</id > <mirrorOf > central</mirrorOf > <name > aliyun maven</name > <url > http://maven.aliyun.com/nexus/content/repositories/central/</url > </mirror > </mirrors >
这个镜像对当前用户所有项目都生效。
一般来说,有的项目需要特定的远程仓库,可以在项目 pom.xml
中配置项目范围的远程仓库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <project > <repositories > <repository > <id > jboss</id > <name > JBoss Repository</name > <url > http://repository.jboss.com/maven2/</url > <releases > <enabled > true</enabled > </releases > <snapshots > <enabled > false</enabled > </snapshots > <layout > default</layout > </repository > </repositories > </project >
这种配置只对当前项目有效。
有的仓库为了安全需要认证才能访问,认证信息的配置必须放到 ~/.m2/settings.xml
中,不能放到项目 pom.xml
中(放在项目中会被提交到版本控制中,不安全):
1 2 3 4 5 6 7 <servers > <server > <id > JBoss Respository</id > <username > username</username > <password > password</password > </server > </servers >
快照版本
:指的是 version
指定为 1.1-SNAPSHOT
这样形式的包。
1 2 3 4 5 6 7 8 <project > <groupId > com.ifangcy.mavendemo</groupId > <artifactId > mavendemo</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > </project >
快照版本在每次打包时 Maven
会自动为项目版本加上时间戳,生成形如 1.1-20201212.221212-12
这样的版本号。
快照版本的依赖每次在编译时都会自动检查是否有最新快照版本可下载,确保使用最新快照版。
3、生命周期与插件 Maven
把构建过程抽象成一整套抽象的生命周期,每个生命周期都可以完成一定的任务,这些任务都交给插件来完成,Maven
已经自带了每个生命周期的默认插件,当然这些插件是可以随时替换的。
对于整个构建过程,Maven
抽象出了三套生命周期:clean
、default
、site
。每套生命周期内部又分为多个阶段,在一套生命周期内部的阶段是有顺序的,后面的阶段依赖前面的阶段,但是三套生命周期是互相独立的,在不同生命周期的阶段是没有依赖关系。
clean
生命周期是负责清理项目,主要阶段:
pre-clean
clean
post-clean
default
生命周期是真正的构建,主要阶段:
validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
package
pre-integration-test
integration-test
post-integration-test
verify
install
deploy
site
生命周期是用于建立和发布站点,主要阶段:
pre-site
site
post-site
site-deploy
执行指定任务的方式就是调用特定的 Maven
生命周期阶段,比如 mvn clean deploy
调用 clean
生命周期的 clean
阶段、default
生命周期的 deploy
阶段。
如上所述,生命周期的阶段的任务由插件来完成,但阶段与插件并不是直接对应的,而是阶段与插件目标 直接对应,而且一个阶段可能对应多个目标,多个目标的执行顺序是按照声明顺序执行。
所谓的插件目标就是插件可以完成的工作,通常来说一个插件可以完成多种工作,每种工作都叫做一个目标,如下:
1 2 3 maven-dependency-plugin:analyze maven-dependency-plugin:tree maven-dependency-plugin:list
表示 maven-dependency-plugin
有三个目标:analyze
、tree
、list
。
只有 org.apache.maven.plugins
和 org.codehaus.mojo
两个 groupId
下的插件才支持命令行调用,如果不是这两个 groupId
下的插件想要使用命令,需要修改 ~/.m2/settings.xml
配置:
1 2 3 4 5 6 <settings > <pluginGroups > <pluginGroup > org.mortbay.jetty</pluginGroup > </pluginGroups > </settings >
自定义阶段对应的目标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-source-plugin</artifactId > <version > 2.1.1</version > <executions > <execution > <id > attach-sources</id > <phase > verify</phase > <goals > <goal > jar-no-fork</goal > </goals > </execution > </executions > </plugin >
phase
并不是必须的,一般插件会默认指定绑定阶段,所以不显式指定也可以。
每个插件都可以通过 pom.xml
配置参数,有的插件还可以通过命令行配置参数,插件的每个目标也可以单独配置参数:
1 2 3 4 5 6 7 8 9 <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > <configuration > <source > 1.8</source > <target > 1.8</target > </configuration > </plugin >
依赖的远程仓库和插件的远程仓库是不同的,在 pom.xml
中配置插件的远程仓库,一般不需要自己配置插件仓库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <pluginRepositories > <pluginRepository > <id > central</id > <name > Maven Plugin Repository</name > <url > https://repo1.maven.org/maven2/</url > <layout > default</layout > <snapshots > <enabled > false</enabled > </snapshots > <releases > <updatePolicy > never</updatePolicy > </releases > </pluginRepository > </pluginRepositories >
Maven
有一个超级 pom.xml
文件,所有项目的 pom.xml
都继承自该超级 pom.xml
文件,该文件路径 ${M2_HOME}/lib/maven-model-builder-x.x.x.jar/org/apache/maven/model/pom-4.0.0.xml
。这个超级 pom.xml
中指定了每个默认核心插件的版本号,所以即使项目中没有指定核心插件版本也可以,当然其他第三方插件是不行的。
4、聚合与继承 聚合
:指的是把一个项目分成多个模块,把这些模块聚合到一起。
继承
:指的是把多个模块相同的依赖、插件抽取到一起。
聚合模块仅帮助聚合其他模块,本身不包含实质性内容。
聚合模块的 packaging
必须使用 pom
。
有的设计中聚合模块、所有模块的父模块是分开成两个模块的,有的设计中聚合模块、所有模块的父模块作为一个模块,我们采取第二种方式。
子模块会自动继承父模块的 groupId
和 version
。
父模块/聚合模块 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 <project > <groupId > com.ifangcy.mavendemo</groupId > <artifactId > mavendemo</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > pom</packaging > <modules > <module > module1</module > <module > module2</module > </modules > </project >
模块1 pom.xml
:
1 2 3 4 5 6 7 8 9 10 11 <project > <parent > <groupId > com.ifangcy.mavendemo</groupId > <artifactId > mavendemo</artifactId > <version > 1.0-SNAPSHOT</version > </parent > <artifactId > module1</artifactId > </project >
父模块可以被子模块继承的元素有:
groupId
version
description
organization
inceptionYear
url
developers
contributors
distributionManagement
issueManagement
ciManagement
scm
mailingLists
properties
dependencies
dependencyManagement
pluginManagement
repositories
build
reporting
依赖管理 所有模块的依赖都可以放到父模块的 dependencies
中,但是放到 dependencies
中的依赖所有模块都会全部继承,即使该模块不需要这个依赖。问题在于如果即让父模块统一管理依赖,又能让子模块按需导入依赖。
dependencyManagement
就是为了解决这个问题而存在,它 不会给父模块和子模块导入依赖 ,但子模块却可以继承这些依赖。
父模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <dependencyManagement > <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jcl</artifactId > <version > 5.2.11.RELEASE</version > </dependency > </dependencies > </dependencyManagement >
子模块:
1 2 3 4 5 6 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > </dependency > </dependencies >
父模块指定两个依赖 junit
、spring-jcl
,子模块只需要一个 junit
而不需要 spring-jcl
,子模块指定依赖时不需要指定版本,版本由父模块统一管理。
父模块实际不会导入任何依赖,子模块只会导入 junit
一个依赖。
插件管理 插件的管理和依赖一样,使用 pluginManagement
,而且插件的配置参数等也会一并继承。
父模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <build > <pluginManagement > <plugins > <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > <configuration > <source > 1.8</source > <target > 1.8</target > </configuration > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > 2.22.1</version > </plugin > </plugins > </pluginManagement > </build >
子模块:
1 2 3 4 5 6 7 8 9 10 <build > <plugins > <plugin > <artifactId > maven-compiler-plugin</artifactId > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > </plugin > </plugins > </build >
5、使用 Maven 测试代码 Maven
运行测试代码主要通过 maven-surefire-plugin
与 JUnit/TestNG
集成,Maven
本身不提供测试功能,通过执行生命周期的测试阶段来执行 JUnit
或 TestNG
测试用例。
运行 mvn test
时 maven-surefire-plugin
插件自动执行测试源码 src/test/java
路径下所以符合如下命名的测试代码:
**/Test*.java
**/*Test.java
**/*TestCase.java
跳过运行测试代码:
或在 pom.xml
中配置:
1 2 3 4 5 6 <plugin > <artifactId > maven-surefire-plugin</artifactId > <configuration > <skipTests > true</skipTests > </configuration > </plugin >
要想不运行测试代码,同时也不编译测试代码:
1 mvn package -Dmaven.test.skip=true
或 pom.xml
中配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > <configuration > <skip > true</skip > </configuration > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > 2.22.1</version > <configuration > <skip > true</skip > </configuration > </plugin >
指定运行特定测试代码,可以使用明确指定、逗号分隔、匹配符等多种方式:
1 2 3 mvn test -Dtest=RandomGeneratorTest mvn test -Dtest=RandomGeneratorTest,AccountCaptchaServiceTest mvn tset -Dtest=Random*Test
maven-surefire-plugin
会自动在 target/surefire-reports
下生成错误报告。
如果想生成测试覆盖率报告,可以使用 Cobertura
实现,加入插件 cobertura-maven-plugin
使用命令:
6、Maven 构建 war 包 Web 应用目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 war |-META-INF |-WEB-INF | | | |-web.xml | |-classes | |-lib | |-index.html |-login.jsp |-img |-css |-js
在 Web 容器中运行时,clases
和 lib
目录会被加入到 classpath
中。
Web 项目的打包必须为 war
,项目目录结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 project | |-pom.xml |-src | |-main | | |-java | | |-resources | | |-webapp | | | |-WEB-INF | | | | |-web.xml | | | |-img | | | |-css | | | |-js | | | |-index.html | | | |-login.jsp | |-test
Web 项目比起普通 jar
项目特殊在多了一个 webapp
目录,对应打包后的 Web 应用。Maven
在打包时会把依赖 jar
文件复制到打包的 war/WEB-INF/lib
中。
一般 Web
项目 pom.xml
中会特别配置 finalName
来指定最终打包名,替代默认的 artifactId-version.packaging
格式的打包名。
1 2 3 <build > <finalName > finalName</finalName > </build >
常见的,Web 项目中 src/main/resources
和 src/main/webapp
中都可能有资源文件,但是打包后这些资源文件在 war
包中的位置是不同的。src/main/resources
下的资源文件打包后位于 war/WEB-INF/classes
目录下(也就是会放到应用的 classpath
中),src/main/webapp
下的资源文件打包后位于 war/
根目录下(这些资源不会被放到 classpath
中,一般称为 web 资源比如 css
、js
、img
等)。
内嵌热部署 Web 开发中,每次修改都需要重新打包,部署到容器中查看效果。
可以使用内嵌容器的方式直接在项目中启动容器查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <plugins > <plugin > <groupId > org.mortbay.jetty</groupId > <artifactId > jetty-maven-plugin</artifactId > <version > 7.6.13.v20130916</version > <configuration > <scanIntervalSeconds > 10</scanIntervalSeconds > <webAppConfig > <contextPath > /api</contextPath > </webAppConfig > </configuration > </plugin > </plugins >
因为该插件 groupId
是 org.mortbay.jetty
,为了在命令行中使用该插件,需要修改下 ~/.m2/settings.xml
:
1 2 3 4 5 <settings > <pluginGroups > <pluginGroup > org.mortbay.jetty</pluginGroup > </pluginGroups > </settings >
在命令中启动容器:
1 2 mvn jetty:run mvn jetty:run -Djetty.port=9999
自动部署 可以使用 Cargo
将项目部署到本地或远程容器。
Cargo
有两种部署方式:
standalone
:把本地容器内容复制一份到指定目录下,然后把项目部署到该目录下
existing
:把项目部署到本地容器目录下,推荐
standalone 模式部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <plugins > <plugin > <groupId > org.codehaus.cargo</groupId > <artifactId > cargo-maven2-plugin</artifactId > <version > 1.0</version > <configuration > <container > <containerId > tomcat6x</containerId > <home > /etc/tomcat6</home > </container > <configuration > <type > standalone</type > <home > ${project.build.directory}/tomcat6x</home > <properties > <cargo.servlet.port > 8080</cargo.servlet.port > </properties > </configuration > </configuration > </plugin > </plugins >
同样的,需要修改 ~/.m2/settings.xml
以便使用命令。
使用:
existing 模式部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <plugins > <plugin > <groupId > org.codehaus.cargo</groupId > <artifactId > cargo-maven2-plugin</artifactId > <version > 1.0</version > <configuration > <container > <containerId > tomcat6x</containerId > <home > /etc/tomcat6</home > </container > <configuration > <type > existing</type > <home > /etc/tomcat6</home > <properties > <cargo.servlet.port > 8080</cargo.servlet.port > </properties > </configuration > </configuration > </plugin > </plugins >
部署到远程容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <plugins > <plugin > <groupId > org.codehaus.cargo</groupId > <artifactId > cargo-maven2-plugin</artifactId > <version > 1.0</version > <configuration > <container > <containerId > tomcat6x</containerId > <type > remote</type > </container > <configuration > <type > runtime</type > <properties > <cargo.remote.username > root</cargo.remote.username > <cargo.remote.password > 123456</cargo.remote.password > <cargo.tomcat.manager.url > http://localhost:8080/manager</cargo.tomcat.manager.url > </properties > </configuration > </configuration > </plugin > </plugins >
部署:
7、版本管理 版本约定:
1 <主版本>.<次要版本>.<增量版本>-<里程碑版本>
主版本:架构重大变化
次要版本:较大范围功能增加、变化,bug修复
增量版本:bug修复
里程碑版本:非稳定版,需要测试
使用 maven-release-plugin
管理版本
8、灵活构建 属性 Maven
中可使用6中属性:
内置属性:主要就是两个,${basedir}
表示项目根目录(也就是 pom.xml
所在目录),${version}
项目版本
POM 属性:pom.xml
文件中每个元素都可以作为属性使用,比如 <project><artifactId>
可以通过 ${project.artifactId}
使用
Settings 属性:和 POM 属性类似,settings.xml
中每个元素也可以作为属性使用,如 ${settings.localRepository}
自定义属性:在 pom.xml
的 <properties>
中定义属性
Java 系统属性:Java 系统属性可以通过属性使用,比如 ${user.home}
环境变量属性:所有环境变量也可以通过 env.
开头属性使用,比如 ${env.JAVA_HOME}
常用 POM 属性:
${project.build.sourceDirectory}
${project.build.testSourceDirectory}
${project.build.directory}
${project.outputDirectory}
${project.testOutputDirectory}
${project.groupId}
${project.artifactId}
${project.version}
${project.build.finalName}
环境隔离 资源文件过滤 Maven
对资源文件的处理知识在打包后把资源文件复制到编译输出目录中,这个过程是由插件 maven-resources-plugin
实现的,并不会主动对资源文件中使用到的 Maven
属性进行处理。
要想让资源文件中的 Maven
属性生效,需要修改插件配置开启资源过滤,这个配置在超级 POM
中(${M2_HOME}/lib/maven-model-builder.jar/org/apache/maven/model/pom.xml
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resources > <resource > <directory > ${project.basedir}/src/main/resources</directory > <filtering > true</filtering > </resource > </resources > <testResources > <testResource > <directory > ${project.basedir}/src/test/resources</directory > <filtering > true</filtering > </testResource > </testResources >
进行资源过滤之后,资源文件中就可以使用 Maven
属性,
web 资源过滤 对于 web 资源(位于 src/main/webapp
下,一般为 css
、js
、img
等),有时候可能也需要不同环境使用不同资源,可以通过配置 maven-war-plugin
实现过滤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <plugin > <artifactId > maven-war-plugin</artifactId > <version > 3.2.2</version > <configuration > <webResources > <resource > <filtering > true</filtering > <directory > src/main/webapp</directory > <includes > <include > **/*.css</include > <include > **/*.js</include > </includes > </resource > </webResources > </configuration > </plugin >
Maven
对不同环境使用不同配置通过 profile
实现,一个 profile
就是一个环境。
profile
可以配置的位置有:
pom.xml
:只对当前项目生效,推荐
~/.m2/settings.xml
:对所有项目生效
${M2_HOME}/conf/settings.xml
:对所有项目生效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <profiles > <profile > <id > dev</id > <activation > <activeByDefault > true</activeByDefault > </activation > <properties > <css.ref > blue.css</css.ref > <db.username > root</db.username > <db.password > 123456</db.password > <db.url > http://localhost:3306?db</db.url > </properties > </profile > <profile > <id > prod</id > <properties > <css.ref > red.css</css.ref > <db.username > root</db.username > <db.password > 123456</db.password > <db.url > http://199.119.111.11:3306?db</db.url > </properties > </profile > </profiles >
激活 profile
方式有多种:
命令行中激活:比如 mvn install -Pdev
激活 dev profile
profile
配置中激活:使用 activeByDefault
默认激活
系统属性激活:’’
操作系统环境激活:
文件是否存在激活:
Maven
的 maven-site-plugin
插件默认会使用两个自定义属性配置读取源码和文档的编码格式及生成站点的编码格式:
1 2 3 4 <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <project.reporting.outputEncoding > UTF-8</project.reporting.outputEncoding > </properties >
Maven
私服??
持续集成??
实现插件??
自定义 archetype
???
五、Mybatis 1、基础 一般的 ORM
框架将 Java 对象和数据库表关联,而 Mybatis
是将 Java 方法和 SQL 语句关联。
Java 方法和 SQL 语句的关联是通过称为 mapper
的 xml
文件和以 Mapper
为后缀的接口文件实现的。
Mybatis
自带基于 HashMap
的高速缓存,第一次执行一条 SQL 时会从数据库中取数据,之后再次执行就会从高速缓存中取数据。