raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.29k stars 807 forks source link

How to invoke the method of injecting objects into a class? #1696

Closed cilfm closed 3 months ago

cilfm commented 3 months ago

I want to implement the mybatis framework data query interface,I have create Mapper class and serviceImpl class as follows:

@Mapper
public interface SysCnDistrictMapper extends BaseMapper<BaseEntity> {
  @Select(databaseId = "", value = {"select * from sys_cn_district"})
  List<Map<String, Object>> apfDycapiDist(@Param("querys") Map<String, Object> paramMap);
}
@Service("")
@Transactional(timeout = -1, label = {}, propagation = Propagation.REQUIRED, noRollbackFor = {}, isolation = Isolation.DEFAULT, rollbackFor = {}, timeoutString = "", transactionManager = "", rollbackForClassName = {}, noRollbackForClassName = {}, value = "", readOnly = false)
public class SysCnDistrictServiceImpl extends ServiceImpl<SysCnDistrictMapper, BaseEntity> implements ISysCnDistrictService {
  public List<Map<String, Object>> apfDycapiDist(Map<String, Object> paramMap) {
    (SysCnDistrictMapper)getBaseMapper();
    return apfDycapiDist(paramMap);
    // Expected code not generated
  }
}

The code for creating the serviceImpl class is as follows:

private Class<?> createServiceImpl(ApfDynamicApiEntity apiEntity, Class<?> serviceClass, Class<?> mapperClass) {
    TableBeanInfo beanInfo = getTableBeanInfo(apiEntity.getPrimaryTable(), BeanType.ServiceImpl);
    List<AnnotationDescription> annotations = new ArrayList<>();
    annotations.add(AnnotationDescription.Builder.ofType(Service.class).build());
    annotations.add(AnnotationDescription.Builder.ofType(Transactional.class).build());

    Class<?> serviceImplClass = new ByteBuddy()
        .subclass(TypeDescription.Generic.Builder.parameterizedType(ServiceImpl.class, mapperClass, BaseEntity.class).build())
        .implement(serviceClass)
        .name(beanInfo.getClassName())
        .annotateType(annotations)
        .method(ElementMatchers.named(getMethodName(apiEntity.getApiPath())))
        .intercept(
            MethodCall.invoke(ElementMatchers.named("getBaseMapper"))
            .andThen(MethodCall.invoke(ElementMatchers.named(getMethodName(apiEntity.getApiPath()))).withAllArguments())
        )
        .make();
        .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
        .getLoaded();
    try {
        SpringUtil.registerBean(beanInfo.getBeanName(), serviceImplClass.newInstance());
    } catch (InstantiationException | IllegalAccessException e) {
        log.error("fail to register {}", beanInfo.toString(), e);
    }
    return serviceImplClass;
}

How to generate the following code?

@Service
@Transactional(timeout = -1, label = {}, propagation = Propagation.REQUIRED, noRollbackFor = {}, isolation = Isolation.DEFAULT, rollbackFor = {}, timeoutString = "", transactionManager = "", rollbackForClassName = {}, noRollbackForClassName = {}, value = "", readOnly = false)
public class SysCnDistrictServiceImpl extends ServiceImpl<SysCnDistrictMapper, BaseEntity> implements ISysCnDistrictService {
  public List<Map<String, Object>> apfDycapiDist(Map<String, Object> paramMap) {
       return baseMapper.apfDycapiDist(paramMap);
   // or
    // return  (SysCnDistrictMapper)getBaseMapper().apfDycapiDist(paramMap);
  }
}
raphw commented 3 months ago

Possibly you are looking for MethodDelegation? You would be able to supply any arguments and return any value, Byte Buddy then weaves it into the method.

cilfm commented 3 months ago

Thank you! I have resolved it.

MethodDelegation.toField("baseMapper")