在新开的一个项目中,出现了一个不太符合常规的数据库设计,这样就导致了代码过于臃肿,且代码是面向过程编程。该封装会设计到反射、ApplicationContextAware设置spring上下文,获取spring管理的接口等一系列知识,废话少说,直接上代码。
private <T> HashMap<T, Integer> test(T bean, String serviceName, String selectMethodName, String updateMethodName, CopyToAdviceMapper copyToAdviceMapper, Long areaId) throws IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException { //获取bean模板 Class<?> aClass = bean.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { //设置字段可访问 declaredField.setAccessible(true); //获取字段名 String name = declaredField.getName(); if ("idFunc".equals(name)) { //向该字段赋值、第一个参数是该对象的模板,第二个参数为值 declaredField.set(bean, areaId); } if ("status".equals(name)) { declaredField.set(bean, 1L); } } List<T> filterList = new ArrayList<>(); //使用ApplicationContextRegister.getApplicationContext()获取Spring上下文拉取已注入的接口。 Object bean1 = ApplicationContextRegister.getApplicationContext().getBean(serviceName);
Class c1 = null; try { //拉取接口类模板 c1 = Class.forName("com.wfyl.system.service." + serviceName); } catch (ClassNotFoundException e) { e.printStackTrace(); } //从拉取到的接口拉取方法、第一个参数为方法名称,第二个参数为参数类型模板 Method method = c1.getDeclaredMethod(selectMethodName, bean.getClass()); //执行拉取到的接口、第一个参数是调用该方法的实例,如果该方法是静态方法,那么可以用null或者用类来代替,第二个参数是变长的,是调用该方法的参数。 List<T> orders = (List<T>) method.invoke(bean1, bean); //执行方法返回的参数,业务相关了。 for (int i = 0; i < orders.size(); i++) { Long orderId = 0L; Date stopTime = new Date(); Class<?> aClass3 = orders.get(i).getClass(); Field[] declaredFields1 = aClass3.getDeclaredFields(); for (Field field : declaredFields1) { field.setAccessible(true); String name = field.getName(); if ("checkOrderId".equals(name)) { //获取值,当字段名称为:checkOrderId的时候,获取他里面的值 orderId = (Long) field.get(orders.get(i)); } if ("stopTime".equals(name)) { stopTime = (Date) field.get(orders.get(i)); } } List<CopyToAdvice> advices = copyToAdviceMapper.selectCopyToAdviceByDayNow(orderId, 0); if (advices.size() != 0) { filterList.add(orders.get(i)); } if (CopyToAdviceUtils.isNow(stopTime)) { Field[] declaredFields4 = aClass3.getDeclaredFields(); for (Field field : declaredFields4) { field.setAccessible(true); String name = field.getName(); if ("status".equals(name)) { //设置值,和前面一样 field.set(orders.get(i), 2L); } } //获取方法,执行方法。和前面一样 Method method1 = c1.getDeclaredMethod(updateMethodName, orders.get(i).getClass()); method1.invoke(orders.get(i)); } } //stream流做过滤操作 orders = orders.stream().filter(item -> !filterList.contains(item)).collect(Collectors.toList()); List<T> collect = new ArrayList<>(); for (T order : orders) { Long orderType = 0L; Class<?> aClass2 = order.getClass(); Field[] declaredFields1 = aClass2.getDeclaredFields(); for (Field field : declaredFields1) { field.setAccessible(true); String name = field.getName(); if ("orderType".equals(name)) { orderType = (Long) field.get(order); } } if (orderType == 1) { collect.add(order); } } return CopyToAdviceUtils.copyToAdvice(collect); }
以上就是我自己写好的一串封装,注释都有些到代码里。如果还有什么简化写法,也可以留言交流哦。
下面的代码是有用过的使用ApplicationContextAware.getApplicationContext获取Spring上下文
package com.wfyl.system.spring;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@Lazy(false)
public class ApplicationContextRegister implements ApplicationContextAware {
private static ApplicationContext APPLICATION_CONTEXT;
/**
* 设置spring上下文 * * @param applicationContext spring上下文 * @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
APPLICATION_CONTEXT = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
}
在获取spring管理的service接口时,有个坑须得说明一下
//WebApplicationContext中可以获得ServletContext的引用,以便web应用可以访问spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法来获得WebApplicationContext对象
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
//反射获取到对应java
Class c1 = Class.forName(packageName);
//获取方法名称,methodName为service层方法
Method method = c1.getDeclaredMethod(methodName,String.class);
//获取注入的service类型,serviceName值为:xxServceiImpl,
//在通过invoke方法执行对应的方法
method.invoke(wac.getBean(serviceName),params);
使用WebApplicationContext访问spring上下文时,会获取到null。
网上有说使用:spring-boot:run的方式来启动项目,而不使用main的方式,这样是不可行的。
springboot无论以main方法还是spring-boot:run的方式执行都不会跑SpringBootServletInitializer中的onStartup导致ContextLoaderListener没有执行。
考虑到以往的经验ContextLoaderListener一般是配置在web.xml中的对web容器有依赖,所以我直接把工程打成war放到tomcat跑果然可以调用SpringBootServletInitializer中的onStartup,但是还是不能获取ContextLoader.getCurrentWebApplicationContext(),原因是在onStartup初始化ContextLoader时使用的是构造函数,没有用ContextLoader.initWebApplicationContext方式,所以获取不到。
要用这种方式得重写SpringBootServletInitializer中的onStartup
所以推荐使用ApplicationContextAware的方式获取ApplicationContext,这样对非web及web环境都有很好的支持
虽然上面有具体的实现,但在业务里,大家不好理解
下面是一些反射的用法示例:
- Method.invoke:
package com.tn.class;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Client {
public static void main(String[] args) throws Exception {
Class<User> clas=User.class;
Method m=clas.getMethod("method", String[].class);
m.invoke(null, new Object[]{new String[]{"aa","bb","cc"}});//静态方法可省略对象,直接用null替代,或用clas
m=clas.getDeclaredMethod("method", int[].class);//非public方法要用declared获取
m.setAccessible(true);//非public方法需要设置为可访问
m.invoke(clas.newInstance(), new int[]{1,2,3,4,3,2,1});//非静态方法需要提供底层的类对象
}
}
class User{
public static void method(String…strings){
System.out.println(Arrays.toString(strings));
}
private void method(int...ints){
System.out.println(Arrays.toString(ints));
}
}
- Class.newInstance()
public class NewInstanceOfClass { public static void main(String[] args) throws Exception { // Create and Return String class StringBuilder s1 = new StringBuilder(); Class cl1 = s1.getClass();
// We are creating a new instance of the // class denoted by this object cl1 // by using newInstance() method Object s2 = cl1.newInstance(); Class cl2 = s2.getClass(); // Display Instance System.out.println("Instance s1: " + cl1.toString()); System.out.println("Instance s2: " + cl2.toString()); }