项目实战运用,利用反射拯救高耦合、低可用、不可读、超臃肿代码!

在新开的一个项目中,出现了一个不太符合常规的数据库设计,这样就导致了代码过于臃肿,且代码是面向过程编程。该封装会设计到反射、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(&quot;com.wfyl.system.service.&quot; + serviceName);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
//从拉取到的接口拉取方法、第一个参数为方法名称,第二个参数为参数类型模板
    Method method = c1.getDeclaredMethod(selectMethodName, bean.getClass());
//执行拉取到的接口、第一个参数是调用该方法的实例,如果该方法是静态方法,那么可以用null或者用类来代替,第二个参数是变长的,是调用该方法的参数。
    List&lt;T&gt; orders = (List&lt;T&gt;) method.invoke(bean1, bean);
//执行方法返回的参数,业务相关了。
    for (int i = 0; i &lt; orders.size(); i++) {
        Long orderId = 0L;
        Date stopTime = new Date();
        Class&lt;?&gt; aClass3 = orders.get(i).getClass();
        Field[] declaredFields1 = aClass3.getDeclaredFields();
        for (Field field : declaredFields1) {
            field.setAccessible(true);
            String name = field.getName();
            if (&quot;checkOrderId&quot;.equals(name)) {
	    //获取值,当字段名称为:checkOrderId的时候,获取他里面的值
                orderId = (Long) field.get(orders.get(i));
            }
            if (&quot;stopTime&quot;.equals(name)) {
                stopTime = (Date) field.get(orders.get(i));
            }
        }
        List&lt;CopyToAdvice&gt; 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 (&quot;status&quot;.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 -&gt; !filterList.contains(item)).collect(Collectors.toList());
    List&lt;T&gt; collect = new ArrayList&lt;&gt;();
    for (T order : orders) {
        Long orderType = 0L;
        Class&lt;?&gt; aClass2 = order.getClass();
        Field[] declaredFields1 = aClass2.getDeclaredFields();
        for (Field field : declaredFields1) {
            field.setAccessible(true);
            String name = field.getName();
            if (&quot;orderType&quot;.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(&quot;method&quot;, 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(&quot;Instance s1: &quot; + cl1.toString());
    System.out.println(&quot;Instance s2: &quot; + cl2.toString());
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值