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
猜你喜欢
- 本文探讨使用C# StringBuilder 的最佳实践,用于减少内存分配,提高字符串操作的性能。在 .NET 中,String 对象是不可
- MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效
- 使用filter对response内容进行加密编写加密类(AES)/** * aes加密解密 */public class AesEncry
- 本文实例讲述了C#获取文件创建时间的方法。分享给大家供大家参考。具体如下:C#获取文件创建时间,主要用到了FileInfo的Creattio
- 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序、电影应用程序和体育相关的应用程序。您是否想知道如何将视频内容添加到您的
- 现在android的每一个项目都会需要设置为全屏,现在介绍两种设置为全屏的方式。一、在配置文件中设置android:theme=”@andr
- 通过拍照或相册中获取图片,并进行裁剪操作,然后把图片显示到ImageView上。 当然也可以上传到服务器(项目中绝大部分情况是上传
- 本文为大家分享了AForge实现C#摄像头视频录制功能的具体方法,供大家参考,具体内容如下1. 概述最近由于兴趣学习了下在C#上使用AFor
- 背景在工作中,遇到这样的场景:有个es索引构建服务,需要从各个业务服务获取索引的信息,从而构建索引,业务服务都实现同一个接口&mda
- 本文实例讲述了C#实现系统托盘通知的方法。分享给大家供大家参考。具体实现方法如下:namespace WindowsApplication1
- Java Json的各种处理一、net.sf.json1、Json转MapJSONObject jsonObject = JSONObjec
- 1.前言开发过程中,讲一个对象的属性和值赋值到另一个对象上,大量使用了get、set方法,看着很臃肿,思考下肯定不只有我有这种想法,所以技术
- 前言本文提供三种不同的解决方式,也是三种不同的情况和思路我的问题是在springboot整合了xxl-job一段时间后出现的。如果你程序里集
- 1、局部变量在方法或语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。局部变量声明在方法、构造方
- Oracle官网是这样介绍默认方法的,使用默认方法,可以达到往接口里面增加新的功能,而且保持与老版本代码兼容,也就是原来的实现类可以不需要被
- 前言真的一秒就可以实现么?是的,因为我们直接复制粘贴工具类拿来用就可以。 工具类 WaterMarkUtil.java&
- 1. 前言无论是自我学习中,还是在工作中,固然会遇到与前端搭配实现分页的功能,发现有几种方式,特此记录一下。2. 先说结论分页功能直接交给前
- 举例说明:1、有一个200*200像素的窗口,想要把它放在800*600像素的屏幕中间,屏幕的位置应是(800/2,600/2)=(400,
- 请求SpringBoot接受前台参数的六种方式,首先因为从前台发送的请求没有界面的话只能是从地址栏发送并且只能是Get请求,为了测试其他的请
- 最近项目中有截屏的需求,普通的view截屏方法网上一搜一大把,但是SurfaceView截屏黑屏问题很多文章说的并不清楚,自己参考了一些别的