欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

静态代理&动态代理

程序员文章站 2022-06-10 20:49:21
...

静态代理

我们可以使用类的包装来实现静态代理

接口

public interface Flyable {
    void fly();
    String eat(String food);
}

实现类(目标类)

public class Bird implements Flyable {
	 
    @Override
    public void fly() {
        System.out.println("Bird is flying...");
        try {
            Thread.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

	@Override
	public String eat(String food) {
		// TODO Auto-generated method stub
		System.out.println("Bird is eating..."+food);
		return "eat返回值:吃饱了";
	}
}

静态代理类

public class BirdTimeProxy implements Flyable {//在fly方法前后执行一个计时功能
    private Flyable flyable;
    public BirdTimeProxy(Flyable flyable) {
        this.flyable = flyable;
    }
    @Override
    public void fly() {
        long start = System.currentTimeMillis();
        System.out.println("time-start:"+start);
        flyable.fly();
        long end = System.currentTimeMillis();
        System.out.println("time-over:"+end);
        System.out.println("Fly time = " + (end - start));
    }

	@Override
	public String eat(String food) {
		return flyable.eat(food);
	}
}

可以看出,这里使用了包装的方法

静态代理测试

	@Test
	public void fun(){
		Bird bird = new Bird();
        BirdTimeProxy p = new BirdTimeProxy(bird);
        p.fly();
        p.eat("苹果");
	}

结果:

time-start:1590666159624
Bird is flying...................
time-over:1590666160072
Fly time = 448
Bird is eating.................苹果

静态代理的缺点

1.接口中如果有很多个方法,那么这些方法都必须在包装类里面实现,会很麻烦
2.一个代理对象只能代理接口的一个类,如果上述flyable接口有其他的类,比如鸟、昆虫、飞机等等,就需要一个个地去为他们实现代理类,过于麻烦
3.如果接口要增加某个方法,那么不仅实现类要增加方法,相应的代理也要增加方法,就麻烦

动态代理

public static Object newProxyInstance(
ClassLoader loader,		Class<?>[] interfaces,		InvocationHandler h)

主要是使用这个方法来生成动态代理,
第一个参数是类加载器,
第二个参数是要目标类的接口(一个类不止实现一个接口),
第三个参数就是InvocationHandler 的实现类;
其中InvocationHandler 的实现类需要实现的函数就是public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
而这里的Object proxy表示的就是目标类,也就是被代理的类
Method method表示正在执行的方法
Object[] args)表示的是方法传进来的参数

实现一

	@Test
	public void fun4(){
		Flyable flyable=(Flyable) Proxy.newProxyInstance(demo.class.getClassLoader(), 
													Bird.class.getInterfaces(), 
												new InvocationHandler() {
			//这里生成了InvocationHandler接口的一个匿名类
			@Override
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				//proxy:在这里就没用到,按理说应该是method.invoke(proxy, args);但是下面对应的是new 类
				//method:代表正在执行的方法
				//args:代表正在执行的方法中的参数
				//Object:代表方法执行完毕之后的返回值
				System.out.println("正在invoke"+method.getName());
				//代表每个方法执行完毕之后返回对象
				Object obj=null;
				if(method.getName().equalsIgnoreCase("fly")){
					System.out.println("起飞前做一些准备");
					//打印args中的内容
					System.out.println("args:"+Arrays.toString(args));
					obj=method.invoke(new Bird(), args);//执行当前的方法
					System.out.println("起飞后做一些放松");
					
				}else{
					System.out.println("args:"+Arrays.toString(args));
					obj=method.invoke(new Bird(), args);//执行当前的方法	
				}
				return obj;
			}
		});
		flyable.fly();
		flyable.eat("苹果");
	}

测试结果

正在invoke eat
args:[青菜]
Bird is eating.................青菜
正在invoke fly
起飞前做一些准备
args:null
Bird is flying...................
起飞后做一些放松

缺点

可以看到,这样生成的代理,每次代理目标类的方法时,调用 method的public Object invoke(Object obj, Object... args)都要再new一个目标类,这显然不合理

改进思路

所以可以通过包装的方式,包装类实现InvocationHandler,将目标类作为包装类的一个私有变量,并且重写public Object invoke(Object proxy, Method method, Object[] args)方法,在这个方法内部调用method的invoke方法时,只要使用一下私有变量就可以啦,其他的和方式一一致。

方式二

public class MyInvocationHandler implements InvocationHandler{
	private Object target;
	public MyInvocationHandler() {
		super();
	}
	public MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {//method:代表正在执行的方法
		//args:代表正在执行的方法中的参数
		//Object:代表方法执行完毕之后的返回值
		System.out.println("正在invoke"+method.getName());
		//代表每个方法执行完毕之后返回对象
		Object obj=null;
		if(method.getName().equalsIgnoreCase("fly")){
			System.out.println("起飞前做一些准备");
			//打印args中的内容
			System.out.println("args:"+Arrays.toString(args));
			obj=method.invoke(target, args);//执行当前的方法
			System.out.println("起飞后做一些放松");
			
		}else{
			System.out.println("args:"+Arrays.toString(args));
			obj=method.invoke(target, args);//执行当前的方法	
		}
		return obj;
	}

}

测试

	@Test
	public void fun5(){
		Flyable target=new Bird();//target类
		InvocationHandler invocationHandler=new MyInvocationHandler(target);//改写的
		Flyable flyableProxy=(Flyable) Proxy.newProxyInstance(demo.class.getClassLoader(), Bird.class.getInterfaces(), invocationHandler);
		flyableProxy.eat("青菜");
		flyableProxy.fly();
	}