Java动态代理

欢迎访问:我的个人网站

Java 动态代理

动态代理机制是指通过代理类,接口与具体的实现类并不直接产生关系,而是在运行期间产生关联。大致的流程如下:

  • 创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。
  • 创建一个代理类(wrapper)同时使其也实现这个接口。在代理类中持有一个被代理对象的引用。
  • 在代理类方法中调用该对象的方法。代理类方法里面可以添加一些新的功能。
    整个流程大致如下:
    OOOOOOOOOOOOOOOOOOOOO动态代理图示OOOOOOOOOOOOOOOOOOOOOOO

在Java中执行动态代理操作的类主要位于Java.lang.reflect包下,进一步的话,可以分为以下的两个类:

1.接口InvocationHandler

接口中仅仅定义了一个方法:

Object invoke(Object obj, Method method, Object[] args);

在使用该接口的时候,需要注意各个参数的含义,obj指代理类,method指被代理的方法,args则为被代理方法的参数数组。

2. 动态代理类Proxy

该类就是动态代理类,代理实现了InvocationHandler接口的代理类,主要有以下函数:

//构造一个代理类
protected Proxy(InvocationHandler handler);

//获得一个代理类,其中loader是类装载器,该装载器对代理类进行装载。
//interfaces是被代理类所需要实现的所有接口的集合,当传递之后,被代理类就宣称实现了所有给定的接口,也自然而然可以调用接口中的方法。
static Class getProxy(ClassLoader loader, Class[] interfaces)

//返回代理类的一个实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, Invocationhandler handler);

从非动态代理的例子说起

这里展示了一个非动态代理时的例子,循序渐进的过度到动态代理,也更容易接受,明白差异具体存在哪些地方。

场景:所有小动物都会吃,我们定义一个叫的接口,并找来两只具体的小动物去实现该接口叫的行为。然后我们打算对叫这个过程进行增强。

OOOOOOOOOOOOOOOD1OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

定义接口:

public interface Action {  
    void shout();  
}

定义具体实现类:

public class Dog implements Action{  
  @Override  
  public void shout() {  
        System.out.println("汪汪汪...");  
  }  
}

public class Cat implements Action {  
  @Override  
  public void shout() {  
        System.out.println("喵喵喵...");  
  }  
}

调用:

Dog dog = new Dog();  
dog.shout();  
Cat cat = new Cat();  
cat.shout();

输出:

汪汪汪...
喵喵喵...

很简单的一个流程,那么接下来,如果我们要考虑分别对这两只动物的“叫”方法进行增强,那么可以引入两个包装类, 分别对方法进行增强,那么上述的流程将转换为下面这样:

OOOOOOOOOOOOOOOOOD2OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

新增狗狗包装类, 猫猫包装类:

public class DogWrapper {  
 Dog dog;   
 public DogWrapper(Dog dog) {  
        this.dog = dog;  
 }  
 public void enhanceShout(){  
  System.out.println("小偷来啦!!!");  
  dog.shout();  
  System.out.println("小偷跑了...");  
 }  
}

public class CatWrapper {  
 Cat cat;  
 public CatWrapper(Cat cat){  
     this.cat = cat;  
 }  
 public void enhanceShout(){  
  System.out.println("主人来啦");  
  cat.shout();  
  System.out.println("主人走了...");  
 }  
}

调用:

DogWrapper dogWrapper = new DogWrapper(new Dog());  
CatWrapper catWrapper = new CatWrapper(new Cat());  
  
dogWrapper.enhanceShout();  
catWrapper.enhanceShout();

输出:

小偷来啦!!!
汪汪汪...
小偷跑了...
主人来啦
喵喵喵...
主人走了...

以上就是不使用动态代理时候对一个方法进行增强的过程,流程很简单,但是问题也是非常明显的,如果要为Action接口添加新的行为,那么所有实现了该接口的类都必须同步增加对方法的实现。如果增加5个,10个…缺点是很明显的。

