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

 更新时间:2021年08月30日 09:40:16   作者:罗罗诺亚F  
这篇文章主要介绍了java 反射调用Service导致Spring注入Dao失效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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所有拥有的特性。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java使用位运算实现权限管理的示例详解

    Java使用位运算实现权限管理的示例详解

    在开发中,权限管理是一个非常常见的需求,本文将详细讲解如何使用 Java 中的 位运算 实现一个轻量级、高效的权限管理系统,并提供完整的代码示例和解释,感兴趣的小伙伴可以了解下
    2025-06-06
  • Springboot项目使用AOP与自定义注解记录请求日志方式

    Springboot项目使用AOP与自定义注解记录请求日志方式

    这篇文章主要介绍了Springboot项目使用AOP与自定义注解记录请求日志方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-06-06
  • Java时间轮算法的实现代码示例

    Java时间轮算法的实现代码示例

    本篇文章主要介绍了Java时间轮算法的实现代码示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java 前台加后台精品图书管理系统的实现

    Java 前台加后台精品图书管理系统的实现

    相信每一个学生学编程的时候,应该都会写一个小项目——图书管理系统。为什么这么说呢?我认为一个学校的氛围很大一部分可以从图书馆的氛围看出来,而图书管理系统这个不大不小的项目,接触的多,也比较熟悉,不会有陌生感,能够练手,又有些难度,所以我的小项目也来了
    2021-11-11
  • SpringBoot项目POM文件的使用小结

    SpringBoot项目POM文件的使用小结

    本文主要详细介绍了Maven中SpringBoot项目的POM文件配置,包括项目的依赖和插件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • java使用Socket实现SMTP协议发送邮件

    java使用Socket实现SMTP协议发送邮件

    这篇文章主要为大家详细介绍了java使用Socket实现SMTP协议发送邮件的相关资料,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • Java微服务架构中的关键技术和设计原则解读

    Java微服务架构中的关键技术和设计原则解读

    Java是一种面向对象的高级编程语言,具有跨平台兼容性、自动内存管理等特点,它支持多线程、异常处理,并拥有丰富的标准库和强大的社区生态,微服务架构是将应用分解为多个小型服务的设计风格
    2024-11-11
  • java使用UDP实现点对点通信

    java使用UDP实现点对点通信

    这篇文章主要为大家详细介绍了java使用UDP实现点对点通信,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • JavaPoet的使用指南小结

    JavaPoet的使用指南小结

    这篇文章主要介绍了JavaPoet的使用指南小结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • Spring Boot Security 结合 JWT 实现无状态的分布式API接口

    Spring Boot Security 结合 JWT 实现无状态的分布式API接口

    JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。这篇文章主要介绍了Spring Boot Security 结合 JWT 实现无状态的分布式API接口 ,需要的朋友可以参考下
    2019-04-04

最新评论