Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/378.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用ByteBuddy代理现有对象_Java_Proxy_Aop_Byte Buddy - Fatal编程技术网

Java 如何使用ByteBuddy代理现有对象

Java 如何使用ByteBuddy代理现有对象,java,proxy,aop,byte-buddy,Java,Proxy,Aop,Byte Buddy,我想使用AOP自动向注释类添加一些功能 例如,假设有一个接口(StoredOnDatabase),其中包含一些有用的方法来从数据库读取和写入bean。假设有一些类(POJO)没有实现这个接口,并且用注释@Bean进行注释。当出现此注释时,我希望: 创建实现接口StoredOnDatabase的bean代理 为setter添加拦截器,当bean的属性被修改时,我可以使用它来“跟踪” 使用对所有这些bean都有效的泛型equals()和hashCode()方法 我不想改变POJO的类。一个简单的解决

我想使用AOP自动向注释类添加一些功能

例如,假设有一个接口(StoredOnDatabase),其中包含一些有用的方法来从数据库读取和写入bean。假设有一些类(POJO)没有实现这个接口,并且用注释@Bean进行注释。当出现此注释时,我希望:

  • 创建实现接口StoredOnDatabase的bean代理
  • 为setter添加拦截器,当bean的属性被修改时,我可以使用它来“跟踪”
  • 使用对所有这些bean都有效的泛型equals()和hashCode()方法
  • 我不想改变POJO的类。一个简单的解决方案是在实例化bean之前使用ByteBuddy完成所有这些工作。它可以是一个解决方案,但我想知道是否可以将bean实例化为一个干净的POJO,并使用代理添加其他功能

    我正在尝试使用ByteBuddy,我认为我有一个可行的解决方案,但它似乎比我预期的更复杂

    如上所述,我需要代理类的实例以向它们添加新接口,拦截对现有方法的调用并替换现有方法(主要是equals()、hashCode()和toString()

    下面的示例似乎与我需要的非常接近(复制自):

    我可以看到ByteBuddy生成的类正在拦截方法“hello”,并用Target中定义的静态方法替换其实现。 这有几个问题,其中之一是需要通过调用newInstance()实例化一个新对象。这不是我需要的:代理对象应该包装现有实例。我可以使用Spring+CGLIB或java代理来实现这一点,但它们还有其他限制(请参阅)

    我确信我可以使用上面例子中的解决方案来实现我所需要的,但似乎我最终会编写很多样板代码(见下面的答案)


    我遗漏了什么吗?

    我想出了以下解决方案。最后,它完成了我想要的一切,代码也比Spring AOP+CGLIB少(是的,有点神秘):

    import net.bytebuddy.ByteBuddy;
    import net.bytebuddy.description.modifier.Visibility;
    import net.bytebuddy.implementation.FieldAccessor;
    import net.bytebuddy.implementation.MethodDelegation;
    import net.bytebuddy.implementation.bind.annotation.Origin;
    import net.bytebuddy.implementation.bind.annotation.SuperCall;
    import net.bytebuddy.implementation.bind.annotation.This;
    import net.bytebuddy.matcher.ElementMatchers;
    import org.junit.Before;
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.lang.reflect.Method;
    
    import static org.junit.Assert.*;
    import static org.mockito.Mockito.*;
    
    public class ByteBuddyTest {
        private static final Logger logger = LoggerFactory.getLogger(ByteBuddyTest.class);
        private Logger mockedLogger;
    
        @Before
        public void setup() {
            mockedLogger = mock(Logger.class);
        }
    
        public interface ByteBuddyProxy {
            public Resource getTarget();
            public void setTarget(Resource target);
        }
    
        public class LoggerInterceptor {
            public void logger(@Origin Method method, @SuperCall Runnable zuper, @This ByteBuddyProxy self) {
                logger.debug("Method {}", method);
                logger.debug("Called on {} ", self.getTarget());
                mockedLogger.info("Called on {} ", self.getTarget());
    
                /* Proceed */
                zuper.run();
            }
        }
    
        public static class ResourceComparator {
            public static boolean equalBeans(Object that, @This ByteBuddyProxy self) {
                if (that == self) {
                    return true;
                }
                if (!(that instanceof ByteBuddyProxy)) {
                    return false;
                }
                Resource someBeanThis = (Resource)self;
                Resource someBeanThat = (Resource)that;
                logger.debug("someBeanThis: {}", someBeanThis.getId());
                logger.debug("someBeanThat: {}", someBeanThat.getId());
    
                return someBeanThis.getId().equals(someBeanThat.getId());
            }
        }
    
        public static class Resource {
            private String id;
    
            public String getId() {
                return id;
            }
    
            public void setId(String id) {
                this.id = id;
            }
        }
    
        @Test
        public void useTarget() throws IllegalAccessException, InstantiationException {
            Class<?> dynamicType = new ByteBuddy()
                    .subclass(Resource.class)
                    .defineField("target", Resource.class, Visibility.PRIVATE)
                    .method(ElementMatchers.any())
                    .intercept(MethodDelegation.to(new LoggerInterceptor())
                            .andThen(MethodDelegation.toField("target")))
                    .implement(ByteBuddyProxy.class)
                    .intercept(FieldAccessor.ofField("target"))
                    .method(ElementMatchers.named("equals"))
                    .intercept(MethodDelegation.to(ResourceComparator.class))
                    .make()
                    .load(getClass().getClassLoader())
                    .getLoaded();
    
            Resource someBean = new Resource();
            someBean.setId("id-000");
            ByteBuddyProxy someBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            someBeanProxied.setTarget(someBean);
    
            Resource sameBean = new Resource();
            sameBean.setId("id-000");
            ByteBuddyProxy sameBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            sameBeanProxied.setTarget(sameBean);
    
            Resource someOtherBean = new Resource();
            someOtherBean.setId("id-001");
            ByteBuddyProxy someOtherBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            someOtherBeanProxied.setTarget(someOtherBean);
    
            assertEquals("Target", someBean, someBeanProxied.getTarget());
            assertFalse("someBeanProxied is equal to sameBean", someBeanProxied.equals(sameBean));
            assertFalse("sameBean is equal to someBeanProxied", sameBean.equals(someBeanProxied));
            assertTrue("sameBeanProxied is not equal to someBeanProxied", someBeanProxied.equals(sameBeanProxied));
            assertFalse("someBeanProxied is equal to Some other bean", someBeanProxied.equals(someOtherBeanProxied));
            assertFalse("equals(null) returned true", someBeanProxied.equals(null));
    
            /* Reset counters */
            mockedLogger = mock(Logger.class);
            String id = ((Resource)someBeanProxied).getId();
            @SuppressWarnings("unused")
            String id2 = ((Resource)someBeanProxied).getId();
            @SuppressWarnings("unused")
            String id3 = ((Resource)someOtherBeanProxied).getId();
            assertEquals("Id", someBean.getId(), id);
            verify(mockedLogger, times(3)).info(any(String.class), any(Resource.class));
        }
    }
    
    导入net.bytebuddy.bytebuddy;
    导入net.bytebuddy.description.modifier.Visibility;
    导入net.bytebuddy.implementation.FieldAccessor;
    导入net.bytebuddy.implementation.MethodDelegation;
    导入net.bytebuddy.implementation.bind.annotation.Origin;
    导入net.bytebuddy.implementation.bind.annotation.SuperCall;
    导入net.bytebuddy.implementation.bind.annotation.This;
    导入net.bytebuddy.matcher.ElementMatchers;
    导入org.junit.Before;
    导入org.junit.Test;
    导入org.slf4j.Logger;
    导入org.slf4j.LoggerFactory;
    导入java.lang.reflect.Method;
    导入静态org.junit.Assert.*;
    导入静态org.mockito.mockito.*;
    公共类ByteBuddyTest{
    私有静态最终记录器Logger=LoggerFactory.getLogger(ByteBuddyTest.class);
    私人记录器模拟记录器;
    @以前
    公共作废设置(){
    mockedLogger=mock(Logger.class);
    }
    公共接口ByteBuddyProxy{
    公共资源getTarget();
    公共目标(资源目标);
    }
    公共类日志拦截器{
    公共void记录器(@Origin Method、@SuperCall Runnable zuper、@This ByteBuddyProxy self){
    debug(“方法{}”,方法);
    debug(“在{},self.getTarget()上调用);
    info(“调用{}”,self.getTarget());
    /*进行*/
    zuper.run();
    }
    }
    公共静态类ResourceComparator{
    公共静态布尔等式(对象,@This ByteBuddyProxy self){
    如果(那=self){
    返回true;
    }
    如果(!(ByteBuddyProxy的实例)){
    返回false;
    }
    资源somebeans=(资源)self;
    Resource someBeanThat=(Resource)那;
    debug(“somebeans:{}”,somebeans.getId());
    debug(“someBeanThat:{}”,someBeanThat.getId());
    返回somebeansh.getId().equals(somebeansh.getId());
    }
    }
    公共静态类资源{
    私有字符串id;
    公共字符串getId(){
    返回id;
    }
    公共无效集合id(字符串id){
    this.id=id;
    }
    }
    @试验
    public void useTarget()引发IllegaAccessException,实例化Exception{
    类dynamicType=newbytebuddy()
    .subclass(Resource.class)
    .defineField(“目标”、Resource.class、Visibility.PRIVATE)
    .method(ElementMatchers.any())
    .intercept(MethodDelegation.to)(新的LoggerInterceptor())
    第三种方法(MethodDelegation.toField(“目标”))
    .implement(ByteBuddyProxy.class)
    .截获(FieldAccessor.ofField(“目标”))
    .method(ElementMatchers.named(“equals”))
    .intercept(MethodDelegation.to(ResourceComparator.class))
    .make()
    .load(getClass().getClassLoader())
    .getLoaded();
    Resource someBean=新资源();
    setId(“id-000”);
    ByteBuddyProxy someBeanProxied=(ByteBuddyProxy)dynamicType.newInstance();
    setTarget(someBean);
    Resource sameBean=新资源();
    sameBean.setId(“id-000”);
    ByteBuddyProxy SamebeanProxy=(ByteBuddyProxy)dynamicType.newInstance();
    setTarget(sameBean);
    Resource someOtherBean=新资源();
    setId(“id-001”);
    ByteBuddyProxy someOtherBeanProxied=(ByteBuddyProxy)dynamicType.newInstance();
    setTarget(someOtherBean);
    assertEquals(“目标”、someBean、someBeanProxied.getT
    
    import net.bytebuddy.ByteBuddy;
    import net.bytebuddy.description.modifier.Visibility;
    import net.bytebuddy.implementation.FieldAccessor;
    import net.bytebuddy.implementation.MethodDelegation;
    import net.bytebuddy.implementation.bind.annotation.Origin;
    import net.bytebuddy.implementation.bind.annotation.SuperCall;
    import net.bytebuddy.implementation.bind.annotation.This;
    import net.bytebuddy.matcher.ElementMatchers;
    import org.junit.Before;
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.lang.reflect.Method;
    
    import static org.junit.Assert.*;
    import static org.mockito.Mockito.*;
    
    public class ByteBuddyTest {
        private static final Logger logger = LoggerFactory.getLogger(ByteBuddyTest.class);
        private Logger mockedLogger;
    
        @Before
        public void setup() {
            mockedLogger = mock(Logger.class);
        }
    
        public interface ByteBuddyProxy {
            public Resource getTarget();
            public void setTarget(Resource target);
        }
    
        public class LoggerInterceptor {
            public void logger(@Origin Method method, @SuperCall Runnable zuper, @This ByteBuddyProxy self) {
                logger.debug("Method {}", method);
                logger.debug("Called on {} ", self.getTarget());
                mockedLogger.info("Called on {} ", self.getTarget());
    
                /* Proceed */
                zuper.run();
            }
        }
    
        public static class ResourceComparator {
            public static boolean equalBeans(Object that, @This ByteBuddyProxy self) {
                if (that == self) {
                    return true;
                }
                if (!(that instanceof ByteBuddyProxy)) {
                    return false;
                }
                Resource someBeanThis = (Resource)self;
                Resource someBeanThat = (Resource)that;
                logger.debug("someBeanThis: {}", someBeanThis.getId());
                logger.debug("someBeanThat: {}", someBeanThat.getId());
    
                return someBeanThis.getId().equals(someBeanThat.getId());
            }
        }
    
        public static class Resource {
            private String id;
    
            public String getId() {
                return id;
            }
    
            public void setId(String id) {
                this.id = id;
            }
        }
    
        @Test
        public void useTarget() throws IllegalAccessException, InstantiationException {
            Class<?> dynamicType = new ByteBuddy()
                    .subclass(Resource.class)
                    .defineField("target", Resource.class, Visibility.PRIVATE)
                    .method(ElementMatchers.any())
                    .intercept(MethodDelegation.to(new LoggerInterceptor())
                            .andThen(MethodDelegation.toField("target")))
                    .implement(ByteBuddyProxy.class)
                    .intercept(FieldAccessor.ofField("target"))
                    .method(ElementMatchers.named("equals"))
                    .intercept(MethodDelegation.to(ResourceComparator.class))
                    .make()
                    .load(getClass().getClassLoader())
                    .getLoaded();
    
            Resource someBean = new Resource();
            someBean.setId("id-000");
            ByteBuddyProxy someBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            someBeanProxied.setTarget(someBean);
    
            Resource sameBean = new Resource();
            sameBean.setId("id-000");
            ByteBuddyProxy sameBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            sameBeanProxied.setTarget(sameBean);
    
            Resource someOtherBean = new Resource();
            someOtherBean.setId("id-001");
            ByteBuddyProxy someOtherBeanProxied = (ByteBuddyProxy)dynamicType.newInstance();
            someOtherBeanProxied.setTarget(someOtherBean);
    
            assertEquals("Target", someBean, someBeanProxied.getTarget());
            assertFalse("someBeanProxied is equal to sameBean", someBeanProxied.equals(sameBean));
            assertFalse("sameBean is equal to someBeanProxied", sameBean.equals(someBeanProxied));
            assertTrue("sameBeanProxied is not equal to someBeanProxied", someBeanProxied.equals(sameBeanProxied));
            assertFalse("someBeanProxied is equal to Some other bean", someBeanProxied.equals(someOtherBeanProxied));
            assertFalse("equals(null) returned true", someBeanProxied.equals(null));
    
            /* Reset counters */
            mockedLogger = mock(Logger.class);
            String id = ((Resource)someBeanProxied).getId();
            @SuppressWarnings("unused")
            String id2 = ((Resource)someBeanProxied).getId();
            @SuppressWarnings("unused")
            String id3 = ((Resource)someOtherBeanProxied).getId();
            assertEquals("Id", someBean.getId(), id);
            verify(mockedLogger, times(3)).info(any(String.class), any(Resource.class));
        }
    }