0

I'm learning proxy usage in java and want to log time of method's execution.

All of this components are placed in com.example.testproxyproject package.

I have Order interface which is implemented by OrderImpl class

Order:

@Component
public interface Order {
    String getName();
    String getVector();
    void setName(String name);
    void setVector(String vector);
}

OrderImpl only overrides those methods and don't add something new expect annotations

OrderImpl:

@Component
public class OrderImpl implements Order {
    private String name;
    private String vector;
    @Time
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getVector() {
        return vector;
    }
    @Time
    @Override
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void setVector(String vector) {
       this.vector = vector;
    }
}

Time annotation is quite simple

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Time {
}

For proxy I have custom invocation handler where I measure time of method execution which are marked with Time annotation

public class CustomInvokeHandler implements InvocationHandler {
    private Object object;
    private List<Method> methods;
    public CustomInvokeHandler(Object object,List<Method> methods){
        this.object = object;
        this.methods = methods;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long timeBeforeInvoking = System.nanoTime();
        Object result = method.invoke(object,args);
        if(methods.stream().anyMatch(listMethod->listMethod.getName().equals(method.getName()))) {
            System.out.println(System.nanoTime()-timeBeforeInvoking);
        }
        return result;
    }
}

I use it in beanPostProcessor class

CustomBeanProcessor:

@Component
public class CustomBeanProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        if (beanClass.getInterfaces().length == 0) return bean;
        return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), new CustomInvokeHandler(bean, getAnnotatedMethods(bean.getClass().getMethods(), Time.class)));
    }
    public <T extends Annotation>List<Method> getAnnotatedMethods(Method[] methods, Class<T> annotation){
        return Stream.of(methods).filter(method-> method.isAnnotationPresent(annotation)).collect(Collectors.toList());
    }
}

In Demo Application I test my custom beanpostprocessor

@Configuration
@ComponentScan("com.example.testproxyproject")
public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(DemoApplication.class);
        Order order = context.getBean(Order.class);
        order.setName("new Name");
        System.out.println(order.getName());
    }
}

Result: enter image description here

When I run It works as it should be, but If i change needed bean to OrderImpl class I will get this exception

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.testproxyproject.OrderImpl' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
    at com.example.testproxyproject.DemoApplication.main(DemoApplication.java:12)
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
DozezQuest
  • 179
  • 7

1 Answers1

1

This is precisely why you declare your dependencies as an interface instead of the implementation class: You replaced the original bean, which was OrderImpl, with a wrapped (proxied) version that is no longer an OrderImpl. The exact same thing happens with other kinds of advice.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152