$Proxy293 无法使用 JDK 动态代理转换为 Spring AOP 中的 ...service.impl.ext.MyService

$Proxy293 cannot be cast to ...service.impl.ext.MyService in Spring AOP using JDK Dynamic Proxy

除了 Spring AOP 和 Spring-Boot 之外,我还尝试使用 JDK 动态代理来切入 JHipster 为我提供的方法,这样我就可以 mask/block一个页面的数据全部来自后台。几个问题。为什么我的代理没有正确转换,我如何获得由 PageImpl(运行时类型)表示的 class?如果Page是我的接口,PageImpl是我的底层具体实现,如何获取PageImpl的类型?感谢您的未来投入。这是代码:

首先,代理实现。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyInterceptor implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyInterceptor.class);
    
    private final Object target;

    public MyInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T target, Class interfaceType) {
        return (T) Proxy.newProxyInstance(
                interfaceType.getClassLoader(),
            new Class[] { interfaceType },
            new MyInterceptor(target));
    }
}

现在,我的方面。

public class MyAspect {

    private final Logger LOGGER = LoggerFactory.getLogger(MyAspect.class);

    @Autowired
    private MyService myService; 
    
    private final Environment env;

    public MyAspect(Environment env) {
        this.env = env;
    }
    
    @AfterReturning(value = "execution(* com.some.package.service..findAll(*))", returning="page")
    private void entityResourceFindAll(JoinPoint joinPoint, Object page) {
        LOGGER.debug("entityResourceFindaAll");

        // The concrete impl is PageIml<T>. How can I get the type
        // that PageIml represents, such as MyAttachment?

        Page<?> entityPage = (Page<?>)page;
        LOGGER.debug("simple name: {}", entityPage.getClass().getSimpleName());
        LOGGER.debug("joinPoint typeName {}", joinPoint.getSignature().getDeclaringTypeName());
        MyService proxy = MyInterceptor.getProxy(myService, MyServiceImpl.class); 

        // error shows here: .$Proxy293 cannot be cast to ...service.impl.ext.MyService

        LOGGER.debug("proxy getClass: {}", proxy.getClass());
        LOGGER.debug("proxy typeName: {}", proxy.getClass().getTypeName());
        proxy.determineMaskOrBlockPaginate(entityPage);
        
    }
}

然后,MyService

@Service
public class MyService<T> implements MyServiceImpl{

    private final Logger LOGGER = LoggerFactory.getLogger(MyService.class);

    public void determineMaskOrBlockPaginate(Page<?> page) {
        /*
         * 
         * Get user authorization in determining to mask/block or both
         * come to think of it, I don't think you can do both 
         * block wipes data from UI
         */
        
        if(blocked) {
            testYourAPIBlockPaginate(page);
        }else if(masked) {
            testYourAPIBlockPaginate(page);
        }
    }
}

然后,接口代表我的对象(MyService)上的代理。

import java.util.List;
import java.util.Optional;

import org.springframework.data.domain.Page;

public interface MyServiceImpl {
    
    public List<Object> testYourAPIMaskPaginate(Page<?> page);
    public List<Object> testYourAPIBlockPaginate(Page<?> page);
    public void testYourAPIBlockPaginate(Page<?> page);
}

实体的服务实现与 JHipster 提供的方法。我正在获取 Page< SiteAttachments >,稍后将成为 Page< T >。

@Service
@Transactional
public class SiteAttachmentsServiceImpl implements SiteAttachmentsService {

    private final Logger log = LoggerFactory.getLogger(SiteAttachmentsServiceImpl.class);

    private final SiteAttachmentsRepository siteAttachmentsRepository;

    public SiteAttachmentsServiceImpl(SiteAttachmentsRepository siteAttachmentsRepository) {
        this.siteAttachmentsRepository = siteAttachmentsRepository;
    }

    /**
     * Get all the siteAttachments.
     *
     * @param pageable the pagination information.
     * @return the list of entities.
     */
    @Override
    @Transactional(readOnly = true)
    public Page<SiteAttachments> findAll(Pageable pageable) {
        log.debug("Request to get all SiteAttachments");
        return siteAttachmentsRepository.findAll(pageable);
    }
}

