当前位置:首页 > 通信资讯 > 正文

spring的反射机制(spring反射调用bean)

目录
  • 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的方法

?
1 2 3 4 5 6 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容器取得对象

?
1 2 3 4 5 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类

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @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类

?
1 2 3 @Service public class ModuleService { }

1.3、编写TestKey注解

?
1 2 3 4 5 6 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TestKey { String key() default ""; }

1.4、编写TestAspectService

?
1 2 3 4 5 6 7 8 9 @Component public class TestAspectService { @Autowired private ModuleService moduleService; @TestKey(key = "key") public void sayHello() { System.out.println("************--->************" + moduleService); } }

1.5、编写TestAspect切面

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @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类

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @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类

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @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

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。