vlambda博客
学习文章列表

一文带你看懂:不为人知的 package-info.java 文件




许多同学日常开发时,都不太注意到,原来自己维护这么久的 web 工程中,居然还有 package-info.java 这么一个类。
但只要我们在 idea 中搜索一下,就能够发现很多依赖包,都用到了这个类的实现。


那么,下面我们一起研究下,到底什么是 package-info.java 类文件~~
【目录】
1)注解技术
2)package-info类拆解
3)动态ClassLoader原理 与 双亲委派机制
4)自定义ClassLoader



一、注解技术

注解(annotation)是 Java 5 引入的,用来为类、方法、字段、参数等 Java 结构提供额外信息的机制。我先举个例子,比如,Java 核心类库中的@Override注解是被用来声明某个实例方法重写了父类的同名同参数类型的方法。

package java.lang;
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}


二、package-info类拆解


一文带你看懂:不为人知的 package-info.java 文件


下面我们用一个简单的例子(分包逻辑开发),演示 package-info.java 的用法:


1)创建一个package:packageinfo


2)创建一个注解:PackageAnotation



/** * PackageAnotation.java 定义修饰包的注解 * <b>package-info不是平常类,其作用有三个:</b><br> * 1、为标注在包上Annotation提供便利;<br> * 2、声明包的私有类和常量;<br> * 3、提供包的整体注释说明。<br> */@Target(ElementType.PACKAGE)@Retention(RetentionPolicy.RUNTIME)@interface PackageAnotation { public String version() default "";}


3)创建一个类,类名 = package-info.java



/** * <b>package-info不是平常类,其作用有三个:</b><br> * 1、为标注在包上Annotation提供便利;<br> * 2、声明包的私有类和常量;<br> * 3、提供包的整体注释说明。<br> */@PackageAnotation(version="1.0")package packageinfo;


4)创建测试用例,类名 = TestPackageInfo.java


package packageinfo;
/** * 测试package-info.java文件的作用 * 1、为标注在包上Annotation提供便利;<br> * 2、声明包的私有类和常量;<br> * @author JoyoungZhang@gmail.com */public class TestPackageInfo { public static void main(String[] args) { //1 获取 Package 类 Package p = Package.getPackage("packageinfo"); //2 判断这个包对象,(是否在 package-info.java 中声明)且被某个注解(PackageAnotation)修饰 if(p != null && p.isAnnotationPresent(PackageAnotation.class)) { //3 如果找到了注解,获取注解信息,实现业务逻辑(Servlet过滤、鉴权、分发请求等) PackageAnotation nav = p.getAnnotation(PackageAnotation.class); if (nav != null) { System.out.println("package version:" + nav.version()); } } }}


三、动态ClassLoader原理 与 双亲委派机制


3.1、动态ClassLoader原理


上面的测试用例,Package.getPackage("packageinfo"); 背后隐藏了一个原理 -- 类的动态加载。


写好一个Java程序,都是由若干个.class文件组织而成的一个完整的Java应用程序。当程序在运行时,即会调用该程序的一个入口函数,通过它来调用系统的相关功能;而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法。因此,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是按需通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。


所以ClassLoader就是用来动态加载class文件到内存当中用的。


一文带你看懂:不为人知的 package-info.java 文件


我们写好的应用类(业务代码),大部分都是通过这个类加载器 AppClassLoader 来加载到JVM里面。


3.2 JVM默认有3个默认类加载器


3.2.1 AppClassLoader

AppClassLoader 称为应用类加载器,又称为系统类加载器,负责在JVM启动时,加载来自在命令java中的classpath或者java.class.path系统属性或者CLASSPATH操作系统属性所指定的JAR包和类路径。


3.2.2 ExtClassLoader

ExtClassLoader 称为扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有jar包或者由java.ext.dirs系统属性指定的jar包.放入这个目录下的jar包对AppClassLoader加载器都是可见的(因为ExtClassLoader是AppClassLoader的父加载器,并且Java类加载器采用了委托机制)。


3.2.3 BootstrapClassLoader

BootstrapClassLoader 称为启动类加载器,是Java类加载层次中最顶层的类加载器,负责加载JDK中的核心类库,如:rt.jar、resources.jar、charsets.jar等,可通过如下程序获得该类加载器从哪些地方加载了相关的jar或class文件。


3.3 双亲委派机制

如果说,我们想引用一些外部依赖,那么就需要更高层的类加载器,让它们帮我们完成这个类装载的过程了,而这个过程也有个专有名词:双亲委派机制。


类加载分级:

-> 自定义ClassLoader

-> AppClassLoader

-> ExtClassLoader

-> BootstrapClassLoader


某个特定的类加载器在接到加载类的请求时,首先将加载任务委托交给父类加载器,父类加载器又将加载任务向上委托,直到最父类加载器,如果最父类加载器可以完成类加载任务,就成功返回,如果不行就向下传递委托任务,由其子类加载器进行加载。

一文带你看懂:不为人知的 package-info.java 文件



一文带你看懂:不为人知的 package-info.java 文件
总结




以上是关于package-info.java的相关知识介绍,还牵扯到了类加载器的回顾,希望对大家工作有所帮助。


一文带你看懂:不为人知的 package-info.java 文件





后端技术&架构精华





《源码系列》




《经典书籍》



《服务端技术栈》


《算法系列》






《项目管理》