Java反射与自定义注解

   反射,在 Java 常用框架中屡见不鲜。它存在于 java.lang.reflact 包中,就我的认识,它可以拿到类的字段和方法,及构造方法,还可以生成对象实例等。对深入的机制我暂时还不了解,本篇文章着重在使用方面,并附上一个本人应用到项目中的案例。

  • 基础姿势

    拿到类,反射是以类为基础的基础,首先拿到项目中的类,既可以这样拿

Class<?> clazz = Class.forName(类路径);

    也可以这样拿

Class<?> clazz = 类名.getClass();

    在一般意义的 JavaBean 中,存在构造函数、字段、一般函数三中不同元素,只要拿到了类,接着拿到它们就是水到渠成

Constructor constructors = clazz.getConstructor(null);//拿到构造函数
Field[] fields = clazz.getDeclaredFields();//拿到它定义的所有字段
Method[] methods = clazz.getDeclaredMethods();//拿到它定义的所有方法

    注意,拿到无参的构造函数传入的是 null,拿到有参构造函数,则按照构造函数的参数位置传入对应的类型 class 就行,比如

Constructor constructors = clazz.getConstructor(String.class,Integer.class,Double.class);//拿到有参构造函数

    拿到他们有什么用?拿到构造函数还好可以新建对象,拿到字段呢?这时候就得配合自定义注解来使用了?

    定义一个自定义标签

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
//@Documented
public @interface AnnotationDemo {
public String name();
public String value();
}

    ElementType 是作为标志存在的,而这个 RetentionPolicy 则是对功能上有影响的,它里面有三种策略。从源码上看它存在 CLASS,RUNTIME,SOURCE 三种方式。这个 Documented 是生成 java 文档时候是否带上的意思。

package java.lang.annotation;

/**

  • Annotation retention policy. The constants of this enumerated type

  • describe the various policies for retaining annotations. They are used

  • in conjunction with the {@link Retention} meta-annotation type to specify

  • how long annotations are to be retained.

  • @author Joshua Bloch

  • @since 1.5
    */
    public enum RetentionPolicy {
    /**

    • Annotations are to be discarded by the compiler.
      */
      SOURCE,

    /**

    • Annotations are to be recorded in the class file by the compiler
    • but need not be retained by the VM at run time. This is the default
    • behavior.
      */
      CLASS,

    /**

    • Annotations are to be recorded in the class file by the compiler and
    • retained by the VM at run time, so they may be read reflectively.
    • @see java.lang.reflect.AnnotatedElement
      */
      RUNTIME
      }

    官方解释是,这个注解会留在编译器,class 文件,和 VM 中,我理解为作用范围。一般使用 RUNTIME。

    接着造一个 bean。

