当前位置:首页 > Java技术 > Java反射调用导致Spring依赖注入和切面不能正常使用

Java反射调用导致Spring依赖注入和切面不能正常使用

2022年11月09日 23:14:38Java技术10

今天在项目中遇到一个由于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/cgj_dreaming/article/details/89736819

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/69174.html

分享给朋友:

“Java反射调用导致Spring依赖注入和切面不能正常使用” 的相关文章

SpringBoot整合hibernate纯注解版

SpringBoot整合hibernate纯注解版

一、hibernate是什么 hibernate是一款优秀的ORM(Object Relational Mapping ,对象关系映射)框架,是一种面向对象编程的框架,它对JDBC进行了封装,是一个全自动的ORM框架,可以自动生成SQL语句,也可以自定义HQL进行执行脚本。 优点:hib...

前后端分离,SpringBoot如何实现验证码操作

前后端分离,SpringBoot如何实现验证码操作

验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的。SpringBoot版本本文基于的Spring Boot的版本是2.6.7 。引入依赖captcha一款超简单的验证码生成,还挺好玩的.还有中文验证码,动态验证码...

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

SpringBoot整合MybatisPlus基本的增删改查,保姆级教程

概述MybatisPlus是国产的第三方插件, 它封装了许多常用的CURDapi,免去了我们写mapper.xml的重复劳动,这里介绍了基本的整合SpringBoot和基础用法。引入依赖在项目中pom文件引入mybatisplus和mysql驱动依赖,如下图   &nb...

Spring Cloud面试问题

Spring Cloud面试问题

问:什么是Spring Cloud?     答: Spring Cloud Stream App Starters是基于Spring Boot的Spring Integration应用程序,提供与外部系统的集成。Spring Cloud Task。...

深入理解 Java 并发锁

深入理解 Java 并发锁

📦 本文以及示例源码已归档在 javacore 一、并发锁简介 确保线程安全最常见的做法是利用锁机制(Lock、sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的...

JDBC连接时所犯错误1.字符集设置不合适2.连接MySQL8.0社区版时时区不一致3..包名不能以Java.命名4.驱动被弃用

Microsoft JDBC Driver 的主页为:https://msdn.microsoft.com/en-us/data/aa937724.aspx 下载所需驱动 今天连接时报了四次错,记录下来 1.java.sql.SQLException:...

JAVA的JDK环境变量的配置JAVA

JAVA的JDK环境变量的配置JAVA

首先要在官网下载java 官网:http://www.oracle.com/technetwork/java/javase/downloads/ 到这个界面 选择我接受 记住该地址 最好的办法新建记事本,然后按ctrl+s保存 java环境变量的...

JAVA UUID 生成唯一标识

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket Reprint it anywhere u want 需求     项目在设计表的时候,要处理并发多...

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程: * JDK 1.8 * Maven 3.5.2 * Spring Boot 2.1.3.R...

Java回顾之Spring基础

Java回顾之Spring基础

这是针对Java进行回顾的一系列文章,这篇主要是和Spring基础相关。   第一篇:Java回顾之I/O   第二篇:Java回顾之网络通信   第三篇:Java回顾之多线程   第四篇:Java回顾之多线程同步   第五篇:Java回顾之集...

发表评论

访客

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