java 反射调用Service导致Spring注入Dao失效的解决方案

作者:罗罗诺亚F 时间:2021-06-30 15:47:58 

目录
  • java 反射调用Service导致Spring注入Dao失效

    • 问题发生背景:

    • 1、错误方法:通过反射执行service的方法

    • 2、解决方法:通过获取Spring容器取得对象

  • 反射调用导致Spring特性失效

    • 1、抛出问题

      • 1.1、编写TestAspectController类

      • 1.2、编写ModuleService类

      • 1.3、编写TestKey注解

      • 1.4、编写TestAspectService

      • 1.5、编写TestAspect切面

    • 2、解决问题

      • 2.1、编写SpringContextUtil类

      • 2.2、修改TestAspectController类

java 反射调用Service导致Spring注入Dao失效

问题发生背景:

原本打算做一个xml配置文件,写一个公用类然后根据读取配置反射动态调用方法。执行过程中,发现service中的dao为null,经过调查由于使用反射,导致dao注入失败。

1、错误方法:通过反射执行service的方法


String serviceClass = templateInfo.getService();//service执行类的名称
String method = templateInfo.getMethod();//调用方法名
//根据反射执行保存操作
Class<?> classType = Class.forName(serviceClass);
Method m = classType.getDeclaredMethod(method,new Class[]{PageData.class});
m.invoke(classType.newInstance(),pd);

2、解决方法:通过获取Spring容器取得对象


WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
DivStattisTabService service = (DivStattisTabService)
Class<?>  cls = wac.getBean("divstattistabService").getClass();
Method m = classType.getDeclaredMethod(method,new Class[]{PageData.class});
m.invoke(wac.getBean("divstattistabService"),pd);

注:m.invoke方法第一个参数不能使用newInstance方法,否则Service中dao的注入失败,dao为null

反射调用导致Spring特性失效

今天在项目中遇到一个由于Java反射调用Bean方法而导致Spring特性失效的问题,折腾了半天,现给出解决方案。

1、抛出问题

我要在控制器的某个方法中通过反射调用一个service的方法,但是这个方法已经被纳入切面同时该方法也依赖于其他通过Spring自动注入的Bean实例,准备代码如下:

1.1、编写TestAspectController类


@RestController
public class TestAspectController {
   @GetMapping("/testAspect")
   public Object testAspect() throws NoSuchMethodException {
       try {
           //通过完整类名反射加载类
           Class cla = Class.forName("com.icypt.learn.service.TestAspectService");
           //取得类实例
           Object obj = cla.newInstance();
           //通过实例反射调用sayHello方法
           obj.getClass().getDeclaredMethod("sayHello").invoke(obj);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }
       return "ok";
   }
}

1.2、编写ModuleService类


@Service
public class ModuleService {
}

1.3、编写TestKey注解


@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestKey {
   String key() default "";
}

1.4、编写TestAspectService


@Component
public class TestAspectService {
   @Autowired
   private ModuleService moduleService;
   @TestKey(key = "key")
   public void sayHello() {
       System.out.println("************--->************" + moduleService);
   }
}

1.5、编写TestAspect切面


@Aspect
@Component
public class TestAspect {
   @Pointcut("@annotation(com.icypt.learn.aspect.TestKey)")
   public void process() {
   }
   @Before("process()")
   public void boBefore() {
       System.out.println("********before*********");
   }
   @After("process()")
   public void doAfter() {
       System.out.println("********after*********");
   }
}

运行结果:

