文章目录
代理模式
关乎代理模式就是指再做具体的某件事前,不是通过自己本身去执行而是通过一个第三方或者中间人来进行执行。那么这个就是代理了。
譬如,我要开店卖啤酒。我不会直接去啤酒工厂来进货(啤酒厂人家也不接待我),我会通过一个啤酒代理商来进行与啤酒厂之间的沟通,可能有人会想,为啥不从厂家拿呢,厂家也会考虑这客户之间的关系维护啊,出货量什么的,啤酒厂还都要去管理,我需要在这方面花费精力是不值当的,我好好做我的啤酒,把啤酒做到最好最完美,而与客户沟通的话就与我的代理商来接洽吧,我工厂只用管理好我和代理商之间的关系就好了。这一切的一切都是从成本出发的,再或者大一点的厂子人家会有自己的销售部,这个就是厂自己的代理,所有人都不会直接到车间去买啤酒都是通过销售部来进行订货或者购买的。
那么知道代理的意义是干嘛的了。那么看看程序中最简单的代理模式。
静态代理
再我看看静态代理的时候其实这个的实现与Decorator装饰器模式是一模一样的。但是静态代理方式用的是装饰器加聚合来实现的。
回顾一下装饰器模式,我写的一个简单的小例子,对那个字符串装饰器修改为字符串静态代理模式:
这次其实就是再被代理的方法前后打印下日志而已。
重构以后的类图:
//统一接口方法
public interface Showable {
void show();
}
//被代理的对象
public class MyStr implements Showable {
@Override
public void show() {
System.out.println("kiss ");
}
}
//代理对象after
public class AfterStr implements Showable {
Showable showStr;
public AfterStr(Showable showStr) {
this.showStr = showStr;
}
public void show() {
showStr.show();
System.out.println("hi in after ..");
}
}
//代理对象before
public class BeforeStr implements Showable {
Showable showStr;
public BeforeStr(Showable showStr) {
this.showStr = showStr;
}
public void show() {
System.out.println("hi in before ..");
showStr.show() ;
}
}
//测试方法
public static void main(String[] args) {
MyStr ms = new MyStr();
new BeforeStr(new AfterStr(ms)).show();
System.out.println("====================");
new AfterStr(new BeforeStr(ms)).show();
}
结果
hi in before …
kiss
hi in after …
====================
hi in before …
kiss
hi in after …
其实这就是一个简单的代理模式了。通过测试方法可以看出。其实这个很简单了比装饰器那个还简单点。去掉了。装饰器的主类全部采用了接口来做。这样耦合度更低。
这里必须要说明一下。装饰器适用于有明显目标的方法。比如jdk的inputstream,所有的装饰器就是只对inputstream来用的不会用到其他类对象上。所以修饰器是有针对性的。而代理其实是可以没有针对性的那么理论来说代理是可以加在任何方法上的。
这个静态代理的的例子之所以叫静态,是因为这里代理类都是自己写好的Showable 接口代理。
动态代理
首先我们来看看JDK中采用反射实现的动态代理
JDK的动态代理实现
先看一段代码:
// main函数
//这个是针对上一个列子进行的修改。
public static void main(String[] args) {
//创建被代理对象 ms
MyStr ms = new MyStr();
// 反射生成代理类ss
Showable ss = (Showable) Proxy.newProxyInstance(MyStr.class.getClassLoader(), new Class[] { Showable.class },
new MyStrInvocationHandler(ms));
//调用代理类执行方法
ss.show();
}
//代理类的实际操作
class MyStrInvocationHandler implements InvocationHandler {
Showable show;
MyStrInvocationHandler(Showable show) {
this.show = show;
}
//代理类的实际执行方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("hi before....");
Object o = method.invoke(show, args);
System.out.println("hi after....");
return o;
}
}
动态代理的原理
可以看到这么来做的话很明显不用通过自己来实现代理方法采用jdk的反射类Proxy就可以完成。只是把具体的代理实现方法写到了invoke方法中。
对于这个Proxy动态代理方法是在运行的时候直接通过jdk本身的反射功能创建了一个二进制的class ,而这个class并不写到磁盘上而是直接加载入内存中,直接通过内存的方法直接调用。
需要注意的是jdk的动态代理必须是实现一个接口的,要不是没有办法来实现的。
在main函数中增加一行代码
//增加一个配置
// System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
public static void main(String[] args) {
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
MyStr ms = new MyStr();
Showable ss = (Showable) Proxy.newProxyInstance(MyStr.class.getClassLoader(), new Class[] { Showable.class },
new MyStrInvocationHandler(ms));
ss.show();
}
再次运行后会在工程目录下创建一个新的目录com.sun.proxy
下有个新的二进制class为$Proxy0.class
通过堆class的方便易我们可以获得一个代理类
public final class $Proxy0 extends Proxy
implements Showable
{
private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable paramObject)
{
}
throw new UndeclaredThrowableException(paramObject);
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final void show()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
static
{
try
{
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Showable").getMethod("show", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
由此我们可以看出来了。再main函数中的声明
Proxy.newProxyInstance(MyStr.class.getClassLoader(), new Class[] { Showable.class },
new MyStrInvocationHandler(ms));
对于newProxyInstance其实就是创建一个继承Proxy类并实现Showable的接口类。
那么自然它也实现了Showable的接口show()方法,
public final void show()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
其中的核心代码为
this.h.invoke(this, m3, null);
JDK14 Proxy 生成代理的源码简读
具体的实现参考JDK,我的JDK版本为14版
Proxy类 newProxyInstance 源码
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
// 非空检查
Objects.requireNonNull(h);
//安全检查
final Class<?> caller = System.getSecurityManager() == null? null:Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
* 查找或创建指定的代理类以及构造方法。
* 这块操作是关键
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
对于newProxyInstance方法的三个参数:
- 第一个类加载器。需要你指定一个类加载器、这里使用的话推荐使用被代理类相同的类加载器。因为若自定义加载器打破双亲委派,很可能会导致无法加载类。
- 第二个就是你的动态代理类需要实现的接口
- 第三个就是你需要实现的代理方法。
关于getProxyConstructor 方法
//Look up or generate the designated proxy class and its constructor.
private static Constructor<?> getProxyConstructor(Class<?> caller,ClassLoader loader, Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
//这个proxyCache.sub(intf).computeIfAbsent
//为关键点,可以看出这里有个cache 是用于存放生成的代理的
//sub获取对应的接口类所在的cache
// 判断这个代理是否存在,存在就获取,不存在就创建一个新的
//computeIfAbsent 就是创建新的代理方法
return proxyCache.sub(intf).computeIfAbsent(
loader,
//而真正创建proxy是通过 new ProxyBuilder 来创建的
(ld, clv) -> new ProxyBuilder(ld,clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}
这里是对于proxyCache 的声明
private static final ClassLoaderValue<Constructor<?>> proxyCache =
new ClassLoaderValue<>();
关于ProxyBuild 中的build 方法
Constructor<?> build() {
// 可以看到 定义class是通过以下这个defineProxyClass 这个方法
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
defineProxyClass 创建ProxyClass 的方法
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
创建包
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// all proxy interfaces are public
proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: PROXY_PACKAGE_PREFIX;
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}
if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
}
}
/*
类名后面跟着的数字
* Choose a name for the proxy class to generate.
*
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;
ClassLoader loader = getLoader(m);
trace(proxyName, m, loader, interfaces);
/*
* Generate the specified proxy class.
* 这里是创建proxy的关键方法 generateProxyClass
* 这里通过环境变量的设置PROXY_GENERATOR_V49 来判断用那个Generator,
* 通过 generateProxyClass 来创建新的 proxyClassFile ,
* 而我们再使用过程中并没有设置PROXY_GENERATOR_V49
* 所以默认还是采用的ProxyGenerator对象来创建代理类。
* 而关于proxyClassFile 可以看出是一个字节数组,这就是真正的class文件
* 刚才配置了一段关于ProxyGenerator的配置设置他的文件保存,那就指的是这个。
*/
byte[] proxyClassFile = PROXY_GENERATOR_V49
? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags)
: ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
try {
Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
null, "__dynamic_proxy__");
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
关于 PROXY_GENERATOR_V49 的定义
private static final boolean PROXY_GENERATOR_V49 =
GetBooleanAction.privilegedGetProperty("jdk.proxy.ProxyGenerator.v49");
ProxyGenerator.generateProxyClass 的实现
static byte[] generateProxyClass(ClassLoader loader, final String name,List<Class<?>> interfaces,int accessFlags) {
ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags);
//关键点 generateClassFile 创建一个classfile 文件。通过此方法来创建的。这就是核心方法了
final byte[] classFile = gen.generateClassFile();
//saveGeneratedFiles 这就是是否要保存文件的定义。再测试main函数中设置为了true就会运行一下代码。
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Path.of(dotToSlash(name.substring(0, i)));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i + 1) + ".class");
} else {
path = Path.of(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
关于saveGeneratedFiles 的定义
private static final boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"jdk.proxy.ProxyGenerator.saveGeneratedFiles"));
关于generateClassFile 的实现
private byte[] generateClassFile() {
visit(V14, accessFlags, dotToSlash(className), null,
JLR_PROXY, typeNames(interfaces));
/*
* Add proxy methods for the hashCode, equals,
* and toString methods of java.lang.Object. This is done before
* the methods from the proxy interfaces so that the methods from
* java.lang.Object take precedence over duplicate methods in the
* proxy interfaces.
* 就是给代理类增加hashcode方法,equals方法 以及tostring方法
*/
addProxyMethod(hashCodeMethod);
addProxyMethod(equalsMethod);
addProxyMethod(toStringMethod);
/*
* Accumulate all of the methods from the proxy interfaces.
* 为类增加接口的实现方法
*/
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
if (!Modifier.isStatic(m.getModifiers())) {
addProxyMethod(m, intf);
}
}
}
/*
* For each set of proxy methods with the same signature,
* verify that the methods' return types are compatible.
*/
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
// 构造函数创建
generateConstructor();
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for the Method object
visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName,
LJLR_METHOD, null, null);
// Generate code for proxy method
pm.generateMethod(this, className);
}
}
//静态初始化
generateStaticInitializer();
return toByteArray();
}
总结
扯的太远了,
关于其中的 addProxyMethod generateConstructor generateStaticInitializer 等方法通过源码也可以看到最终调用的是 jdk.internal.org.objectweb.asm 包下的 MethodWriter ClassWriter 中的方法进行操作的具体这里就不在接着写了。反正就是生成二进制class的。关于这个包也是可以了解的。这个与反射的最大区别就是反射出来的是对象,而这个asm下是创建class的。而对象又是根据class来的。
其实我们只要知道java 通过proxy可以动态的创建一个代理类来进行代理操作,而且也可以再代码中可以编写一个新的class 类来这些都证明了java的动态语言功能。
code generate 一个开源的动态代理生成办法。
对于jdk中的proxy实现是需要继承接口来实现的。但是很多代码维护中为了不改变原有的程序结构现在需要对原有的方法添加代理的时候就没有办法使用jdk自带的proxy来实现了。这里介绍一种不需要继承接口就可以实现动态代理的第三方工具 code generate lib
就是cglib
maven引入
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
测试使用代码
public class StartMain {
public static void main(String[] args) {
Enhancer en =new Enhancer();
en.setSuperclass(MyShow.class);
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("hi baby ..");
return proxy.invokeSuper(obj, args);
}
});
MyShow show=(MyShow)en.create();
show.show();
}
}
class MyShow{
public void show() {
System.out.println("kiss ..");
}
}
通过代码可以看到 这里的MyShow 没有实现任何接口,而通过Enhancer 对MyShow增加了代理功能。
- 首先使用的是需要设置 setSuperclass 父类为被代理对象。
- 再添加代理功能setCallback
- 实际动态代理方法实现
- 对于MethodInterceptor 为了省事用了内部匿名类的方式来实现了。
- 其实这与jdk中Proxy中的 InvocationHandler 是十分类似的。
- 而方法intercept 比invoke 多了一个MethodProxy proxy 的变量。
- 其实最后是通过MethodProxy 的引用来完成的被代理的执行,
- 而InvocationHandler的使用是需要将被代理对象通过构造函数来进行传入引用的。
- 可以说这个cglib的使用更灵活,而耦合度与jdk的proxy相比更低
- 但是需要注意的一点,因为是继承关系来实现的动态代理。再final类型的对象中是无法使用的,因为fianl是无法被继承的。
- 再有可以查看源码这个cglib最终的实现也是通过JDK中的类实现的