1、反射簡介
反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;
對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及
動態調用對象的方法的功能稱為java語言的反射機制。
2、問題:
在項目中打算做一個通用的導出方法,但是這個方法是寫在一個普通的工具類中的,這個工具類中我們通過使用反射的方法去調用其他的service層,通過service層插入數據庫實體對象,但是serviceImpl中的dao接口對象卻為空。經過調查由於使用反射,導致dao注入失敗。原因是自動裝配是在 spring環境下當使用該類的實例時由spring容器完成了類的實例化過程,當然包括對依賴對象的實例化過程而通過反射創建實例時,是根據你調用的構造函數完成的實例化過程,沒有 容器的自動化創建實例了,所以需要自己對依賴對象進行注入。所以依賴spring容器實例化
和自己用反射實例化是兩種獨立的方式,不能相互滲透的。
3、代碼解析
a:原本我們使用反射調用service層錯誤的方式
Class<?> classType = Class.forName(serviceClass); Method m = classType.getDeclaredMethod("method名稱",new Class[]{parameters.class}); List<?> list = m.invoke(classType.newInstance(),parameters);
注意:這里我們就是使用classType.newInstance()方法才會使service中的dao注入失敗。
b:正確的方法,通過spring容器取得對象
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext(); Class<?> classType = Class.forName(serviceClass); Method m = classType.getDeclaredMethod("method名稱",new Class[]{parameters.class}); List<?> list = m.invoke(wac.getBean("service的id對象名稱"),parameters);
4、以上的代碼是別人spring的代碼,由於本人是用springboot,所以重新寫了下代碼。
(1)、工具類:SpringBootBeanUtil.java
import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; /** * SpringBoot 普通類獲取Spring容器中的bean工具類 * @author lvgang */ @Component public class SpringBootBeanUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringBootBeanUtil.applicationContext == null) { SpringBootBeanUtil.applicationContext = applicationContext; } System.out.println("========ApplicationContext配置成功========"); System.out.println("========在普通類可以通過調用SpringBootBeanUtil.getApplicationContext()獲取applicationContext對象========"); System.out.println("========applicationContext="+ SpringBootBeanUtil.applicationContext +"========"); } /** * 獲取applicationContext * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 通過name獲取 Bean. * @param name * @return */ public static Object getBean(String name) { return getApplicationContext().getBean(name); } /** * 通過class獲取Bean. * @param clazz * @return */ public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } /** * 通過name,以及Clazz返回指定的Bean * @param name * @param clazz * @return */ public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } }
(2)調用:forName中的為自己獲取並拼接的類地址
try { //從ApplicationContext中取出已創建好的的對象 //不可直接反射創建serviceimpi對象,因為反射創建出來的對象無法實例化dao接口 ApplicationContext applicationContext = SpringBootBeanUtil.getApplicationContext(); //反射創建serviceimpi實體對象,和實體類 Class<?> ServiceImplType = Class.forName(GlobalParams.REF_SERVICE+className+"ServiceImpl"); Class<?> entityType = Class.forName(GlobalParams.REF_ENTITY+className); //反射設置方法參數。 Method method = ServiceImplType.getDeclaredMethod("Insert",entityType); //在ApplicationContext中根據class取出已實例化的bean method.invoke(applicationContext.getBean(ServiceImplType),className); } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { e.printStackTrace(); return GlobalResult.resOk("個性化表單數據插入失敗"); } }