Quiz on proxy, annotation, interface and implementation classes

* retention : hold
* policy : strategy

ps: simply test the handwriting agent, jdk dynamic agent, and cglib dynamic agent, and analyze their principles according to their different results

1: Test purpose

  • I want to take a look at the ability of different proxy patterns to read annotations on Methods in the target class

2: Agent Brief

  • jdk dynamic proxy: only for interface operations
  • cglib dynamic proxy: you can proxy for classes that do not implement interfaces or classes that implement interfaces

3: Code preparation

1) Custom annotation

  • Set retention policy to run time
  • Only one array is defined in the annotation
  • When the attribute name is value, it can be omitted from the annotation
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
	String[] value();
}

2) Interface

public interface Service {
	void eat();
}

3) Implementation class

  • Add a custom annotation on the eat() method
public class ServiceImpl implements Service {
	@Override
	@MyAnno({"jim","tom"})
	public void eat() {
		System.out.println("eat ... ");
	}
}

3: Test case

1) Handwriting proxy mode

public class AnnoTest implements Service{

	@MyAnno({ "jim", "tom" })
	public void test() {
		System.out.println("test...");
	}

	private Service target;

	public AnnoTest(Service target) {
		this.target = target;
	}
	
	@Override
	public void eat() {

		try {
			Class clazz = target.getClass();
			Method method = clazz.getMethod("eat");
			if(method.isAnnotationPresent(MyAnno.class)) {
				MyAnno myAnno = method.getAnnotation(MyAnno.class);
				System.out.println(myAnno.value()[0]);
				target.eat();
				System.out.println(myAnno.value()[1]);
			}else {
				target.eat();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest annoTest = new AnnoTest(service);
		annoTest.eat();
	}

}
  • test result
jim
eat ... 
tom
  • Principle analysis: this should be easy to understand. A parent class reference points to a child class object. When calling a method, we call the method implementation of the implementation class. When we call it, we judge whether there are our annotations on the eat() method of the target class in the eat() method of the proxy object. It is normal to read them

2)JDK dynamic proxy

public class AnnoTest2 implements InvocationHandler{

	private Service target;

	public AnnoTest2(Service target) {
		this.target = target;
	}
	
	public Object createProxy() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object ret = null;
		if(method.isAnnotationPresent(MyAnno.class)) {
			MyAnno anno = method.getAnnotation(MyAnno.class);
			System.out.println(anno.value()[0]);
			ret = method.invoke(target, args);
			System.out.println(anno.value()[1]);
		}else {
			ret = method.invoke(target, args);
		}
		return ret;
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest2 annoTest = new AnnoTest2(service);
		Service proxy = (Service) annoTest.createProxy();
		proxy.eat();
		
	}

}
  • Running results
eat ... 
  • Principle analysis: jdk dynamic proxy, which proxy the interface, is implemented according to reflection technology. We can know from the invoke() method that we are calling the method of the proxy instance. There is no custom annotation on the interface, so there will be no annotation on the proxy instance
  • If you annotate the interface, you can read

3)CGLIB dynamic proxy

public class AnnoTest3 implements MethodInterceptor {

	private Object target;

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

	public Object createProxy() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}

	@Override
	public Object intercept(Object object, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		Object ret = null;
		if(method.isAnnotationPresent(MyAnno.class)) {
			MyAnno anno = method.getAnnotation(MyAnno.class);
			String[] val = anno.value();
			System.out.println(val[0]);
			ret = method.invoke(target, args);
			System.out.println(val[1]);
		}else {
			ret = method.invoke(target, args);
		}
		return ret;
	}

	public static void main(String[] args) throws Exception {
		Service service = new ServiceImpl();
		AnnoTest3 annoTest = new AnnoTest3(service);
		Service proxy = (Service) annoTest.createProxy();
		proxy.eat();
	}

}
  • Running results
jim
eat ... 
tom
  • Principle analysis: the target object of cglib proxy is the implementation class object, so it can be read

Tags: Java Design Pattern JDK cglib

Posted by slobodnium on Mon, 30 May 2022 13:11:54 +0530