举个简单的例子吧,假设我们想把一个对象的所有属性值存到数据库里,通常会用到
insert into XXX (field1,field2) values (v001,v002)
像这种语句,格式都一个样,唯一的区别就是XXX表和表的字段与值的不同。但是针对不同的表,总要重复写这条语句,然后一条一条复制粘贴表里的所有字段,再一条一条复制粘贴所有的字段值,要是表多而且字段多的话,是不是看着有点累还容易出错……
所以为什么不写个程序,让它通过传递参数来找到这个类名,以及相应属性和和值然后自动生成这条sql语句呢?如此一来,岂不美哉。
那么我来尝试偷懒一下,直接上代码:
(为了简单,我们在这里假设数据库的字段和我们javabean的字段值一样,数据库表名与我们的javabean类名一样,且没有Boolean类型的成员)
public static String getSaveSQL(Object javaBean) {
StringBuilder sb = new StringBuilder(); // 由于需要增加的内容较多,固采用StringBuilder代替String
Class<?> targetClass = javaBean.getClass(); // 目标类的Class对象
String className = targetClass.getSimpleName(); // 简单类名
sb.append("insert into ");
sb.append(className); // 类名即对应表名
sb.append(" (");
Field[] fields = targetClass.getDeclaredFields(); // 获得属性域
List<String> fieldList = new ArrayList<>(); // 存储属性名的数组
List<String> methodList = new ArrayList<>(); // 存储方法名的数组
String fieldName = null;
String methodName = null;
for (Field field : fields) { // 遍历属性域,获得对应属性名和方法名
fieldName = field.getName();
methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
fieldList.add(fieldName);
methodList.add(methodName);
}
int i;
for (i = 0; i < fieldList.size(); i++) { // 构建sql语句中的字段部分
sb.append(fieldList.get(i));
if (i != fieldList.size() - 1)
sb.append(",");
}
sb.append(") values (");
Object to = null;
Method tm = null;
try {
for (i = 0; i < methodList.size(); i++) { // 构建sql语句中的值部分
tm = targetClass.getMethod(methodList.get(i));
to = tm.invoke(javaBean);
if (to instanceof String || to instanceof Boolean) {
sb.append("\'" + to + "\'");
} else {
sb.append(to);
}
if (i != methodList.size() - 1)
sb.append(",");
}
sb.append(")");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return sb.toString();
}
大功告成。这么一来的话,以后我们想获取这条存储的sql语句,只要调用这个方法传入相应的类名参数就行了?省力了不少吧。当然其他的类似语句都可以通过这个方式生成。
————————————————————————
再举个例子吧,比如说我们要把javabean的属性和属性值放到一个map里,如果用反射的话那就更简单了:
public static Map addJavaBean(Map map, Object javaBean) {
Class<?> classType = javaBean.getClass(); // 获得对象类型
Field[] fields = classType.getDeclaredFields(); // 获得对象所有属性域
for (Field field : fields) { // 遍历属性域,并获得其值
String attributeName = field.getName(); // 获得属性名
String methodGet = "get" + attributeName.substring(0, 1).toUpperCase() + attributeName.substring(1); // javabean的get()方法
try {
Method get = classType.getMethod(methodGet);// 获得get方法的域
Object attributeValue = get.invoke(javaBean);// 调用get方法,返回属性值
map.put(attributeName, attributeValue); // 添加到map中
} catch (NoSuchMethodException e) {
e.printStackTrace(System.out);
} catch (SecurityException e) {
e.printStackTrace(System.out);
} catch (IllegalAccessException e) {
e.printStackTrace(System.out);
} catch (IllegalArgumentException e) {
e.printStackTrace(System.out);
} catch (InvocationTargetException e) {
e.printStackTrace(System.out);
}
}
return map;
}
当然你也可以写个方法,删除不必要的属性域。
————————————————————————————
以上都是对javabean的操作,用熟练的话舒服的一批。
要不再搞个复杂点的……比如说动态代理什么的?
public class DAOfactory {
@SuppressWarnings("unchecked")
protected static <T> T getProxyInstance(Class<?> daoClass, Class<? extends DAO> implClass)
throws IllegalArgumentException, InstantiationException, IllegalAccessException {
return (T) Proxy.newProxyInstance(daoClass.getClassLoader(), new Class[] { daoClass },
new DynamicProxy(implClass.newInstance()));
}
public static StudentDAO getStudentDAOInstance() {
try {
return getProxyInstance(StudentDAO.class, StudentDAOimpl.class);
} catch (IllegalArgumentException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
public static TeacherDAO getTeacherDAOInstance() {
try {
return getProxyInstance(TeacherDAO.class, TeacherDAOimpl.class);
} catch (IllegalArgumentException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
}
在这里面吧,我想让类在实例化的时候自动在工厂里生成它的实例,并且找到它的代理。
其中上面的Proxy.newProxyInstance是java里关于反射的一个方法,前两个参数大家都能看懂,至于第三个参数做了什么,还得我们自己实现。
public class DynamicProxy implements InvocationHandler {
Object proxied; // 被代理的impl对象
public DynamicProxy(Object proxied) { // 传入被代理的对象
this.proxied = proxied;
}
/*
* 重载invoke方法 功能:实现对impl类的代理,同时实现数据的库的连接,以事务的方式发送
*
* @method:接口类被调用的方法
*
* @args:调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Connection conn = MyDBConnectionFactory.getMyDBConnection();
Class<? extends DAO> clazz = (Class<? extends DAO>) proxied.getClass();
Object retobj = null;
try {
Method setConnMethod = clazz.getMethod("setConnection", new Class[] { Connection.class });
setConnMethod.invoke(proxied, conn);
conn.setAutoCommit(false);
retobj = method.invoke(proxied, args);
conn.commit();
} catch (SQLException e) {
conn.rollback();
e.printStackTrace();
retobj = null;
} finally {
conn.close();
}
return retobj;
}
}
比如说我们这么写,那么数据库的连接和事务回滚我们就把它写到代理里面了。
这样一来呢,以后我们要找dao的实例,只需要
this.teacherDAO = DAOfactory.getTeacherDAOInstance();
就可以啦。
(如果不用动态代理,得在每个impl后都写个proxy,代码将大量重复,看着都心烦)
——————————————————————
反正呢,熟悉反射之后,一般我看到有跟javabean有关字符串频繁出现,都会下意识的搞个小方法让它自动生成,然后对一些设计模式和框架底层实现也可以帮助理解啊,总之能用代码偷懒,就很舒服。
作者:寂地
链接:https://www.zhihu.com/question/66525147/answer/243139185
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。