代理,注解,接口和实现类的小测验

三世轮回 提交于 2020-08-14 05:06:27
* retention : 保留
* policy : 策略

ps : 简单测试了一下手写代理,jdk动态代理,和cglib动态代理,根据其不同的结果分析其原理

一:测试目的

  • 主要想看一下不同的代理模式对于目标类中方法上注解的读取能力

二:代理简述

  • jdk动态代理:只针对接口操作
  • cglib动态代理:既可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理

三:代码准备

1)自定义注解

  • 设置保留策略为运行期
  • 注解中只定义一个数组
  • 当属性名为value时,注解中可以省略不写
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
	String[] value();
}

2)接口

public interface Service {
	void eat();
}

3)实现类

  • 在eat()方法上添加自定义注解
public class ServiceImpl implements Service {
	@Override
	@MyAnno({"jim","tom"})
	public void eat() {
		System.out.println("eat ... ");
	}
}

三:测试实例

1)手写代理模式

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();
	}

}
  • 测试结果
jim
eat ... 
tom
  • 原理分析 : 这个应该比较容易理解,就是一个父类引用指向子类对象,调用方法的时候调用的是实现类的方法实现,我们调用的时候,是在代理对象的eat()方法中判断目标类的eat()方法上有没有我们的注解,能读到很正常

2)JDK动态代理

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();
		
	}

}
  • 运行结果
eat ... 
  • 原理分析 : jdk动态代理,它代理的是接口,然后根据反射技术进行实现,通过invoke()方法我们可以知道,我们是在调用代理实例的方法,接口上并没有我们的自定义注解,所以代理实例上也不会有注解
  • 如果在接口上加上注解,则可以读到

3)CGLIB动态代理

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();
	}

}
  • 运行结果
jim
eat ... 
tom
  • 原理分析 : cglib代理的目标对象是实现类对象,所以是可以读到的
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!