静态代理&动态代理

静态代理

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

接口
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();
	}
动态代理缺点

被代理的对象必须要实现一些借口,否则无法使用动态代理

cglib代理

spring的包里包含了cglib,就不用再单独导包了
cglib的好处就是可以对所有非final类进行代理,比动态代理少了些限制
首先要Enhancer en=new Enhancer();然后设置需要代理的目标类en.setSuperclass(Bird.class);接着要告诉en如何强化方法en.setCallback(callback);这里的callbackMethodInterceptor的实现类,下面的代码则是创建了以一个匿名类。
MethodInterceptor的实现类中,需要重写public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy)方法,注意,使用目标对象的原生方法时,要用methodProxy.invokeSuper(object, args);而不是methodProxy.invoke(object, args);(后者会栈溢出)

@Test
	public void fun6(){//cglib代理
		Enhancer en=new Enhancer();
		en.setSuperclass(Bird.class);
		en.setCallback(new MethodInterceptor() {
			@Override
			public Object intercept(Object object, Method method, Object[] args,
					MethodProxy methodProxy) throws Throwable {
				System.out.println("打开事务"+method.getName());
				Object returnValue=methodProxy.invokeSuper(object, args);
				System.out.println("提交事务");
				return returnValue;
			}
		});
		Flyable flyable=(Flyable) en.create();
		flyable.eat("pear");
		flyable.fly();
	}

结果

打开事务eat
Bird is eating.................pear
提交事务
打开事务fly
Bird is flying...................
提交事务
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值