最后,我的资源class还是前端门

@RestController
@RequestMapping("/api")
public class SiteAttachmentsResource {

    private final Logger log = LoggerFactory.getLogger(SiteAttachmentsResource.class);

    @Value("${jhipster.clientApp.name}")
    private String applicationName;

    private final SiteAttachmentsService siteAttachmentsService;
    
    @Autowired
    private MyService<?> myService;

    public SiteAttachmentsResource(SiteAttachmentsService siteAttachmentsService) {
        this.siteAttachmentsService = siteAttachmentsService;
    }

    /**
     * {@code GET  /site-attachments} : get all the siteAttachments.
     * @param pageable the pagination information.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of siteAttachments in body.
     */
    @GetMapping("/site-attachments")
    public ResponseEntity<List<SiteAttachments>> getAllSiteAttachments(Pageable pageable) {
        log.debug("REST request to get a page of SiteAttachments");

         // I am intercepting here (getting Page<SiteAttachments>)
         // to then call the below commented out code (later overwritten by JHipster)
         // that will be called in the entityResourceFindAll() in MyAspect.

        Page<SiteAttachments> page = siteAttachmentsService.findAll(pageable); 

//        List<Object> maskedSiteAttachmentList = gsamService.testYourAPIMaskPaginate(page);      
//        List<Object> blockedSiteAttachmentsList = gsamService.testYourAPIBlockPaginate(page);    
//        page = new PageImpl(blockedSiteAttachmentsList);

        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
        log.debug("getAllSiteAttachments------------------------------------------------");
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }
}

您的代码有几处奇怪的地方,例如:

  • 为什么你调用你的接口 MyServiceImpl 就好像它是实现一样,然后调用实际的实现 MyService?这是可怕的命名。不应该反过来吗?

  • 您的接口无法编译,因为两个 testYourAPIBlockPaginate 方法具有相同的类型擦除。在将代码发布到这里之前,您没有尝试编译代码吗?

  • 你使用泛型的方式也比较奇怪,但不是问题的根本原因,所以我就不详细评论了。

您还在自己的代码中混淆了接口和实现,因为您方面的这一行:

MyService proxy = MyInterceptor.getProxy(myService, MyServiceImpl.class);

实际上应该是(根据你颠倒的 class 命名方案):

MyServiceImpl proxy = MyInterceptor.getProxy(myService, MyServiceImpl.class);

这是因为 MyServiceImpl 是您示例中接口类型的名称。

让我们重命名这两个 classes 以反映他们实际在做什么,好吗?然后这个例子有效(没有 Spring,没有 AOP,只是专注于你的问题):

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;

class Scratch {
  public static void main(String[] args) {
    MyServiceImpl myService = new MyServiceImpl();
    MyService proxy = MyInterceptor.getProxy(myService, MyService.class);
  }

  public static class MyInterceptor implements InvocationHandler {
    private final Object target;

    public MyInterceptor(Object target) {
      this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      return method.invoke(target, args);
    }

    @SuppressWarnings("unchecked")
    public static <T> T getProxy(T target, Class interfaceType) {
      return (T) Proxy.newProxyInstance(
        interfaceType.getClassLoader(),
        new Class[] { interfaceType },
        new MyInterceptor(target)
      );
    }
  }

  public interface MyService {
    List<Object> doSomething(String text);

    List<Object> doSomethingElse(String text);
  }

  public static class MyServiceImpl<T> implements MyService {
    @Override
    public List<Object> doSomething(String text) {
      return Arrays.asList("doSomething", text);
    }

    @Override
    public List<Object> doSomethingElse(String text) {
      return Arrays.asList("doSomethingElse", text);
    }
  }

}

为了重现您的错误,您必须将 MyService proxy 更改为 MyServiceImpl proxy。这正是你的问题,它只是看起来相反,因为我将 classes 重命名为它们应该命名的。