springboot中 利用java反射调用Service,注入Dao接口为null

本文探讨了Java反射机制在项目中的应用,特别是在调用Service层时遇到的问题及解决策略。详细解析了如何避免反射创建对象导致的Spring依赖注入失败,并提供了一种通过Spring容器获取并调用Bean的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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("个性化表单数据插入失败");
            }
        }
<think>嗯,用户的问题是关于在Spring Boot项目中如何通过泛型自动处理DTO到BO,再到PO的转换。首先,我需要理解他们的需求。他们希望规范Controller层的输入,使用DTO,然后自动转换为业务逻辑层的BO,Service处理后再自动转为PO进行数据库操作。关键点在于如何利用泛型来统一这些转换,避免重复代码。 我应该先回忆一下Java中的泛型机制,以及Spring框架中的相关特性。比如,泛型可以用来创建通用的转换接口或类,这样不同的DTO、BO、PO都可以通过同一套逻辑处理转换。可能需要用到泛型接口和抽象类,结合Spring的Bean管理。 接下来,用户提到的自动转换,可能涉及对象属性拷贝。这里常用的工具有Spring的BeanUtils或者Apache的BeanUtils,但更推荐MapStruct,因为它在编译时生成代码,效率更高。不过,用户的问题重点在泛型的实现,所以可能需要将转换逻辑封装到泛型类中。 然后,我需要考虑如何设计这样的泛型转换器。或许可以定义一个泛型接口,比如Converter<D, B, P>,其中D是DTO,B是BO,P是PO。然后具体的转换器实现这个接口,提供具体的转换逻辑。但为了自动化,可能需要一个基础类或者工具类来处理这些转换。 另外,Service层和Controller层需要如何集成这个转换器呢?或许可以在基类Controller和Service使用泛型,声明转换器,然后在子类中指定具体的类型参数。这样,子类就可以直接调用父类的转换方法,无需重复编写转换代码。 可能还需要考虑泛型方法的使用,例如在基类Service中定义一个convertToPo方法,接收BO并返回PO,具体的转换由子类或通过工具类处理。或者使用抽象方法让子类实现具体的转换逻辑,但这可能不够自动化。 另外,用户可能希望转换过程是自动的,比如在Controller接收到DTO后,自动转换为BO,然后Service处理完自动转为PO。这时候可能需要AOP或者拦截器,但结合泛型的话,可能更倾向于在基类中封装转换步骤。 需要确保转换工具能够处理不同层之间的对象属性映射,比如属性名称和类型一致的情况下,可以使用BeanUtils.copyProperties,否则可能需要自定义映射。但用户的问题重点在泛型的结构设计,而不是具体的属性拷贝方式。 可能还需要考虑三层结构:Controller使用DTO,Service使用BO,DAO使用PO。所以,泛型转换器需要在这三层之间传递,每个层处理自己的转换。例如,Controller调用转换器将DTO转BO,Service处理业务逻辑后调用转换器将BO转PO,然后DAO保存PO。 总结一下,解决方案可能包括以下几个部分: 1. 定义泛型转换接口或抽象类,处理DTO到BO,BO到PO的转换。 2. 在Controller和Service的基类中使用泛型,声明这些转换器。 3. 使用工具类进行属性拷贝,结合泛型方法减少重复代码。 4. 具体的子类继承基类,并指定具体的DTO、BO、PO类型,实现必要的转换逻辑。 可能需要提供示例代码,说明如何定义这些泛型类和接口,以及如何在子类中使用。同时,要指出可能的扩展点,比如自定义转换逻辑,或者使用MapStruct等更高效的映射工具。 另外,要确保代码结构清晰,符合Spring Boot的规范,比如使用@Component注解让转换器被Spring管理,或者通过构造函数注入依赖。 可能还需要考虑异常处理,比如转换过程中出现的属性不匹配问题,但用户的问题可能更关注结构设计,所以这部分可以简要带过。 最后,需要验证这个方案是否真的能实现自动转换,减少重复代码。比如,每个Controller和Service子类只需要继承基类并指定泛型参数,就能自动获得转换方法,无需在每个类中编写转换代码。</think>以下是通过泛型实现分层对象自动转换的解决方案,分步骤说明: --- ### 一、定义泛型转换接口 ```java public interface EntityConverter<D, B, P> { B toBo(D dto); P toPo(B bo); D toDto(P po); //可选反向转换 } ``` --- ### 二、实现基础转换工具类 ```java public abstract class BaseConverter<D, B, P> implements EntityConverter<D, B, P> { @Override public B toBo(D dto) { return BeanUtils.convert(dto, getBoClass()); } @Override public P toPo(B bo) { return BeanUtils.convert(bo, getPoClass()); } // 通过反射获取泛型实际类型 protected abstract Class<B> getBoClass(); protected abstract Class<P> getPoClass(); } ``` --- ### 三、创建泛型基础Controller ```java public abstract class BaseController<D, B> { @Autowired private EntityConverter<D, B, ?> converter; protected B convertToBo(D dto) { return converter.toBo(dto); } } ``` --- ### 四、创建泛型基础Service ```java public abstract class BaseService<B, P> { @Autowired private EntityConverter<?, B, P> converter; protected P convertToPo(B bo) { return converter.toPo(bo); } // 数据库操作示例 @Transactional public void save(B bo) { P po = convertToPo(bo); repository.save(po); } } ``` --- ### 五、具体业务实现示例 #### 1. 定义具体转换器 ```java @Component public class UserConverter extends BaseConverter<UserDTO, UserBO, UserPO> { @Override protected Class<UserBO> getBoClass() { return UserBO.class; } @Override protected Class<UserPO> getPoClass() { return UserPO.class; } } ``` #### 2. 具体Controller实现 ```java @RestController @RequestMapping("/users") public class UserController extends BaseController<UserDTO, UserBO> { @PostMapping public ResponseEntity<?> create(@RequestBody UserDTO dto) { UserBO bo = convertToBo(dto); userService.save(bo); return ResponseEntity.ok().build(); } } ``` #### 3. 具体Service实现 ```java @Service public class UserService extends BaseService<UserBO, UserPO> { @Autowired private UserRepository repository; // 可添加具体业务逻辑 } ``` --- ### 六、转换工具BeanUtils实现 ```java public class BeanUtils { public static <T> T convert(Object source, Class<T> targetClass) { if (source == null) return null; T target = BeanUtils.instantiateClass(targetClass); BeanUtils.copyProperties(source, target); return target; } } ``` --- ### 关键设计点说明: 1. **泛型层级绑定**:通过泛型参数`<D, B, P>`明确各层对象类型关系 2. **模板方法模式**:BaseConverter强制子类实现类型获取方法 3. **Spring上下文集成**:使用`@Component`+`@Autowired`实现自动装配 4. **类型安全转换**:通过反射获取具体Class类型,避免强制类型转换 5. **可扩展性**:支持通过重写方法实现特殊字段转换逻辑 --- ### 优化建议: 1. 使用MapStruct替代反射实现(编译期生成转换代码) 2. 增加`ConverterFactory`管理多个转换器 3. 添加参数校验注解(如`@Valid`)与统一异常处理 4. 对集合类型转换进行支持(List/Page转换) 实际项目中建议结合Spring的`ConversionService`进行扩展,可参考JSR-303标准实现更健壮的参数校验体系。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值