2019-03-28 21:57:26.548 INFO 30348 --- [nio-8082-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-03-28 21:57:26.548 INFO 30348
--- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2019-03-28 21:57:26.587 INFO 30348
--- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 39 ms
************--->************null

根据结果可以发现,切面没有被执行,同时依赖注入的Bean也没有获得实例,其实原因很简单,就是因为我们是手动通过反射获得的Bean的实例,这种方式相当于我们new Bean(),此Bean的实例已完全脱离Spring容器,所以Spirng无法感知它的存在,那么如何解决呢?

2、解决问题

2.1、编写SpringContextUtil类


@Component
public class SpringContextUtil implements ApplicationContextAware {
     // Spring应用上下文环境  
   private static ApplicationContext applicationContext;
     /**
    * 实现ApplicationContextAware接口的回调方法,设置上下文环境
    *  
    * @param applicationContext
    */  
   public void setApplicationContext(ApplicationContext applicationContext) {  
       SpringContextUtil.applicationContext = applicationContext;  
   }  
     /**
    * @return ApplicationContext
    */  
   public static ApplicationContext getApplicationContext() {  
       return applicationContext;  
   }  
     /**
    * 获取对象
    *  
    * @param name
    * @return Object
    * @throws BeansException
    */  
   public static Object getBean(String name) throws BeansException {
       return applicationContext.getBean(name);  
   }
   public static Object getBean(String name, Class cla) throws BeansException {
       return applicationContext.getBean(name, cla);
   }
}

此类的作用就是手动通过BeanId获取Bean实例。

2.2、修改TestAspectController类


@RestController
public class TestAspectController {
   @GetMapping("/testAspect")
   public Object testAspect() throws NoSuchMethodException {
       try {
           //通过完整类名反射加载类
           Class cla = Class.forName("com.icypt.learn.service.TestAspectService");
           //获取首字母小写类名
           String simpleName = cla.getSimpleName();
           String firstLowerName = simpleName.substring(0,1).toLowerCase()
+ simpleName.substring(1);
           //通过此方法去Spring容器中获取Bean实例
           Object obj = SpringContextUtil.getBean(firstLowerName, cla);
           //通过实例反射调用sayHello方法
           obj.getClass().getDeclaredMethod("sayHello").invoke(obj);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }
       return "ok";
   }
}

其他类保持不变,运行结果如下:

2019-03-28 22:13:59.311 INFO 37252 --- [nio-8082-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'
2019-03-28 22:13:59.312 INFO 37252
--- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started 2019-03-28 22:13:59.350 INFO 37252
--- [nio-8082-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 38 ms
********before*********
************--->************com.icypt.learn.service.ModuleService@5681f667
********after*********

通过结果可以发现,注入的Bean已经获得了实例同时切面也友好的执行,问题完美解决。解决问题核心思想就是我们通过Spring的反射机制获得Bean的实例化对象,而后通过Java的反射机制携带该实例对象去处理业务,这样就不会使Bean脱离Spring容器管理,当然也可以享有Spring的Bean所有拥有的特性。

来源:https://blog.csdn.net/Tracycater/article/details/50778662

标签:java,Service,Spring,注入Dao
0
投稿

猜你喜欢

  • 深入理解Java中的final关键字_动力节点Java学院整理

    2022-05-05 23:18:24
  • Java非侵入式API接口文档工具apigcc用法详解

    2023-11-24 10:01:00
  • java 生成有序账号的实现方法

    2023-08-12 03:28:01
  • Android 如何使用log4j及注意事项

    2023-06-01 07:47:24
  • C# string格式的日期时间字符串转为DateTime类型的方法

    2022-04-07 09:34:30
  • java按钮控件数组实现计算器界面示例分享

    2021-09-12 22:37:59
  • 使用Feign调用注解组件(实现字段赋值功能)

    2023-01-06 15:19:59
  • IDEA 2022 中的Lombok 使用基础教程

    2023-04-09 21:57:09
  • Spring工作原理简单探索

    2023-04-26 10:30:56
  • Java CAS操作与Unsafe类详解

    2023-06-15 10:06:49
  • java源码解析之String类的compareTo(String otherString)方法

    2023-11-11 23:10:00
  • 深入了解Java中的反射机制(reflect)

    2022-06-22 06:34:18
  • Android登录时密码保护功能

    2023-10-24 07:09:40
  • Android 根据手势顶部View自动展示与隐藏效果

    2022-01-11 07:30:20
  • C#编程中常见数据结构的比较(Unity3D游戏开发)

    2022-01-02 06:58:03
  • Java实现简单台球游戏

    2022-06-28 23:55:59
  • 浅析Java编程中枚举类型的定义与使用

    2021-07-04 23:46:16
  • java异常处理执行顺序详解try catch finally

    2022-10-01 04:10:10
  • Android开发之自定义加载动画详解

    2023-07-27 01:41:05
  • 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法(推荐)

    2022-11-09 11:40:37
  • asp之家 软件编程 m.aspxhome.com