先上代码

    public static void main(String[] args) throws Exception {
        if (args == null || args.length != 1) {
            System.out.println("usage java -jar xxx.jar testMethodNam/all");
            System.exit(1);
        }
        setUpBeforeClass();
        Class clazz = AllTest.class.forName(AllTest.class.getName());
        String argMethod = args[0];
        if ("all".equals(argMethod)) {
            Method[] methods = clazz.getDeclaredMethods();
            
            for (Method method : methods) {
                String methodName = method.getName();
                if (methodName.startsWith("test")) {
                    System.out.println(methodName);
                    method.invoke(clazz.newInstance(), null);
                }
            }
        } else {
            try {
                Method method = clazz.getDeclaredMethod(argMethod, null);
                System.out.println(method.getName());
                method.invoke(clazz.newInstance(), null);
            } catch (NoSuchMethodException e) {
                System.out.println("there is no method :" + argMethod);
                System.exit(1);
            }
        }    
    }

上面的代码有一部分非常冗余,就是clazz的声明,clazz可以用AllTest.class代替,取的就是AllTest的Class对象。

反射

java反射API的第一个主要作用是获取程序在运行时刻的内部结构。这对于程序的检查工具盒调试器来说是非常实用的。只需短短的十几行代码就可以遍历出一个java类的内部结构,包括构造方法、声明的域和定义的方法等。

只要有java.lang.Class类的对象,就可以获取该类的构造方法、域和方法,对应的方法分别是getConstructor、getField和getMethod。对应的还有getDeclaredXXX版本,getDeclaredXXX的方法只会获取该类自身所声明的元素,而不会考虑继承下来的元素。这些类中的方法可以获取到所对应的结构元数据。

java.lang.reflect中:

Class类:代表一个类。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor 类:代表类的构造方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
    class MyClass {
        public int count;
        public MyClass(int start) {
            count = start;
        }
        public void increase(int step) {
            count = count + step;
        }
    }
 

使用反射API:

    MyClass myClass = new MyClass(0); //一般做法

    myClass.increase(2);
    System.out.println("Normal -> " + myClass.count);
    try {
        Constructor constructor = MyClass.class.getConstructor(int.class); //获取构造方法

        MyClass myClassReflect = constructor.newInstance(10); //创建对象

        Method method = MyClass.class.getMethod("increase", int.class);  //获取方法

        method.invoke(myClassReflect, 5); //调用方法

        Field field = MyClass.class.getField("count"); //获取域

        System.out.println("Reflect -> " + field.getInt(myClassReflect)); //获取域的值

    } catch (Exception e) { 
        e.printStackTrace();
    } 
 

使用java反射API的时候可以绕过java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true集合。有了这种机制,很方便的在运行时获取程序的内部状态。比如:两个程序员在合作开发,但A开发的很快,而且需要B没有开发ok的类,则此时只要定义要协定好方法名和属性等,A则可以继续开发。或者像本节刚开始的例子一样,如果命名有一定的规范,则只需要加实现的case,运行的main方法可以不用更改任何东西即可。

## java的注解(Annotation)

### 注解处理器

注解处理器类库(java.lang.reflect.AnnotatedElement)

Annotation接口代表程序元素前面的注解,是所有Annotation类型的父接口。接口主要有以下实现类:

Class:类定义
Constructor:构造器定义
Field:成员变量定义
Method:方法定义
Package:包定义

java.lang.reflect包所有提供的反射API补充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能在运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。

Target:目标:类、接口、方法、构造函数等等
    ANNOTATION、CONSTRUCTOR、FIELD、LOCAL_VARIABLE、METHOD、PACKAGE、PARAMETER、TYPE
    如果只允许对方法和构造函数进行注释:@Target({ElementType.METHOD,ElementType.CONSTRUCTOR})
Retention:为设置注释是否保存在class文件中。
    RetentionPolicy.SOURCE 不将注释保存在class文件,编译时被过滤掉
    RetentionPolicy.CLASS 只保存在class文件,而是用反射读取时忽略这些注释
    RetentionPolicy.RUNTIME 保存在class文件,也可以通过反射去读注释
Documented:自动生成文档
inherited:加上该注释,父类的注释可以被子类继承

是用反射读取注释:

Method Method= TestAnnotation.class.getMethod("myMethod",null);
Annotation annotation = method.getAnnotation(MyAnnotation.class);
PS:使用反射得到注释信息,必须用@Retention(RetentionPolicy.RUNTIME)注释

注解知识点

借鉴别人的实例:

interface:
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitName{
        String value() default "";
    }
    =====
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitColor{
        public enum Color {BULE,RED,GREEN};
        Color fruitColor() default Color.GREEN;
    }
    ======
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface FruitProvider{
        public int id() default -1;
        public String name() default "";
        public String address() default "";
    }
    ======
    Apple.java

    public class Apple {

        @FruitName("Apple")
        private String appleName;
        
        @FruitColor(fruitColor=Color.RED)
        private String appleColor;
        
        @FruitProvider(id=1,name="BigApple",address="YourHome")
        private String appleProvider;
        
        public void setAppleColor(String appleColor){
            this.appleColor = appleColor;
        }
        
        public String getAppleColor(){
            return appleColor;
        }
        public String getAppleName() {
            return appleName;
        }
        public void setAppleName(String appleName) {
            this.appleName = appleName;
        }
        public String getAppleProvider() {
            return appleProvider;
        }
        public void setAppleProvider(String appleProvider) {
            this.appleProvider = appleProvider;
        }
        
        public void displayName(){
            System.out.println("What fruit do you have? : Apple");
        }
    }
    ======
    FruitInfoUtil.java
    public class FruitInfoUtil {
        public static void getFruitInfo(Class<?> clazz){
            
            String strFruitName = "Name: ";
            String strFruitColor = "Color: ";
            String strFruitProvider = "Provider: ";
            
            Field[] fields = clazz.getDeclaredFields();
            
            for(Field field : fields){
                if(field.isAnnotationPresent(FruitName.class)){
                    FruitName fn = (FruitName) field.getAnnotation(FruitName.class);
                    strFruitName = strFruitName + fn.value();
                    System.out.println(strFruitName);
                }else if(field.isAnnotationPresent(FruitColor.class)){
                    FruitColor fc = (FruitColor) field.getAnnotation(FruitColor.class);
                    strFruitColor = strFruitColor + fc.fruitColor().toString();
                    System.out.println(strFruitColor);
                }else if(field.isAnnotationPresent(FruitProvider.class)){
                    FruitProvider fp = (FruitProvider) field.getAnnotation(FruitProvider.class);
                    strFruitProvider = strFruitProvider + fp.id() + "==" + fp.name() + "===" +fp.address();
                    System.out.println(strFruitProvider);
                }else{
                    System.out.println("Wrong work flow into this place!");
                }
            }              
        }          
    }
    ======
    FruitRun.java
    public class FruitRun {
        public static void main(String[] args) {
            
            FruitInfoUtil.getFruitInfo(Apple.class);
            
            Apple a = new Apple();
            a.displayName();
        }
    }

## maven的main-class打包

maven工程中export出来的jar在linux下用命令行执行时,出现MainClassNotFound的异常。而用maven install方式打包也出现同样的问题。

这是因为在pom.xml文件中,需要在build之前加入main-class的属性:

<properties>
<main-class>com.java.MainMethod</main-class>
<java-version>1.6</java-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

在build中的plugins加入:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>${main-class}</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

现在用maven install打出的jar,在命令行中运行时不会报错了。 PS:将maven项目中依赖的jar打包,存放在target目录下的dependency中

mvn dependency:copy-dependencies