那Java怎么跟代理勾搭上?代理能帮我们做什么?有哪些实践?
代理其实在程序语言中来源于设计模式:代理模式------使用代理对象完成用户请求,屏蔽了用户对真实对象的访问。就像租房子:房东拥有房屋,对房子有控制权,可以将使用权转让。我作为租客需要验证房东是不是有房权证啊 具不具备出租资格啊。验证完了之后要定租金 签订合同啊。房东只是想把房子出租出去,不想关注怎么验证,怎么拟合同,太麻烦了,也不专业。这时候就需要代理出面了。
房东:我要出租房子!
中介:我要验证信息 签订合同 定期收房租!
我:租房子!
各司其职,单一职责 整个世界清晰了。 让我们来实现吧……
接口:可以出租房子的人
public interface Owner {
/**
* 将房屋出租给租客
* @param tenantName
* @return
*/
Boolean rentHouse(String tenantName);
/**
* 返回房东姓名
* @return
*/
String findHouseOwnerName();
}
房东实现了Owner接口:
public class HouseOwner implements Owner {
private String ownerName;
public HouseOwner(String ownerName) {
this.ownerName = ownerName;
}
public Boolean rentHouse(String tenantName) {
//房东只愿意把房子租给叫vincent的人 就是我拉
if (StringUtils.equalsIgnoreCase("vincent", tenantName)) {
System.out.println(StringUtils.join(ownerName + "愿意将房子出租给" + tenantName));
return Boolean.TRUE;
} else {
System.out.println(StringUtils.join(ownerName + "拒绝将房子出租给" + tenantName));
}
return Boolean.FALSE;
}
public String findHouseOwnerName() {
return ownerName;
}
}
中介:中介也实现了Owner接口 但他只是代理
public class HouseOwnerProxy implements Owner {
private Owner houseOwner;
public HouseOwnerProxy(Owner houseOwner) {
this.houseOwner = houseOwner;
}
public Boolean rentHouse(String tenantName) {
System.out.println("第一步:检查房东房屋的合法性");
Boolean isSuccessRent = houseOwner.rentHouse(tenantName);
if (isSuccessRent) {
System.out.println(StringUtils.join("第二步:与", tenantName, "签订租房合同"));
System.out.println("最后一步:将信息同步给房东" + houseOwner.findHouseOwnerName() + ",完成分成!!!");
} else {
System.out.println("房东拒绝出租,看下一家……");
}
return isSuccessRent;
}
public String findHouseOwnerName() {
return houseOwner.findHouseOwnerName();
}
}
租房场景:
public class ProxyTestCenter {
public static void main(String[] args) {
//房东
HouseOwner houseOwner = new HouseOwner("包租婆");
//中介
HouseOwnerProxy proxy = new HouseOwnerProxy(houseOwner);
//中介出租房屋给vincent
proxy.rentHouse("vincent");
}
}
运行结果:
整个流程就是这样呢 。虽说是中介出租房屋给我 其实真正的幕后是房东在处理。
上面就是【静态代理】。
HouseOwnerProxy也像HouseOwner一样实现了Owner接口,但实际调用的是HouseOwner的实现。它在调用的前后加上了其他的功能 :检查房屋的合法性 签订合同 房租等…… 比较装逼的叫法是:无入侵的类代理!!
刚才是代理类为我们处理了额外的事情:检查合法性,制定合同等;房东只关心他自己的部分。我们可以联想代理可以为我们做什么?sample:假如我们需要监控每个业务service方法的处理时间以实现对慢处理的优化【类似DB慢查询log】,该怎么做?
—我们在方法开始记录一个时间 在方法处理完后记录时间 两个时间相减即可。
这样我们需要对我们2000多个service差不多60000个方法都加上这样的代码,这样监控的代码和业务实现的代码耦合在一起,像一坨屎一样!我们要分开,所以我们写了2000个代理类60000个代理方法,这样分开了!找了个实习生终于写完了,,第二天code review的时候 实习生背了锅 被开了。架构师对我说 :“vincent 你负责把他的静态代理改成动态的。” 我答应了,尽管我并不知道什么叫“动态代理”。
为了能够逆袭追 . 上女神,一代码农vincent开始熬夜查资料,终于研究明白了。他打算在下一个分享日给大家分享----JDK动态代理。
“动态代理” ------- 动态地生成代理类! 让我们忘了那个一个一个方法手写代理类的实习生吧,动态生成吧。vincent改了代码,他了解到JDK内置的动态代理核心就是两个类(接口)。InvocationHandler 和 Proxy! InvocationHandler翻译过来叫调用处理器,是一个接口,有一个需要实现的方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy就是动态代理类,我们用动态代理来实现业务吧:
public interface Owner {
Boolean rentHouse(String tenantName) throws InterruptedException;
void cleanHouse() throws InterruptedException;
void decorateHouse() throws InterruptedException;
void sleepWithGirls() throws InterruptedException;
}
public class HouseOwner implements Owner {
private String ownerName;
public HouseOwner(String ownerName) {
this.ownerName = ownerName;
}
public Boolean rentHouse(String tenantName) throws InterruptedException {
TimeUnit.SECONDS.sleep(RandomUtils.nextInt(5));
return Boolean.TRUE;
}
public void cleanHouse() throws InterruptedException {
TimeUnit.SECONDS.sleep(RandomUtils.nextInt(5));
}
public void decorateHouse() throws InterruptedException {
TimeUnit.SECONDS.sleep(RandomUtils.nextInt(5));
}
public void sleepWithGirls() throws InterruptedException {
TimeUnit.SECONDS.sleep(RandomUtils.nextInt(5));
}
public String findHouseOwnerName() {
return ownerName;
}
}
注意下面的代理类,实现了InvocationHandler接口,不再一一实现每一个方法了!!!
public class HouseOwnerProxy implements InvocationHandler {
private Owner houseOwner;
public HouseOwnerProxy(Owner houseOwner) {
this.houseOwner = houseOwner;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = method.invoke(houseOwner, args);
long endTime = System.currentTimeMillis();
System.out.println(StringUtils.join( method.getName() + "方法一共消耗了:", endTime - startTime) + " 毫秒");
return result;
}
public Owner getOwnerProxy(){
return (Owner) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
houseOwner.getClass().getInterfaces(),this);
}
}
下面是具体场景:
public class ProxyTestCenter {
public static void main(String[] args) throws InterruptedException {
//房东
HouseOwner houseOwner = new HouseOwner("包租婆");
//中介
HouseOwnerProxy proxy = new HouseOwnerProxy(houseOwner);
//生产代理类
Owner owner = proxy.getOwnerProxy();
owner.cleanHouse();
owner.decorateHouse();
owner.rentHouse("vincent");
owner.sleepWithGirls();
}
}
中介通过InvocationHandler代理了【每一个方法】,运行结果:
这样我们就动态地实现代理,之所以叫“动态”,是getOwnerProxy这里:
(Owner) Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
houseOwner.getClass().getInterfaces(),
this)
创建了具体的实现,让我们分析下这个方法的参数,第一个是ClassLoader,第二个是被代理类实现的接口们 ,第三个是代理类。我们猜想,jvm在运行的时候是【动态创建了具有那些接口属性,并加上代理实现】的类,然后调用代理类的 cleanHouse, decorateHouse的方法。
静态代理从代码层面很容易理解,而动态代理总让人觉得有点悬乎,到底发生了什么?为什么按照这样的方式就能代理每一个方法?没办法,看源码喽……
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler就是一个接口而已,看来就是一颗棋子,没什么料。剩下的就是Proxy类了,由Proxy.newProxyInstance开始吧:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
//获取接口类
final Class<?>[] intfs = interfaces.clone();
//安全检查 跳过
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
//重点就在这了 如何获取的Class?????
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
//以下可以看作是通过反射 生成对象
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
由上面单拿出来:Class<?> cl = getProxyClass0(loader, intfs);
进去:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
//看着应该是自己做的map cache
return proxyClassCache.get(loader, interfaces);
}
显然proxyClassCache.get 进去:
proxyClassCache.get(loader, interfaces);
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
不再赘述,找到Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
进去(看源码重要的不是看什么 而是不看什么!):
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//掉过 直奔主题
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
由上面进入:ProxyGenerator.generateProxyClass( proxyName, interfaces)
//看名字是多么的直白啊
public static byte[] generateProxyClass(final String var0, Class[] var1) {
ProxyGenerator var2 = new ProxyGenerator(var0, var1);
//通过var2来创建类的字节码 核心实现 有很多的字节操作细节
final byte[] var3 = var2.generateClassFile();
// saveGeneratedFiles参数是new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue(); 如果为true 创建class到文件系统
if(saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if(var1 > 0) {
Path var3x = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]);
Files.createDirectories(var3x, new FileAttribute[0]);
var2 = var3x.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class", new String[0]);
}
Files.write(var2, var3, new OpenOption[0]);
return null;
} catch (IOException var4) {
throw new InternalError("I/O exception saving generated file: " + var4);
}
}
});
}
return var3;
}
generateProxyClass还能更明显么?终于找到你。我们通过sun.misc.ProxyGenerator.saveGeneratedFiles能找到class文件,OK ,我就想找到动态生成的class文件看一眼,通过代码很难分析到底生成到那个路径了,只能debug了,我就debug刚才的main方法,万万没想到:
默认的值是false。也就是生成的class不落盘,那咋办?刚才我们知道:
saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
哈. 一个参数而已,我们自己重新设置,所以有了:
public class FuckProxyGenerator {
private static final String SAVEGENERATEDFLAG = "sun.misc.ProxyGenerator.saveGeneratedFiles";
public static void generatorClass() throws IOException {
Boolean systemParam = Boolean.valueOf(System.getProperty(SAVEGENERATEDFLAG));
if (Boolean.FALSE == systemParam) {
System.setProperty(SAVEGENERATEDFLAG, Boolean.TRUE.toString());
}
systemParam = Boolean.valueOf(System.getProperty(SAVEGENERATEDFLAG));
byte[] byteArray = ProxyGenerator.generateProxyClass("ProxyClass", HouseOwner.class.getInterfaces());
FileUtils.writeByteArrayToFile(new File("/Users/vincent/Documents/tempFiles/TargetHouseOwnerProxy.class"), byteArray);
}
public static void main(String[] args) throws IOException {
generatorClass();
}
}
反编译TargetHouseOwnerProxy.class
import com.starbucks.demo.proxy.Owner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxyClass extends Proxy
implements Owner
{
private static Method m3;
private static Method m1;
private static Method m6;
private static Method m4;
private static Method m0;
private static Method m5;
private static Method m2;
public ProxyClass(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final Boolean rentHouse(String paramString)
throws InterruptedException
{
try
{
return (Boolean)this.h.invoke(this, m3, new Object[] { paramString });
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sleepWithGirls()
throws InterruptedException
{
try
{
this.h.invoke(this, m6, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void cleanHouse()
throws InterruptedException
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void decorateHouse()
throws InterruptedException
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m3 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("rentHouse", new Class[] { Class.forName("java.lang.String") });
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m6 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("sleepWithGirls", new Class[0]);
m4 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("cleanHouse", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m5 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("decorateHouse", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}import com.starbucks.demo.proxy.Owner;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxyClass extends Proxy
implements Owner
{
private static Method m3;
private static Method m1;
private static Method m6;
private static Method m4;
private static Method m0;
private static Method m5;
private static Method m2;
public ProxyClass(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final Boolean rentHouse(String paramString)
throws InterruptedException
{
try
{
return (Boolean)this.h.invoke(this, m3, new Object[] { paramString });
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sleepWithGirls()
throws InterruptedException
{
try
{
this.h.invoke(this, m6, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void cleanHouse()
throws InterruptedException
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void decorateHouse()
throws InterruptedException
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException|InterruptedException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m3 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("rentHouse", new Class[] { Class.forName("java.lang.String") });
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m6 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("sleepWithGirls", new Class[0]);
m4 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("cleanHouse", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m5 = Class.forName("com.starbucks.demo.proxy.Owner").getMethod("decorateHouse", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
其中
public final class ProxyClass extends Proxy implements Owner
就是动态生成的代理类。
抽出一个方法看一下:
Boolean rentHouse(String paramString)
return (Boolean)this.h.invoke(this, m3, new Object[] { paramString });
就是调用了实现InvocationHandler接口的代理类中得invoke方法。
public ProxyClass(InvocationHandler paramInvocationHandler) throws {
super(paramInvocationHandler);
}
也就是说:动态代理是jvm运行时会动态创建一个代理类 实现其中每一个方法。在调用时实际是调用那个代理类的方法而已。是动态的生成静态代理类!
这样貌似明白了动态代理,开始用动态代理替代静态的代理----那2000个service的运行时间监控。又一轮的code review,架构师看了vincent的代码,说“vincent,你可以用CGLib啊!再优化优化,太业余了” vincent卒 享年25岁!