虽然可以考虑将DogWrapper继承于Dog类而不再实现Action接口,但是很明显这样会严重限制DogWrapper的适用范围,虽然在本例中,确实是对每个具体的动物创建了一个具体的包装类以单独进行增强。但是实际上有时候会有一个单独的Wrapper对很多实现了某一接口的类进行通用性的增强,所以将Wrapper归结到某个具体类下,这种方式也还是行不通的。所以也就需要使用动态代理了…

转换为动态代理形式再次实现

如果使用动态代理去实现的话,那么 要包装/增强的类必须实现一个接口, 并且该接口里面定义了所有准备在包装器里面使用的方法。

1.首先建立一个代理类以进行对动物叫声的增强

此时就不用实现之前的Action接口转而实现接口:InvocationHandler。该代理类实现了InvocationHandler接口并在要实现的方法里面添加了增强,并且在实例化一的时候,要求指定被代理的对象。

public class ActionWrapper implements InvocationHandler{
	//被代理对象
	private Object proxyed;
	
	public ActionWrapper(Object obj){
		this.proxyed = obj;
	}

	@Override
	public Object invoke(Object obj, Method method, Object...params) throws Throwable{
		//前置增强内容
		System.out.println("饿了...");
		
		Object r = method.invoke(this.proxyed, args);
		
		//后置增强内容
		System.out.println("得到食物...");
		return r;
	}
}

2.通过Proxy类实例化一个动态代理对象

在建立完代理类之后,需要借助Proxy的newProxyInstance方法来实例化一个动态代理对象并执行后续的操作。流程如下:
需要注意的是,实例化的代理对象需要强转为 被代理对象所实现的接口类型,而非被代理对象的类型。

//建立被代理对象
Dog dog = new Dog();
//创建一个代理类  
ActionWrapper actionWrapper = new ActionWrapper(dog);  
/*
借助Proxy类来实例化一个代理类的对象
需要传递:
	1.被代理对象的类加载器。
	2.被代理对象所实现的所有接口集合。
	3.代理类。
转换为Action类型!
*/
Action action = (Action) Proxy.newProxyInstance(Dog.class.getClassLoader(), Dog.class.getInterfaces(), actionWrapper);  
//执行方法,此时执行的结果就是增强后的效果了
action.shout();

输出:

饿了...
汪汪汪...
得到食物...

上面实例化一个代理类对象的过程大致为:告诉Proxy类用一个指定的类加载器动态的创建一个对象。该对象实现了指定的接口,并提供了InvocationHandler来替代原来方法的主体。在ActionWrapper代理类里面没有任何对Action接口的引用。在实例化这个代理类的时候,指定了被代理的对象。而被代理对象中的所有方法都会由代理类的invoke方法接管。

可以尝试:在Action中新增一个方法并让实现类(Dog)进行实现,那么ActionWrapper仍旧可以拦截该方法并为其添加增强。同理对方法名进行更改也如此…

抽离为一个简单的AOP容器

动态代理的形式与Spring框架的AOP特性非常相像,实际上Spring的AOP底层实现就是基于上面说到的动态代理。利用这一点,可以很轻易对多个业务逻辑进行拦截添加额外的功能(安全,食物,日志,过滤…)

OOOOOOOOOOOOOOOOODDD55OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

这里简单的将上诉的动态代理抽离为一个容器:

class AOPContainer{  
    public static Object getBean(Object action){  
         ActionWrapper actionWrapper = new ActionWrapper(action);  
		 return Proxy.newProxyInstance(action.getClass().getClassLoader(), action.getClass().getInterfaces(), actionWrapper);  
  }  
}

后续所有的类在实现了一个接口的前提下,均可以使用该容器进行增强:

public interface A{
	void aa();
}

public class AA implements A{
	public void aa(){
		System.out.println("测试");
	}
}

public static void main(String...args){
	AA a = new AA();  
	A b = (A) AOPContainer.getBean(a);  
	b.aa();
}

输出:

饿了...
测试
得到食物...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值