$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 重命名为它们应该命名的。
除了 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 重命名为它们应该命名的。