java 反射調(diào)用Service導(dǎo)致Spring注入Dao失效的解決方案
java 反射調(diào)用Service導(dǎo)致Spring注入Dao失效
問題發(fā)生背景:
原本打算做一個(gè)xml配置文件,寫一個(gè)公用類然后根據(jù)讀取配置反射動(dòng)態(tài)調(diào)用方法。執(zhí)行過程中,發(fā)現(xiàn)service中的dao為null,經(jīng)過調(diào)查由于使用反射,導(dǎo)致dao注入失敗。
1、錯(cuò)誤方法:通過反射執(zhí)行service的方法
String serviceClass = templateInfo.getService();//service執(zhí)行類的名稱
String method = templateInfo.getMethod();//調(diào)用方法名
//根據(jù)反射執(zhí)行保存操作
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方法第一個(gè)參數(shù)不能使用newInstance方法,否則Service中dao的注入失敗,dao為null
反射調(diào)用導(dǎo)致Spring特性失效
今天在項(xiàng)目中遇到一個(gè)由于Java反射調(diào)用Bean方法而導(dǎo)致Spring特性失效的問題,折騰了半天,現(xiàn)給出解決方案。
1、拋出問題
我要在控制器的某個(gè)方法中通過反射調(diào)用一個(gè)service的方法,但是這個(gè)方法已經(jīng)被納入切面同時(shí)該方法也依賴于其他通過Spring自動(dòng)注入的Bean實(shí)例,準(zhǔn)備代碼如下:
1.1、編寫TestAspectController類
@RestController
public class TestAspectController {
@GetMapping("/testAspect")
public Object testAspect() throws NoSuchMethodException {
try {
//通過完整類名反射加載類
Class cla = Class.forName("com.icypt.learn.service.TestAspectService");
//取得類實(shí)例
Object obj = cla.newInstance();
//通過實(shí)例反射調(diào)用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*********");
}
}
運(yùn)行結(jié)果:
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
根據(jù)結(jié)果可以發(fā)現(xiàn),切面沒有被執(zhí)行,同時(shí)依賴注入的Bean也沒有獲得實(shí)例,其實(shí)原因很簡單,就是因?yàn)槲覀兪鞘謩?dòng)通過反射獲得的Bean的實(shí)例,這種方式相當(dāng)于我們new Bean(),此Bean的實(shí)例已完全脫離Spring容器,所以Spirng無法感知它的存在,那么如何解決呢?
2、解決問題
2.1、編寫SpringContextUtil類
@Component
public class SpringContextUtil implements ApplicationContextAware {
// Spring應(yīng)用上下文環(huán)境
private static ApplicationContext applicationContext;
/**
* 實(shí)現(xiàn)ApplicationContextAware接口的回調(diào)方法,設(shè)置上下文環(huán)境
*
* @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);
}
}
此類的作用就是手動(dòng)通過BeanId獲取Bean實(shí)例。
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實(shí)例
Object obj = SpringContextUtil.getBean(firstLowerName, cla);
//通過實(shí)例反射調(diào)用sayHello方法
obj.getClass().getDeclaredMethod("sayHello").invoke(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return "ok";
}
}
其他類保持不變,運(yùn)行結(jié)果如下:
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*********
通過結(jié)果可以發(fā)現(xiàn),注入的Bean已經(jīng)獲得了實(shí)例同時(shí)切面也友好的執(zhí)行,問題完美解決。解決問題核心思想就是我們通過Spring的反射機(jī)制獲得Bean的實(shí)例化對象,而后通過Java的反射機(jī)制攜帶該實(shí)例對象去處理業(yè)務(wù),這樣就不會(huì)使Bean脫離Spring容器管理,當(dāng)然也可以享有Spring的Bean所有擁有的特性。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot集成定時(shí)器和多線程異步處理操作
這篇文章主要介紹了Springboot集成定時(shí)器和多線程異步處理操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
java循環(huán)練習(xí)的簡單代碼實(shí)例
本篇文章介紹了,java中循環(huán)練習(xí)的一些簡單代碼實(shí)例。需要的朋友參考下2013-04-04
Spring Cloud Hystrix實(shí)現(xiàn)服務(wù)容錯(cuò)的方法
Hystrix是SpringCloud中重要的熔斷保護(hù)組件,由Netflix開源,主要提供延遲和容錯(cuò)管理,以保障分布式系統(tǒng)的高可用性和魯棒性,通過封裝依賴項(xiàng)實(shí)現(xiàn)服務(wù)間隔離,引入回退邏輯應(yīng)對依賴服務(wù)故障,有效防止系統(tǒng)崩潰和服務(wù)級聯(lián)故障2024-10-10
java設(shè)計(jì)簡單學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)簡單學(xué)生管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09

