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() &&<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<String><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<><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<?><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<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><?> 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<?><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> && ""<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><String> tables = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList<><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><?> 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 修饰,并拼凑建表语句,最后新建表,别忘了关闭数据库连接。