public class DemoBean {
    public String pubField;
    protected String protectField;
    String defaultField;
    @AnnotationDemo(name="test",value="123")
    private String priField;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DemoBean(){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>("pub","protect","default","pri"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> DemoBean(String pubField,String protectField,String defaultField,String priField){
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pubField =<span style="color: rgba(0, 0, 0, 1)"> pubField;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.protectField =<span style="color: rgba(0, 0, 0, 1)"> protectField;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.defaultField =<span style="color: rgba(0, 0, 0, 1)"> defaultField;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.priField =<span style="color: rgba(0, 0, 0, 1)"> priField;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> function1(){
    System.out.println(</span>"public function"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> function2(){
    System.out.println(</span>"protect function"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> function3(){
    System.out.println(</span>"default function"<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> function4(){
    System.out.println(</span>"private function"<span style="color: rgba(0, 0, 0, 1)">);
}

}

    在拿到 class 之后,遍历它的 field 寻找注解,当然了,对 method 也可以这样。

            Field[] fields = clazz.getDeclaredFields();//拿到它定义的所有字段
            for(Field field:fields){
                if(field.isAnnotationPresent(AnnotationDemo.class)){
                    System.out.println("有注解");}
                AnnotationDemo annotationDemo = field.getAnnotation(AnnotationDemo.class);
                if(annotationDemo != null){
                    System.out.println("注解 name:"+annotationDemo.name());
                    System.out.println("注解 value:"+annotationDemo.value());}
                System.out.println("属性:"+field.getName()+" "+field.getModifiers());}

    

    这种方式是不是很眼熟啊?没错,Spring 里面到处都是。

  • 实际应用

    在给字段和方法打上标签之后,繁琐的,重复性的行为都让框架为你处理,这种开发方式节省了很多代码,加强了阅读性,是非常提升效率的。

    反射有种方式是绕过编译器对字段封装性的限制的,也就是无论是 public 还是 private 的字段,在反射的程序中都是可以拿到并且改变它的值的。我们知道 Spring 框架对 bean 的注入,有好几种方式。最让人想的清楚的是构造方法注入和 setter 注入。而 @autowired 呢?即使不提供暴露接口一样可以设置,这就是利用了反射的方式。

    经过查阅资料,阅读源码,在 Spring-bean 中寻找到这两个类,因为设计图太过复杂,本人只能在细小之处分析了。

/**
     * Populate the bean instance in the given BeanWrapper with the property values
     * from the bean definition.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @param bw BeanWrapper with bean instance
     */
    protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (bw == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">pvs.isEmpty()) {
            </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> BeanCreationException(
                    mbd.getResourceDescription(), beanName, </span>"Cannot apply property values to null instance"<span style="color: rgba(0, 0, 0, 1)">);
        }
        </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Skip property population phase for null instance.</span>
            <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
        }
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> state of the bean before properties are set. This can be used, for example,
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> to support styles of field injection.</span>
    <span style="color: rgba(0, 0, 255, 1)">boolean</span> continueWithPropertyPopulation = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!mbd.isSynthetic() &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> hasInstantiationAwareBeanPostProcessors()) {
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (BeanPostProcessor bp : getBeanPostProcessors()) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (bp <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp </span>=<span style="color: rgba(0, 0, 0, 1)"> (InstantiationAwareBeanPostProcessor) bp;
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    continueWithPropertyPopulation </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
                    </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
                }
            }
        }
    }

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">continueWithPropertyPopulation) {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||<span style="color: rgba(0, 0, 0, 1)">
            mbd.getResolvedAutowireMode() </span>==<span style="color: rgba(0, 0, 0, 1)"> RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MutablePropertyValues(pvs);

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add property values based on autowire by name if applicable.</span>
        <span style="color: rgba(0, 0, 255, 1)">if</span> (mbd.getResolvedAutowireMode() ==<span style="color: rgba(0, 0, 0, 1)"> RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }

        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Add property values based on autowire by type if applicable.</span>
        <span style="color: rgba(0, 0, 255, 1)">if</span> (mbd.getResolvedAutowireMode() ==<span style="color: rgba(0, 0, 0, 1)"> RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }

        pvs </span>=<span style="color: rgba(0, 0, 0, 1)"> newPvs;
    }

    </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> hasInstAwareBpps =<span style="color: rgba(0, 0, 0, 1)"> hasInstantiationAwareBeanPostProcessors();
    </span><span style="color: rgba(0, 0, 255, 1)">boolean</span> needsDepCheck = (mbd.getDependencyCheck() !=<span style="color: rgba(0, 0, 0, 1)"> RootBeanDefinition.DEPENDENCY_CHECK_NONE);

    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (hasInstAwareBpps ||<span style="color: rgba(0, 0, 0, 1)"> needsDepCheck) {
        PropertyDescriptor[] filteredPds </span>=<span style="color: rgba(0, 0, 0, 1)"> filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hasInstAwareBpps) {
            </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (BeanPostProcessor bp : getBeanPostProcessors()) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (bp <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp </span>=<span style="color: rgba(0, 0, 0, 1)"> (InstantiationAwareBeanPostProcessor) bp;
                    pvs </span>=<span style="color: rgba(0, 0, 0, 1)"> ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (pvs == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
                    }
                }
            }
        }
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (needsDepCheck) {
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }

    applyPropertyValues(beanName, mbd, bw, pvs);
}</span></pre>

    经过一系列前置验证(看得迷迷糊糊)然后进行 bean 注入,调用的是 postProcessPropertyValues 方法,点进去是个接口(多态性是挺坑的)。找了一下,最后找到 AutowiredAnnotationBeanPostProcessor 类,里面有个内部类 AutowiredMethodElement,其中有个 Inject 方法是实施注入的。

    

    public static void makeAccessible(Field field) {
        if((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
            field.setAccessible(true);}
}</span></pre>
    @CallerSensitive
    public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();}
        return ma.invoke(obj, args);
    }

