1.什么是反射
反射就是把Java类中的各个成分映射成一个个的Java对象。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
2.反射的作用
我们通过案例来了解,反射的作用,都说实践出真理,操作一遍理解更深刻。
案例:对集合数组进行拆分批量调用持久层对象进行批量插入,当使用的类只有一个的时候可能简单写法,不用反射会更简单,都是我可能有多个类,类的方法可能有批量删除,批量插入,批量更新等操作时我们就需要写很多重复的代码,只有我们使用反射原理来实现,写个工具类,通过传递不同的对象和方法实现上面的操作.
我们创建UserPojo实体类:
/**
* 用户信息
*/
public class UserPojo {
public String name;
public String sex;
public int age;
//get/set 和 toString 省略
}
创建持久层保持类UserDao:
/**
* 用来保存用户信息持久层
*/
public class UserDao {
public static void getUserInfo(List<UserPojo> userPojos){
System.out.println("---------批量调用分界线--------");
userPojos.forEach(userPojo -> {
//打印每个用户信息
System.out.println(userPojo.toString());
});
//将来这里肯定是调用一个对象然后通过mybatis方式批量插入数据库中
}
public static void getUserUser(List<UserPojo> userPojos){
System.out.println("---------批量调用分界线--------");
userPojos.forEach(userPojo -> {
//打印每个用户信息
System.out.println(userPojo.getName());
});
}
}
用于反射的工具类方法
其中方法的最后一个参数是可以传递多个参数类型的通过runObject.getMethod方法获取对象的一个具体的方法,通过method.invoke方法进行反射操作,调用Class下的newInstance( )静态方法来实例化对象以便操作
/**
* 分割遍历调用
* (该方法以后用在一个集合需要拆分批量调用对应对象的方法进行批量插入数据库的操作)
* @param collectionParameter 集合参数
* @param step 集合拆分的步长
* @param runObject 调用的对象
* @param methodName 方法名称
* @param parameterType 调用方法的参数类型(支持多个参数) 如List.class
*/
public static void splitTraversalCall (List<?> collectionParameter, Integer step, Class<?>runObject, String methodName, Class<?>...parameterType){
int m=collectionParameter.size()/step;
for (int i = 0; i <=m; i++) {
int begin=i*step;
int end=(begin+step)<collectionParameter.size()?(begin+step):collectionParameter.size();
try {
//获取调用对象的指定方法
Method method = runObject.getMethod(methodName, parameterType);
//用方法进行反射操作此处 没用多参数传递
method.invoke(runObject.newInstance(),collectionParameter.subList(begin,end));
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试方法的可用性
public class UserService {
/**
* 模拟批量插入用户信息
* @param args
*/
public static void main(String[] args) {
//需要批量插入的集合
List<UserPojo> userPojoList=new ArrayList<>();
//模拟需要插入的10个用户, 我们需要
for (int i = 0; i < 10; i++) {
UserPojo user=new UserPojo();
user.setName("张"+i);
user.setAge(20+i);
user.setSex("男");
userPojoList.add(user);
}
//使用批量分割集合调用方法
ItmeiUtil.splitTraversalCall(userPojoList,4, UserDao.class,"getUserInfo",List.class);
}
}
可以看出已经批量打印出来,以后可以把持久层方法写成调用mybatis的批量插入方法
好像到这里你还会想这么麻烦为什么不直接调用对象的方法来的直接,就是因为我之前就是在每一个方法里面写最简单的遍历插入,太麻烦了按照之前说的一个类如果至少有三个批量我就要写好多这样重复的代码不美观又麻烦,所以才会想着怎么优化才写的这篇文章.
现在我们在调用同一个类不同的批量方法:getUserUser打印集合名称
/**
* 商品信息
*/
public class GoodsInfo {
public String goodName;
public Long barCode;
public Integer amount;
//get/set 和 toString 省略
}
之后肯定有需要把商品信息保存到库中,那意味着会有持久层,也可能有批量插入商品表,如果还要使用批量插入你是不是可以想到前面的批量分割反射调用方法呢!!!
创建一个GoodInfoDao类
public class GoodInfoDao {
public static void saveGoodInfoDao(List<GoodsInfo> goodInfos){
System.out.println("---------批量调用分界线--------");
goodInfos.forEach(goodInfo -> {
//将来这里肯定是调用一个对象然后通过mybatis方式批量插入数据库中
//打印每个用户信息
System.out.println(goodInfo.toString());
});
}
}
3.Spring中使用出现的问题
需要注意的是在Spring项目中不能使用前面写的工具类,着就是一个坑,遇到一个坑就要解决下个坑[手动滑稽],后面把写好的方法在spring项目使用会报错,因为我们使用这个runObject.newInstance(),由于spring会管理已经注册的对象,但是你使用newInstance相当于重新注册了一个对象,脱离了spring的管理,这样会导致反射方法里面使用spring对象会报空指针.
spring中场景还原
debug展示查看
报空指针异常
4.解决方法:
method.invoke
中传入的对象必须是Spring管理的对象
我们需要创建一个工具类来获取spring的IOC容器对象
工具类的名称随意都是要实现ApplicationContextAware
接口获取上下文,重写类的方法setApplicationContext
记得在这个工具类里面添加Component注解当作配置类注入spring中
重写一下方法,然后在里面创建一个方法传入容器名称,用来获取bean对象
/***
* 分割遍历调用
* (该方法以后用在一个集合需要拆分批量调用对应对象的方法进行批量插入数据库的操作)
* @param collectionParameter 集合参数
* @param step 集合拆分的步长
* @param runObject 调用的对象
* @param beanName spring注入容器的名称
* @param methodName 方法名称
* @param parameterType 调用方法的参数类型(支持多个参数) 如List.class
*/
public static void splitTraversalCallSpring (List<?> collectionParameter, Integer step, Class<?>runObject,String beanName, String methodName, Class<?>...parameterType){
if(collectionParameter.size()>0){
int m=collectionParameter.size()/step;
for (int i = 0; i <=m; i++) {
int begin=i*step;
int end=(begin+step)<collectionParameter.size()?(begin+step):collectionParameter.size();
try {
//获取调用对象的指定方法
Method method = runObject.getMethod(methodName, parameterType);
//用方法进行反射操作此处 没用多参数传递
/**
* 不使用这个runObject.newInstance()
* 由于spring会管理已经注册的对象,但是你使用newInstance相当于重新注册了一个对象,脱离了spring的管理,这样会导致反射方法里面使用spring对象会报空指针
* 解决这个问题,我们注入的反射对象必须是spring管理的对象这样就不会出现问题,通过SpringContextUtils.getBean方法获取注入spring容器的对象名称
* SpringContextUtils是我们创建的工具类实现了ApplictionAware接口
*/
Object bean = SpringContextUtils.getBean(beanName);
method.invoke(bean,collectionParameter.subList(begin,end)); //不是使用Spring管理对象用这个
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在测试方法
可以看出对象已经不是空的了
批量更新数据当然也不会有问题了
需要注意的是我是有定义bean 的名称所以传入获取这个对象也要是这个
如果不定义
bean的名称那么通常:bean的名称是类名称首字母小写如:taobaoServiceimpl
修改方法里面bean名称改成类首字母小写
如果你不相信,可以使用我们写SpringContextUtils
调用getApplicationContext()
方法获取上下文对象applicationContext
里面有个方法是获取bean名称的方法String[] getBeanNamesForType(Class<?> type)
返回与给定类型(包括子类)匹配的 bean 的名称我们传入Object.class
相当于获取所有bean名称
把打印出来的集合信息搜索一下看看有没有这个bean名称
这样spring项目也可以使用了唯一不同的就是需要传递容器的名称获取对象,大功告成!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/83857.html