静态代理模式
1.什么是代理模式:
- 代理模式是个给一个对象提供一个代理对象,并有代理对象控制对元对象的引用。通俗一点就是代理相当于我们所说的中介。
一般来说代理模式有三个对象: - 抽象对象: 代理角色和真实角色对外提供的公共方法,一般是一个接口
- 真实角色: 需要实现代理角色的接口,并且有真实角色自己的业务逻辑,以便工代理角色调用。
- 代理角色: 需要实现抽象角色的接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将同意流程都放到代理角色中处理! 而访问者不去访问真实角色,只用去访问代理角色。
代码验证:(一个果农,一个批发商,一个是小摊贩)
定义果农:
/**
* 果农的果园
* 只种了苹果
*/
public interface FruitAppleFactory {
/**
* 卖水果
* @param size 多少
*/
void wholesaleApple(String size);
}
/**
* 苹果的果农
*/
public class FruitApple implements FruitAppleFactory {
@Override
public void wholesaleApple(String size) {
System.out.println("售卖了"+size+"斤苹果");
}
}
/**
* 批发商
*/
public class MiddleAppleShop implements FruitAppleFactory {
/**
*我从买苹果的果农手里批发苹果
*/
FruitAppleFactory fruitApple;
public MiddleAppleShop(FruitAppleFactory fruitApple) {
this.fruitApple = fruitApple;
}
private void doAfter(){
System.out.println("精品包装了一下");
}
private void doBefore(){
System.out.println("市区免费送货上门");
}
@Override
public void wholesaleApple(String size) {
doAfter();
fruitApple.wholesaleApple(size);
doBefore();
}
}
最后一个小摊贩:
//这是苹果
FruitAppleFactory apple = new FruitApple();
MiddleAppleShop middleShop = new MiddleAppleShop(apple);
middleShop.wholesaleApple("100");
输出结果:
精品包装了一下
售卖了100斤苹果
市区免费送货上门
-------------------------
以上是静态代理模式的实现,静态代理模式的实现很简单,但是一对一的时候会出现静态代理对象数量过多,代码量大,从而导致复杂,可维护性差。比如我要在增加一个香蕉的,橘子的。。。等等。这样一对多的时候就出现了扩展性差的问题。
那么有没有其他方式能够解决呢?----动态代理模式就能来帮助我们了!可以在使用时创建代理类和实例
动态代理模式的优缺点我们先来看一下;
优点:
只需要一个动态代理类就可以解决对个静态代理的问题,避免重复,多余代码。
更强的灵活性。
*缺点:
动态代码的效率要略微第一点,毕竟我们在实现的时候用到了反射。相比静态直接调用对象方法,动态代理则先通过Java 的反射,来简介的调用对象的方法。
应用场景局限,因为Java 的单继承特性。只能针对接口创建代理类,不能针对类创建代理类.
在Java动态代理中有两个重要的类或者接口,一个是InvocationHandler
接口,一个是Proxy
类,这两个类和接口是我们实现动态代理必须的。
InvocationHandler
接口是给动态代理类实现的,负责处理被代理对象的操作的,Proxy
是用来创建动态代理类实例对象的,因为只有得到了这个我们才能调用那些需要代理的方法。
动态代理的就提实现:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理类
*/
public class MiddleShopProxy implements InvocationHandler {
private Object factory;
public Object getFactory() {
return factory;
}
public void setFactory(Object factory) {
this.factory = factory;
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), this);
}
private void doAfter() {
System.out.println("精品包装了一下");
}
private void doBefore() {
System.out.println("市区免费送货上门");
}
/**
*
* @param proxy 代理类的实例(对象)
* @param method 要调用的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doAfter();
Object invoke = method.invoke(factory, args);
doBefore();
return invoke;
}
}
/**
* 果农
* 只有香蕉
*/
public interface FruitBananaFactory {
void wholesaleBanana(String size);
}
/**
* 香蕉的果农
*/
public class FruitBanana implements FruitBananaFactory {
@Override
public void wholesaleBanana(String size) {
System.out.println("售卖了"+size+"斤香蕉");
}
}
调用方法
FruitBananaFactory fruitBananaFactory = new FruitBanana();
MiddleShopProxy middleShopProxy = new MiddleShopProxy();
middleShopProxy.setFactory(fruitBananaFactory);
FruitBananaFactory ban = (FruitBananaFactory) middleShopProxy.getProxyInstance();
ban.wholesaleBanana("100");
输出结果:
---------动态代理模式---------------
精品包装了一下
售卖了100斤香蕉
市区免费送货上门
简单分析一下代理模式的实现方式:
断点调试一下:
通过调试发现动态代理中,代理类的类名是
$Proxy0
这样的。这个名字为何是这样的呢?,通过查看Proxy.newProxyInstance
方法,可以知道和创建对象有关的代码主要有:
关键点就是getProxyClass0(loader,intfs);
这一句,如何获取代理类的对象?
我们点进去看具体的实现:
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
通过proxyClassCache
我们知道Jdk 使用了某种缓存机制缓存了我们 代理类的Class对象,同时get
方法接收的是被代理类的类加载器和类实现的接口。
同时我们在进入这个get 方法,
进入这个apply 方法:
可以看到有个ProxyClassFactory
工厂类 ,我们进入这个类中,寻找关键代码:
String proxyName = proxyPkg + proxyClassNamePrefix + num;
这句代码最后生成了代理类的名称也就是前面所说的$Proxy0
,至此可以明白代理类是如何生成的了。