什么是反射
其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成Class
对象,并将字节码文件中的内容都封装成其他对象,比如Constructor、Field、Method、Modifier
,这样便于操作这些成员。
Class对象: Class类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注解是一种接口。每个数组也属于一个类,该类反映为 Class 对象,该对象由具有相同元素类型和维数的所有数组共享。 Java 原始类型(boolean、 byte、 char、 short、 int、 long、 float 和 double),关键字 void也表示为 Class对象。
Constructor<T>
提供关于类的单个构造方法的信息以及对它的访问权限
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
Parameter
提供有关方法参数的信息,包括其名称和修饰符。它还提供了一种获取参数属性的替代方法。
Modifier
类提供了 static 方法和常量,对类和成员访问修饰符进行解码。
简单说:反射技术可以对一个类进行解剖。Class看做是反射的源头
反射优点:
- 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力。
- 允许程序创建和控制任何类的对象,无需提前硬编码目标类
反射缺点:
- 反射的性能较低。
- 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上。
- 反射会模糊程序内部逻辑,可读性较差。
体会反射的动态性
正射: 代码中直接创建对象
// 静态性
public User getInstance() {
return new User();
}
反射
// 反射的动态性
public <T> T getInstance(String className) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName(className);
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return (T) constructor.newInstance();
}
反射的基本步骤
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
反射常用API
获取对象
获取字节码Class对象
Class<?> clazz = Class.forName("类的全路径");
Class<?> clazz = 类名.class;
Class<?> clazz = 对象.getClass();
获取包名&类名
clazz.getPackage().getName()//包名
clazz.getSimpleName()//类名
clazz.getName()//完整类名
clazz.getAnnotation(Pro.class); //类的注解信息
获取构造方法定义信息
clazz.getConstructor(参数类型列表)//获取公开的构造方法
clazz.getConstructors()//获取所有的公开的构造方法
clazz.getDeclaredConstructors()//获取所有的构造方法,包括私有
clazz.getDeclaredConstructor(int.class,String.class)
获取方法定义信息
clazz.getMethods()//获取所有可见的方法,包括继承的方法
clazz.getMethod(方法名,参数类型列表)
clazz.getDeclaredMethods()//获取本类定义的的方法,包括私有,不包括继承的方法
clazz.getDeclaredMethod(方法名,int.class,String.class)
获取成员变量定义信息
clazz.getFields()//获取所有公开的成员变量,包括继承变量
clazz.getDeclaredFields()//获取本类定义的成员变量,包括私有,但不包括继承的变量
clazz.getField(变量名)
clazz.getDeclaredField(变量名)
设置对象
使用commons-lang3:3.9
提供的反射工具类
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
反射创建实例
jdk方式获取
clazz.newInstance();//执行无参构造创建对象
Constructor<?> c = clazz.getConstructor(String.class,Integer.class)//获取构造方法
c.newInstance("百里玄策",20);//执行含参构造创建对象
使用commons-lang3:3.9
提供的反射工具类
/**
* 测试构造器反射工具类
*/
@Test
public void constructorUtilsTest() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
User user = ConstructorUtils.invokeConstructor(User.class);
System.out.println(user);
}
反射调用成员变量
jdk方式获取
clazz.getDeclaredField(变量名);//获取本类定义的成员变量,包括私有,但不包括继承的变量
clazz.setAccessible(true);//使私有成员允许访问
f.set(实例,值);//为指定实例的变量赋值,静态变量,第一参数给null
f.get(实例);//访问指定实例变量的值,静态变量,第一参数给null
使用commons-lang3:3.9
提供的反射工具类
/**
* 测试属性反射工具类
*/
@Test
public void fieldUtilsTest() throws IllegalAccessException {
User user = new User();
user.setUsername("zysheep");
user.setAge(20);
// 反射设置属性值
FieldUtils.writeField(user,"createTime",new Date(),true);
Date createTime = user.getCreateTime();
log.info("createTime: {}",createTime);
// 反射获取属性值,能获取到来自父类的属性
Object username = FieldUtils.readField(user, "username",true);
// Field field = FieldUtils.getField(user.getClass(), "username");
log.info("username: {}",username);
}
反射调用成员方法
jdk方式获取
Method m = Clazz.getDeclaredMethod(方法名,参数类型列表);
m.setAccessible(true);//使私有方法允许被调用
m.invoke(实例,参数数据);//让指定实例来执行该方法
使用commons-lang3:3.9
提供的反射工具类
/**
* 测试方法反射工具类
*/
@Test
public void methodUtilsTest() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
MethodUtils.invokeMethod(new User(), true, "say");
}
测试反射API
测试实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
public String username;
Integer age;
//定义成员方法
public void run() {
System.out.println("生命不止,运动不息!");
}
@Override
public String toString() {
return "Student [name="+ username +",age="+ age +"]";
}
}
测试获取类对象
/**
* 获取类对象
* 1、Class.forName(“类的全路径”);
* 2、类名.class
* 3、对象.getClass();
*
* @throws Exception
*/
@Test
public void getClazz() throws Exception {
Class<?> student1 = Class.forName("cn.zysheep.reflect.Student");//此处的参数是类的全路径名[包名+类名]
Class<?> student2 = Student.class;
Class<?> student3 = new Student().getClass();//先创建匿名对象,匿名对象没有名字,然后对象的字节码对象
System.out.println("反射得到的字节码Class对象: "+ student1);
System.out.println("获取类的全路径名[包名+类名]: "+ student2.getName());
System.out.println("只获取类名: "+ student3.getSimpleName());
System.out.println("获取包名: "+student3.getPackage().getName());
}
测试获取构造方法
/**
* 获取构造方法
*/
@Test
public void getConstruct() {
//1.获取字节码Class对象
Class<?> clazz = Student.class;
//2.获取构造方法们,并放入cs数组中
Constructor<?>[] cs = clazz.getConstructors();
//3.获取每个构造,有参构造,无参构造
//使用增强for循环完成
for (Constructor c : cs) {
//拿到每个构造方法以后可以获取构造方法的相关信息
System.out.println("获取构造方法的名称: "+c.getName());
Class[] cp = c.getParameterTypes();
System.out.println("获取构造方法的参数类型,可能有多个: "+Arrays.toString(cp));
}
}
测试获取方法
/**
* 获取成员方法
*/
@Test
public void getMethods() throws NoSuchMethodException {
//1.获取Class字节码对象
Class<?> clazz = Student.class;
//2.获取所有的成员变量,公共的!!!
/**
* getMethod(String name, Class<?>... parameterTypes)
* name: 方法名
* parameterTypes: 参数类型字节码
*/
Method method = clazz.getMethod("run",String.class);
// 所有方法,包括父类的方法
Method[] methods = clazz.getMethods();
//3.遍历数组,获取每个成员变量的信息
for (Method m: methods) {
System.out.println("获取方法名: "+m.getName());
System.out.println("获取参数: "+ Arrays.toString(m.getParameters()));
System.out.println("获取参数类型: "+ Arrays.toString(m.getParameterTypes()));
System.out.println("获取返回值类型: "+m.getReturnType());
}
}
测试获取成员变量
/**
* 获取成员变量
*/
@Test
public void getFields(){
//1.获取Class字节码对象
//Class<?>中的"?"是泛型约束的通配符,类似于"*"
Class<?> clazz = Student.class;
//2.获取所有的成员变量,公共的!!!
//!!!注意目前成员变量的修饰符必须是public才能获取到,采用默认修饰符就反射不到
Field[] fs = clazz.getFields();
//3.遍历数组,获取每个成员变量的信息
for (Field f: fs) {
System.out.println("获取变量名: "+f.getName());
System.out.println("获取变量类型: "+f.getType().getName());
}
}
测试获取标注的注解信息
不设置@Target注解标记的位置,@Retention注解作用的范围,注解不生效
1、定义@Pro注解
@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
// 名字
String name() default "";
// 内容描述
String desc() default "";
}
2、 使用@Pro注解
@Data
@AllArgsConstructor
@NoArgsConstructor
@Pro(name = "学生类",desc = "学生实体类")
public class Student {
@Pro(name = "username",desc = "学生姓名")
public String username;
Integer age;
//定义成员方法
@Pro(name = "run",desc = "跑步方法")
public void run(String username) {
System.out.println("生命不止,运动不息!"+username);
}
}
3、获取@Pro注解
/**
* 获取注解信息
* @throws NoSuchFieldException
* @throws NoSuchMethodException
*/
@Test
void getAnnotation() throws NoSuchFieldException, NoSuchMethodException {
Class<Student> clazz = Student.class;
Pro var1 = clazz.getAnnotation(Pro.class);
Pro var2 = clazz.getField("username").getAnnotation(Pro.class);
Pro var3 = clazz.getMethod("run", String.class).getAnnotation(Pro.class);
System.out.println("获取类上的注解信息: "+var1);
System.out.println("获取属性上的注解信息: "+var2);
System.out.println("获取方法上的注解信息: "+var3);
}
测试反射创建对象
/**
* 反射创建对象
* 方式一:通过字节码对象直接调用newInstance(),触发无参构造来创建对象
* 方式二:先获取指定的构造函数,再通过构造函数对象调用newInstance(),触发对应的构造函数来创建对象
* @throws Exception
*/
@Test
public void getObject () throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Student.class;
//2.创建对象
Object obj = clazz.newInstance();//触发无参构造
System.out.println("反射创建的对象: "+obj);//Student [name=null,age=0]
//3.可以指定去调用哪个构造方法,注意根据参数来指定,而且传入的是参数的字节码对象
Constructor<?> c = clazz.getConstructor(String.class,Integer.class);
//4.触发指定的构造方法
Object obj2 = c.newInstance("百里玄策",20);
System.out.println(obj2);//Student [name=百里玄策,age=3]
//5.查看对象具体的属性值,或者调用方法,需要把Object强转成指定的子类类型
/**为什么要把Object强转成子类类型?因为想要使用子类的特有功能,父类无法使用子类的特有功能
* obj是Object类型,Object中没有Student的属性与功能
* 这个操作叫做向下转型--想使用子类特有功能时需要做此操作
*/
Student student = (Student)obj2;
System.out.println(student.getUsername()); // 百里玄策
System.out.println(student.getAge()); // 20
student.run(); // 生命不止,运动不息!
}
注意: 获取有参构造时Constructor<?> c = clazz.getConstructor(String.class,Integer.class)
,参数类型的顺序必须和构造方法参数一致,否则反射失败
暴力反射
指可以将程序中的私有的属性或者方法通过反射技术,通过设置私有可见.setAccessible(true)
,暴力的获取到资源。
1、反射是解释执行的,和对象.方法
执行对比,对执行速度有影响,比较耗时。
2、反射调用优化: Method
和Field
、Constructor
对象都有setAccessible()
方法。参数设置为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查。
需要使用的常见方法如下:
测试实体类
@Data
public class Person {
// 提供私有属性
private String username;
private Integer age ;
private Person(){
System.out.println("执行私有构造");
}
private Person(String username){
System.out.println("执行有参私有构造: "+ username);
}
// 提供私有方法
private void save(Integer age,String username) {
System.out.println("执行私有方法save().."+username+age);
}
}
测试获取私有构造
/**
* 暴力反射获取私有方法
*
* @throws ClassNotFoundException
* @throws NoSuchMethodException
*/
@Test
public void getConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> clazz = Class.forName("cn.zysheep.reflect.Person");
// 获取私有无参构造
Constructor<?> constructor1 = clazz.getDeclaredConstructor();
// 获取私有有参构造
Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
// 设置无参私有构造可见
constructor1.setAccessible(true);
Object var1 = constructor1.newInstance();
// 创建实例,没有设置私有可见会报错
constructor2.setAccessible(true);
constructor2.newInstance("百里玄策");
}
注释constructor1.setAccessible(true);
运行报错
测试获取和设置私有属性
/**
* 暴力反射获取和设置私有属性
* @throws Exception
*/
@Test
public void getFields() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Person.class;
//2.获取私有属性
Field field = clazz.getDeclaredField("username");
//3.根据获取到的属性对象获取属性的类型
System.out.println(field.getType().getName());//java.lang.String
System.out.println(field.getType());//class java.lang.String
//4.设置属性的值
// 获取所有构造器
Constructor<?> constructor = clazz.getDeclaredConstructor();
// 设置私有构造器可见
constructor.setAccessible(true);
// 创建对象
Object obj = constructor.newInstance();
//4.2暴力反射!!!需要设置私有属性可见
field.setAccessible(true);
//4.3通过字段对象来给刚刚创建的对象obj设置username属性值为“百里玄策”
//set(Object obj, Object value)
// obj:要给哪个对象设置属性值,
// value:是要设置的属性的具体值
field.set(obj,"百里玄策");
//4.4通过字段对象获取刚刚给obj对象设置的私有属性的值
System.out.println(field.get(obj));
System.out.println(obj);
}
测试获取和设置私有方法
/**
* 暴力反射获取和设置私有方法
* @throws Exception
*/
@Test
public void getFunction() throws Exception {
//1.获取Class字节码对象
Class<?> clazz = Person.class;
//2.通过暴力反射获取私有方法
/**
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
* name:要获取的方法名
* parameterTypes可变参数,是这个方法的参数类型,但注意要加“.class”
*/
Method method = clazz.getDeclaredMethod("save",Integer.class,String.class);
//3.1没有对象就通过反射的方式创建对象
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object obj = constructor.newInstance();
//3.2 想要执行私有方法,也需要先设置私有方法可见
method.setAccessible(true);
/**invoke(Object obj, Object... args),表示通过反射技术执行方法
* obj :要执行的是哪个对象的方法
* args:执行这个方法【method对象代表的之前获取到的save()】时需要传入的参数
*/
//3.3 通过反射技术invoke(),执行目标对象obj的目标方法method【save()】
//save()被调用时传入的参数是100,"百里玄策"
method.invoke(obj,100,"百里玄策");
}
/**
* Method getDeclaredMethod(String name, Class<?>... parameterTypes)
* name:要获取的方法名
* parameterTypes可变参数,是这个方法的参数类型,但注意要加“.class”
*/
Method method = clazz.getDeclaredMethod("save",Integer.class,String.class);
获取私有方法参数类型的顺序和私有方法类型顺序必须一致,否则报错
Java反射机制实际应用
Java反射机制提供的功能:
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法。
- 在运行时处理注解
- 生成动态代理
Java在运行时获取泛型实例的方法
java中的泛型其实是伪泛型,java在编译阶段会对变异类型进行擦除,擦出到泛型类的最小上限,编译后得到的class文件里面是没有任何泛型信息的,泛型的控制其实就是java编译器进行的控制,编译阶段进行泛型检查。那如果我们想在运行时知道泛型类的类型,如何做到呢?主要用到下面几个api:
1、public Type getGenericSuperclass()
—Class类的方法
2、Type[] getActualTypeArguments()
—ParameterizedType方法
3、public Type[] getGenericInterfaces()
—Class类的方法
定义一个抽象的泛型类
public abstract class BaseDao<T> {}
再定义一个子类
public class BaseDaoImpl extends BaseDao<String> {}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(BaseDaoImpl.class.getGenericSuperclass());
System.out.println(BaseDaoImpl.class.getSuperclass());
System.out.println(BaseDao.class.getGenericSuperclass());
System.out.println(BaseDao.class.getSuperclass());
}
运行结果如下:
从上面运行结果可以看出两点:
1、通过Class类的getGenericSuperclass()是可以获取到泛型信息的
2、泛型类本身是无法获取到泛型信息的,只能通过泛型类的子类来获取泛型信息。这也很好解释,因为泛型类比如BaseDao类自身,其泛型参数并未确定,自然无法获取泛型信息,而子类的泛型参数类型已经确定,父类泛型信息已经确定,所以可以查询。
具体的查询泛型参数类型的代码如下:
抽象类获取泛型实例的方法
public static void main(String[] args) throws ClassNotFoundException {
Class clazz = Class.forName("cn.zysheep.ref.BaseDaoImpl");
Type type = clazz.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType)type;
System.out.println(parameterizedType.getClass().getName());
System.out.println(parameterizedType);
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(Type actualTypeArgument: actualTypeArguments) {
System.out.println(actualTypeArgument.getClass().getName());
System.out.println(actualTypeArgument);
System.out.println(actualTypeArgument.getTypeName());
}
}
运行结果如下:
由上图运行结果可知:
1、getGenericSuperclass()返回的实际类型为ParameterizedTypeImpl,这里面带有父类的泛型类型信息;
2、获取泛型参数信息是通过ParameterizedType的getActualTypeArguments方法,该方法返回的是一个Type类型的数据,该数据存放的数据的实际类型为Class,也就是我们得到了泛型参数的类信息。
接口获取泛型实例的方法
public interface IService<T> {}
public class UserServiceImpl implements IService<String> {}
获取IService的泛型参数类型的方法如下:
public static void main(String[] args) throws ClassNotFoundException {
ParameterizedType parameterizedType = (ParameterizedType) UserServiceImpl.class.getGenericInterfaces()[0];
System.out.println(parameterizedType.getClass().getName());
System.out.println(parameterizedType);
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(Type actualTypeArgument: actualTypeArguments) {
System.out.println(actualTypeArgument.getClass().getName());
System.out.println(actualTypeArgument);
System.out.println(actualTypeArgument.getTypeName());
}
}
运行结果如下:
和泛型类区别就是getGenericSuperclass()
方法换成了getGenericInterfaces(),getGenericInterfaces()
方法返回的是Type[]
,因为一个类可以实现多个接口,所以想要获取哪个接口的泛型信息,需要指定数据下标,这里UserServiceImpl
类就实现了一个接口,所以直接UserServiceImpl.class.getGenericInterfaces()[0]
就可以了。
上面的代码有一个共同点,就是泛型类或者泛型接口已经有一个子类了,通过子类的Class信息可以获取到泛型父类或者泛型接口的泛型类型信息,那么如果没有子类,怎么直接获取泛型类的泛型类型信息呢?很简单,通过匿名内部类(其实还是通过子类)
new BaseDao<Integer>() {}.getClass().getGenericSuperclass();
new IService<Integer>() {}.getClass().getGenericInterfaces())[0];
在运行时处理注解
private String getTableName(Class<?> clazz) throws NotFoundAnnotationException {
if (clazz.isAnnotationPresent(Entity.class)) {
Entity entity = clazz.getAnnotation(Entity.class);
return entity.value();
} else {
throw new NotFoundAnnotationException(clazz.getName() + " is not Entity Annotation.");
}
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
if (field.isAnnotationPresent(Id.class)) {
Id id = field.getAnnotation(Id.class);
} else if (field.isAnnotationPresent(Column.class)) {
Column coulmn = field.getAnnotation(Column.class);
}
}
GenericUtil 工具类
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* 运行时获取泛型类型
*/
public class GenericUtil {
public static <T> T getSuperclassType(Object obj, int i) {
try {
Class<?> aClass = obj.getClass();//先得到类的字节码
Type genericSuperclass = aClass.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
ParameterizedType types = (ParameterizedType) genericSuperclass;//抽象类
Type[] actualTypeArguments = types.getActualTypeArguments();
Class<T> reponseClass = (Class) actualTypeArguments[i];
return reponseClass.newInstance();
} else {
return null;
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static <T> T getInterfaceType(Object obj, int i) {
try {
Class<?> aClass = obj.getClass();//先得到类的字节码
Type[] types = aClass.getGenericInterfaces();
ParameterizedType parameterizedType = (ParameterizedType) types[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Class<T> reponseClass = (Class) actualTypeArguments[i];
return reponseClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
public static Class<?> forName(String className) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
注解+泛型+反射实现通用CRUD工具类
@TableName、@Column、@Id
1、@TableName注解 : 表名称注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableName {
String value();
}
2、@Column 注解 : 表属性字段注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
/**
* 字段名称
*/
String value();
/**
* 字段的类型
* @return
*/
Class<?> type() default String.class;
/**
* 字段的长度
* @return
*/
int length() default 0;
}
3、@Id 注解 : 表主键字段注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {
String value();
}
NotFoundAnnotationException
public class NotFoundAnnotationException extends RuntimeException {
public NotFoundAnnotationException() {
}
public NotFoundAnnotationException(String message) {
super(message);
}
}
JdbcDaoHelper
public class JdbcDaoHelper {
/**
* 数据库用户名
*/
private static final String USER = "root";
/**
* 数据库密码
*/
private static final String PASSWORD = "root";
/**
* 连接数据库的地址
*/
private static final String URL = "jdbc:mysql//localhost:3306/study";
private static Connection conn;
/**
* 获得一个数据库连接对象
* @return java.sql.Connection实例
*/
public static Connection getConnection() {
try {
if (conn == null) {
Class.forName("oracle.jdbc.OracleDriver");
conn = DriverManager.getConnection(URL, USER, PASSWORD);
} else {
return conn;
}
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 释放数据库资源
*/
public static void release(PreparedStatement ps, ResultSet rs) {
try {
if (conn != null) {
conn.close();
conn = null;
}
if (ps != null) {
ps.close();
ps = null;
}
if (rs != null) {
rs.close();
rs = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
BaseDao
import java.util.List;
import java.util.Map;
public interface BaseDao<T> {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
void save(T t) throws Exception;
void delete(Object id, Class<T> clazz) throws Exception;
void update(T t) throws Exception;
T get(Object id, Class<T> clazz) throws Exception;
/**
* 根据条件查询
*
* @param sqlWhereMap key:条件字段名 value:条件字段值
* @param clazz
* @return
* @throws Exception
*/
List<T> findAllByConditions(Map<String, Object> sqlWhereMap, Class<T> clazz) throws Exception;
}
BaseDaoImpl
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;
import java.util.Date;
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
//表的别名
private static final String TABLE_ALIAS = "t";
@Override
public void save(T t) throws Exception {
Class<?> clazz = t.getClass();
//获得表名
String tableName = getTableName(clazz);
//获得字段
StringBuilder fieldNames = new StringBuilder(); //字段名
List<Object> fieldValues = new ArrayList<Object>(); //字段值
StringBuilder placeholders = new StringBuilder(); //占位符
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
if (field.isAnnotationPresent(Id.class)) {
fieldNames.append(field.getAnnotation(Id.class).value()).append(",");
fieldValues.add(pd.getReadMethod().invoke(t));
} else if (field.isAnnotationPresent(Column.class)) {
fieldNames.append(field.getAnnotation(Column.class).value()).append(",");
fieldValues.add(pd.getReadMethod().invoke(t));
}
placeholders.append("?").append(",");
}
//删除最后一个逗号
fieldNames.deleteCharAt(fieldNames.length() - 1);
placeholders.deleteCharAt(placeholders.length() - 1);
//拼接sql
StringBuilder sql = new StringBuilder("");
sql.append("insert into ").append(tableName)
.append(" (").append(fieldNames.toString())
.append(") values (").append(placeholders).append(")");
PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql.toString());
//设置SQL参数占位符的值
setParameter(fieldValues, ps, false);
//执行SQL
ps.execute();
JdbcDaoHelper.release(ps, null);
System.out.println(sql + "\n" + clazz.getSimpleName() + "添加成功!");
}
@Override
public void delete(Object id, Class<T> clazz) throws Exception {
//获得表名
String tableName = getTableName(clazz);
//获得ID字段名和值
String idFieldName = "";
boolean flag = false;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Id.class)) {
idFieldName = field.getAnnotation(Id.class).value();
flag = true;
break;
}
}
if (!flag) {
throw new NotFoundAnnotationException(clazz.getName() + " object not found id property.");
}
//拼装sql
String sql = "delete from " + tableName + " where " + idFieldName + "=?";
PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
ps.setObject(1, id);
//执行SQL
ps.execute();
JdbcDaoHelper.release(ps, null);
System.out.println(sql + "\n" + clazz.getSimpleName() + "删除成功!");
}
@Override
public void update(T t) throws Exception {
Class<?> clazz = t.getClass();
//获得表名
String tableName = getTableName(clazz);
//获得字段
List<Object> fieldNames = new ArrayList<Object>(); //字段名
List<Object> fieldValues = new ArrayList<Object>(); //字段值
List<String> placeholders = new ArrayList<String>();//占位符
String idFieldName = "";
Object idFieldValue = "";
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
PropertyDescriptor pd = new PropertyDescriptor(field.getName(), t.getClass());
if (field.isAnnotationPresent(Id.class)) {
idFieldName = field.getAnnotation(Id.class).value();
idFieldValue = pd.getReadMethod().invoke(t);
} else if (field.isAnnotationPresent(Column.class)) {
fieldNames.add(field.getAnnotation(Column.class).value());
fieldValues.add(pd.getReadMethod().invoke(t));
placeholders.add("?");
}
}
//ID作为更新条件,放在集合中的最后一个元素
fieldNames.add(idFieldName);
fieldValues.add(idFieldValue);
placeholders.add("?");
//拼接sql
StringBuilder sql = new StringBuilder("");
sql.append("update ").append(tableName).append(" set ");
int index = fieldNames.size() - 1;
for (int i = 0; i < index; i++) {
sql.append(fieldNames.get(i)).append("=").append(placeholders.get(i)).append(",");
}
sql.deleteCharAt(sql.length() - 1).append(" where ").append(fieldNames.get(index)).append("=").append("?");
//设置SQL参数占位符的值
PreparedStatement ps = JdbcDaoHelper.getConnection().prepareStatement(sql.toString());
setParameter(fieldValues, ps, false);
//执行SQL
ps.execute();
JdbcDaoHelper.release(ps, null);
System.out.println(sql + "\n" + clazz.getSimpleName() + "修改成功.");
}
@Override
public T get(Object id, Class<T> clazz) throws Exception {
String idFieldName = "";
Field[] fields = clazz.getDeclaredFields();
boolean flag = false;
for (Field field : fields) {
if (field.isAnnotationPresent(Id.class)) {
idFieldName = field.getAnnotation(Id.class).value();
flag = true;
break;
}
}
if (!flag) {
throw new NotFoundAnnotationException(clazz.getName() + " object not found id property.");
}
//拼装SQL
Map<String, Object> sqlWhereMap = new HashMap<String, Object>();
sqlWhereMap.put(TABLE_ALIAS + "." + idFieldName, id);
List<T> list = findAllByConditions(sqlWhereMap, clazz);
return list.size() > 0 ? list.get(0) : null;
}
@Override
public List<T> findAllByConditions(Map<String, Object> sqlWhereMap, Class<T> clazz) throws Exception {
List<T> list = new ArrayList<T>();
String tableName = getTableName(clazz);
String idFieldName = "";
//存储所有字段的信息
//通过反射获得要查询的字段
StringBuffer fieldNames = new StringBuffer();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String propertyName = field.getName();
if (field.isAnnotationPresent(Id.class)) {
idFieldName = field.getAnnotation(Id.class).value();
fieldNames.append(TABLE_ALIAS + "." + idFieldName)
.append(" as ").append(propertyName).append(",");
} else if (field.isAnnotationPresent(Column.class)) {
fieldNames.append(TABLE_ALIAS + "." + field.getAnnotation(Column.class).value())
.append(" as ").append(propertyName).append(",");
}
}
fieldNames.deleteCharAt(fieldNames.length() - 1);
//拼装SQL
String sql = "select " + fieldNames + " from " + tableName + " " + TABLE_ALIAS;
PreparedStatement ps = null;
List<Object> values = null;
if (sqlWhereMap != null) {
List<Object> sqlWhereWithValues = getSqlWhereWithValues(sqlWhereMap);
if (sqlWhereWithValues != null) {
//拼接SQL条件
String sqlWhere = (String) sqlWhereWithValues.get(0);
sql += sqlWhere;
//得到SQL条件中占位符的值
values = (List<Object>) sqlWhereWithValues.get(1);
}
}
//设置参数占位符的值
if (values != null) {
ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
setParameter(values, ps, true);
} else {
ps = JdbcDaoHelper.getConnection().prepareStatement(sql);
}
//执行SQL
ResultSet rs = ps.executeQuery();
while (rs.next()) {
T t = clazz.newInstance();
initObject(t, fields, rs);
list.add(t);
}
//释放资源
JdbcDaoHelper.release(ps, rs);
System.out.println(sql);
return list;
}
/**
* 根据结果集初始化对象
*/
private void initObject(T t, Field[] fields, ResultSet rs)
throws SQLException, IntrospectionException,
IllegalAccessException, InvocationTargetException, IntrospectionException {
for (Field field : fields) {
String propertyName = field.getName();
Object paramVal = null;
Class<?> clazzField = field.getType();
if (clazzField == String.class) {
paramVal = rs.getString(propertyName);
} else if (clazzField == short.class || clazzField == Short.class) {
paramVal = rs.getShort(propertyName);
} else if (clazzField == int.class || clazzField == Integer.class) {
paramVal = rs.getInt(propertyName);
} else if (clazzField == long.class || clazzField == Long.class) {
paramVal = rs.getLong(propertyName);
} else if (clazzField == float.class || clazzField == Float.class) {
paramVal = rs.getFloat(propertyName);
} else if (clazzField == double.class || clazzField == Double.class) {
paramVal = rs.getDouble(propertyName);
} else if (clazzField == boolean.class || clazzField == Boolean.class) {
paramVal = rs.getBoolean(propertyName);
} else if (clazzField == byte.class || clazzField == Byte.class) {
paramVal = rs.getByte(propertyName);
} else if (clazzField == char.class || clazzField == Character.class) {
paramVal = rs.getCharacterStream(propertyName);
} else if (clazzField == Date.class) {
paramVal = rs.getTimestamp(propertyName);
} else if (clazzField.isArray()) {
paramVal = rs.getString(propertyName).split(","); //以逗号分隔的字符串
}
PropertyDescriptor pd = new PropertyDescriptor(propertyName, t.getClass());
pd.getWriteMethod().invoke(t, paramVal);
}
}
/**
* 根据条件,返回sql条件和条件中占位符的值
*
* @param sqlWhereMap key:字段名 value:字段值
* @return 第一个元素为SQL条件,第二个元素为SQL条件中占位符的值
*/
private List<Object> getSqlWhereWithValues(Map<String, Object> sqlWhereMap) {
if (sqlWhereMap.size() < 1) return null;
List<Object> list = new ArrayList<Object>();
List<Object> fieldValues = new ArrayList<Object>();
StringBuffer sqlWhere = new StringBuffer(" where ");
Set<Map.Entry<String, Object>> entrySets = sqlWhereMap.entrySet();
for (Iterator<Map.Entry<String, Object>> iteraotr = entrySets.iterator(); iteraotr.hasNext(); ) {
Map.Entry<String, Object> entrySet = iteraotr.next();
fieldValues.add(entrySet.getValue());
Object value = entrySet.getValue();
if (value.getClass() == String.class) {
sqlWhere.append(entrySet.getKey()).append(" like ").append("?").append(" and ");
} else {
sqlWhere.append(entrySet.getKey()).append("=").append("?").append(" and ");
}
}
sqlWhere.delete(sqlWhere.lastIndexOf("and"), sqlWhere.length());
list.add(sqlWhere.toString());
list.add(fieldValues);
return list;
}
/**
* 获得表名
*/
private String getTableName(Class<?> clazz) throws NotFoundAnnotationException {
if (clazz.isAnnotationPresent(TableName.class)) {
TableName entity = clazz.getAnnotation(TableName.class);
return entity.value();
} else {
throw new NotFoundAnnotationException(clazz.getName() + " is not Entity Annotation.");
}
}
/**
* 设置SQL参数占位符的值
*/
private void setParameter(List<Object> values, PreparedStatement ps, boolean isSearch) throws SQLException {
for (int i = 1; i <= values.size(); i++) {
Object fieldValue = values.get(i - 1);
Class<?> clazzValue = fieldValue.getClass();
if (clazzValue == String.class) {
if (isSearch)
ps.setString(i, "%" + (String) fieldValue + "%");
else
ps.setString(i, (String) fieldValue);
} else if (clazzValue == boolean.class || clazzValue == Boolean.class) {
ps.setBoolean(i, (Boolean) fieldValue);
} else if (clazzValue == byte.class || clazzValue == Byte.class) {
ps.setByte(i, (Byte) fieldValue);
} else if (clazzValue == char.class || clazzValue == Character.class) {
ps.setObject(i, fieldValue, Types.CHAR);
} else if (clazzValue == Date.class) {
ps.setTimestamp(i, new Timestamp(((Date) fieldValue).getTime()));
} else if (clazzValue.isArray()) {
Object[] arrayValue = (Object[]) fieldValue;
StringBuffer sb = new StringBuffer();
for (int j = 0; j < arrayValue.length; j++) {
sb.append(arrayValue[j]).append("、");
}
ps.setString(i, sb.deleteCharAt(sb.length() - 1).toString());
} else {
ps.setObject(i, fieldValue, Types.NUMERIC);
}
}
}
}