    前一个是打开权限,后一个粗略看了下,就是注入了。

    大概的思路就是使用反射打开权限,然后从对象池中拿取对象并设置到该字段中。如果对象池没有,大概就是放个空进去了,最后调用的时候就是空指针了。

  • 实践案例

    未能吸取 Spring 优秀框架的思想,但是自己在项目中应用了一下这种技术。小型项目对建表的要求就是,新建一个 bean 自动生成一个表。是不是很像某 ORM?是的,仅仅使用几百行代码就可以实现这个功能了,采用的就是以反射、Annotation 为基础的技术。

package Common;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import org.apache.log4j.Logger;
import Common.Annotation.ATable;
import Common.Annotation.AutoIncrement;
import Common.Annotation.Column;
import Common.Annotation.PrimaryKey;

/**

  • 初始化数据库 按照 Model 包下的类及字段创建
  • @author ctk

*/

public class InitDataBases {

</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Logger logger = Logger.getLogger(InitDataBases.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">private</span> Connection conn = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> Set&lt;String&gt;<span style="color: rgba(0, 0, 0, 1)"> tables;

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">单例</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> InitDataBases instance = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> InitDataBases();
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> InitDataBases(){
    conn </span>=<span style="color: rgba(0, 0, 0, 1)"> getConnection();
    tables </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> HashSet&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    searchTables();
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 读取数据库资源文件
 * 获得数据库链接
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Connection getConnection(){
    logger.debug(</span>"建立数据库连接"<span style="color: rgba(0, 0, 0, 1)">);
    String driver </span>= ""<span style="color: rgba(0, 0, 0, 1)">;
    String url </span>= ""<span style="color: rgba(0, 0, 0, 1)">;
    String username </span>= ""<span style="color: rgba(0, 0, 0, 1)">;
    String password </span>= ""<span style="color: rgba(0, 0, 0, 1)">;
    
    File f </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(<span style="color: rgba(0, 0, 255, 1)">this</span>.getClass().getClassLoader().getResource("/").getPath()+"jdbc.properties"<span style="color: rgba(0, 0, 0, 1)">);
    
    Properties pro </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Properties();
    InputStream in </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        in </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> FileInputStream(f);
        pro.load(in);
        driver </span>= pro.getProperty("jdbc.driverClass"<span style="color: rgba(0, 0, 0, 1)">);
        url </span>= pro.getProperty("jdbc.url"<span style="color: rgba(0, 0, 0, 1)">);
        username </span>= pro.getProperty("jdbc.username"<span style="color: rgba(0, 0, 0, 1)">);
        password </span>= pro.getProperty("jdbc.password"<span style="color: rgba(0, 0, 0, 1)">);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (FileNotFoundException e) {
        logger.error(</span>"资源文件未找到,请命名为jdbc.properties,并置于src下"<span style="color: rgba(0, 0, 0, 1)">);
        System.err.println(</span>"资源文件未找到,请命名为jdbc.properties,并置于src下"<span style="color: rgba(0, 0, 0, 1)">);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) {
        logger.error(</span>"资源文件读写异常"<span style="color: rgba(0, 0, 0, 1)">);
        System.err.println(</span>"资源文件读写异常"<span style="color: rgba(0, 0, 0, 1)">);
    }
    Connection conn </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        Class.forName(driver);
        conn </span>=<span style="color: rgba(0, 0, 0, 1)"> DriverManager.getConnection(url,username,password);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (ClassNotFoundException e) {
        logger.error(</span>"加载驱动不成功,请检查是否添加了jdbc的必要包"<span style="color: rgba(0, 0, 0, 1)">);
        System.err.println(</span>"加载驱动不成功,请检查是否添加了jdbc的必要包"<span style="color: rgba(0, 0, 0, 1)">);
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"数据库连接错误,检查账号密码和数据库地址"<span style="color: rgba(0, 0, 0, 1)">);
        System.err.println(</span>"数据库连接错误,检查账号密码和数据库地址"<span style="color: rgba(0, 0, 0, 1)">);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> conn;
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 自动建表
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> clazz
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> checkAndCreate(Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)"> clazz){
    String tableName </span>=<span style="color: rgba(0, 0, 0, 1)"> getTableName(clazz);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(tableExist(tableName))
    {
        logger.debug(tableName</span>+"表已存在"<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
    }
    Field[] fields </span>=<span style="color: rgba(0, 0, 0, 1)"> clazz.getDeclaredFields();
    StringBuilder sb </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> StringBuilder("create table "<span style="color: rgba(0, 0, 0, 1)">);
    sb.append(tableName);
    sb.append(</span>" ("<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">for</span>(<span style="color: rgba(0, 0, 255, 1)">int</span> i=0;i&lt;fields.length;i++<span style="color: rgba(0, 0, 0, 1)">){
        PrimaryKey pk </span>= fields[i].getAnnotation(PrimaryKey.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
        sb.append(getColumnName(fields[i]));
        sb.append(</span>" "<span style="color: rgba(0, 0, 0, 1)">);
        Class</span>&lt;?&gt; type =<span style="color: rgba(0, 0, 0, 1)"> fields[i].getType();
        </span><span style="color: rgba(0, 0, 255, 1)">if</span>(type == String.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
            sb.append(</span>"VARCHAR(255)"<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(type == <span style="color: rgba(0, 0, 255, 1)">int</span>.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
            sb.append(</span>"INT(50)"<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(type == <span style="color: rgba(0, 0, 255, 1)">long</span>.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
            sb.append(</span>"BIGINT(20)"<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span>(type == <span style="color: rgba(0, 0, 255, 1)">double</span>.<span style="color: rgba(0, 0, 255, 1)">class</span> || type == <span style="color: rgba(0, 0, 255, 1)">float</span>.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
            sb.append(</span>"DOUBLE"<span style="color: rgba(0, 0, 0, 1)">);
        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">如果是主键字段</span>
        <span style="color: rgba(0, 0, 255, 1)">if</span>(pk != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
            sb.append(</span>" primary key"<span style="color: rgba(0, 0, 0, 1)">);
            AutoIncrement ai </span>= fields[i].getAnnotation(AutoIncrement.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">判断是否自增</span>
            <span style="color: rgba(0, 0, 255, 1)">if</span>(ai != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
                sb.append(</span>" AUTO_INCREMENT"<span style="color: rgba(0, 0, 0, 1)">);
            }
        }
        </span><span style="color: rgba(0, 0, 255, 1)">if</span>(i != (fields.length-1<span style="color: rgba(0, 0, 0, 1)">))
            sb.append(</span>","<span style="color: rgba(0, 0, 0, 1)">);
    }
    sb.append(</span>")DEFAULT CHARSET=utf8"<span style="color: rgba(0, 0, 0, 1)">);
    logger.debug(</span>"sql:"+<span style="color: rgba(0, 0, 0, 1)">sb.toString());
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        PreparedStatement pst </span>=<span style="color: rgba(0, 0, 0, 1)"> conn.prepareStatement(sb.toString());
        pst.execute();
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"建表错误:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());
    }
    
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 获得表名字
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> clazz
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> String getTableName(Class&lt;?&gt;<span style="color: rgba(0, 0, 0, 1)"> clazz){
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获得表别名</span>
    ATable table = clazz.getAnnotation(ATable.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(table != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; ""<span style="color: rgba(0, 0, 0, 1)">.equals(table.name()))
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> table.name();
    </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
    {
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> clazz.getSimpleName();
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 获取列名称
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> field
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getColumnName(Field field){
    Column column </span>= field.getAnnotation(Column.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(column != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> column.value();
    }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> field.getName();
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 查询表是否存在
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> searchTables(){
    String sql </span>= "show tables"<span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        PreparedStatement pst </span>=<span style="color: rgba(0, 0, 0, 1)"> conn.prepareStatement(sql);
        logger.debug(</span>"sql执行:"+<span style="color: rgba(0, 0, 0, 1)">sql);
        ResultSet rset </span>=<span style="color: rgba(0, 0, 0, 1)"> pst.executeQuery();
        </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)">(rset.next()){
            String tname </span>= rset.getString(1<span style="color: rgba(0, 0, 0, 1)">);
            tables.add(tname);
        }
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"sql错误:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 判断是否存在某数据
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> sql
 * </span><span style="color: rgba(128, 128, 128, 1)">@return</span>
 <span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> dataExist(String sql){
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        PreparedStatement pst </span>=<span style="color: rgba(0, 0, 0, 1)"> conn.prepareStatement(sql);
        logger.debug(</span>"sql执行:"+<span style="color: rgba(0, 0, 0, 1)">sql);
        ResultSet rset </span>=<span style="color: rgba(0, 0, 0, 1)"> pst.executeQuery();
        </span><span style="color: rgba(0, 0, 255, 1)">long</span> id = 0<span style="color: rgba(0, 0, 0, 1)">;
        </span><span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)">(rset.next()){
            id </span>= rset.getLong("id"<span style="color: rgba(0, 0, 0, 1)">);
        }
        </span><span style="color: rgba(0, 0, 255, 1)">if</span>(id == 0<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
        </span><span style="color: rgba(0, 0, 255, 1)">else</span>
            <span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"sql错误:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());
        </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
 * 插入数据sql
 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> sql
 </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> insertSql(String sql){
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        PreparedStatement pst </span>=<span style="color: rgba(0, 0, 0, 1)"> conn.prepareStatement(sql);
        logger.debug(</span>"sql执行:"+<span style="color: rgba(0, 0, 0, 1)">sql);
        pst.execute();
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"sql错误:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());
    }
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获得单例</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> InitDataBases getInstance(){
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> instance;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> tableExist(String table) {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> tables.contains(table);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关闭链接</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> closeConn(){
    logger.debug(</span>"关闭数据库连接..."<span style="color: rgba(0, 0, 0, 1)">);
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        conn.close();
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) {
        logger.error(</span>"关闭数据库连接失败:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());
    }
}

}

    然后在启动的 Listener 中加入。

        CommonInfo.FilePackage = this.getClass().getClassLoader().getResource("/").getPath()+"/WEB-INF/fildDownload";
        File pkg = new File(CommonInfo.FilePackage);
        if(!pkg.exists())pkg.mkdirs();
    File f </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> File(<span style="color: rgba(0, 0, 255, 1)">this</span>.getClass().getClassLoader().getResource("/").getPath()+"/Model"<span style="color: rgba(0, 0, 0, 1)">);
    File[] fs </span>=<span style="color: rgba(0, 0, 0, 1)"> f.listFiles();
    List</span>&lt;String&gt; tables = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)">(File fl:fs){
        String fname </span>=<span style="color: rgba(0, 0, 0, 1)"> fl.getName();
        fname </span>= fname.substring(0,fname.length()-6<span style="color: rgba(0, 0, 0, 1)">);
        tables.add(fname);
    }
    InitDataBases initDB </span>=<span style="color: rgba(0, 0, 0, 1)"> InitDataBases.getInstance();
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
        </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> (String table : tables) {
            Class</span>&lt;?&gt; clazz = Class.forName("Model." +<span style="color: rgba(0, 0, 0, 1)"> table);
            initDB.checkAndCreate(clazz);
        }
    } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (ClassNotFoundException e) {
        logger.error(</span>"找不到bean:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage());;
    </span></pre>

    大概的思路就是,检查 bean 文件夹下是否存在 bean 并且 bean 中是否有 Annotation 修饰,并拼凑建表语句,最后新建表,别忘了关闭数据库连接。