代理模式
概念:
- 真实对象:被代理的对象
- 代理对象:
- 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的
实现方法:
- 静态代理:有一个类文件描述代理模式
- 动态代理:在内存中形成代理类
- 实现步骤
- 代理对象和真实对象实现相同的接口
- 代理对象 = Proxy.newProxyInstance();
- 使用代理对象调用方法
- 增强方法
- 增强方式
- 增强参数列表
- 增强返回值类型
- 增强方法体执行逻辑
- 实现步骤
JDK 代理
- JDK 代理是需要有接口的, 在 Proxy.newProxyInstance() 的第二个参数中可以看出,是需要接口类的
- 因为生成的代理类已经继承了一个Proxy对象,所以不能再继承了,那我们得 is-a 才能进行代理,也就是说才能执行他的方法。所以代理需要不是继承就是接口
- 加VM参数:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true可以将生成的动态代理类输出到文件,这个文件默认处于当前项目的com.jdk包下
- 也就是可以在代码中设置 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);
- 准确的说,jdk代理的是接口所持有的方法,而不是代理具体的实现类
案例
interface TargetDao {
String doSomething();
}
class TargetDaoImpl implements TargetDao {
@Override
public String doSomething() {
System.out.println("TargetDaoImpl 做一些事情");
return "成功";
}
}
class ProxyFactory{
Object target;
public ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
/**
* 每调用一个方法都会执行这里
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 该方法体内不能打印proxy代理类,否则会一直循环该代理
System.out.println("jdk代理类的方法之前前增强");
Object invoke = method.invoke(target, args);
System.out.println("jdk代理类的方法之后后增强");
return invoke;
}
});
}
}
public class JdkProxyClient {
public static void main(String[] args) {
TargetDao targetDao = new TargetDaoImpl();
ProxyFactory proxyFactory = new ProxyFactory(targetDao);
TargetDao proxyInstance = (TargetDao)proxyFactory.getProxyInstance();
String s = proxyInstance.doSomething();
System.out.println(s);
}
}
代理生成的.class
// 实现代理对象 TargetDao
public final class $Proxy0 extends Proxy implements TargetDao {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
// 生成的代理方法
public final String doSomething() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.jdk.TargetDao").getMethod("doSomething");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从生成的代理类.class中看出,他确实代理的是接口所持有的方法,而不会代理具体实现类,如果强转成具体实现类是不可行的
CGLIB代理
- cglib 依赖于 asm 需要代入asm的依赖
- cglib 不用局限于是否带有接口,但带有接口的,也能代理,因为生成的代理类默认实现了一个接口,所以还可以使用继承,而接口可以多实现,所以也可以代理接口的对象
-
查看cglib生成的代理类
-
在运行环境中添加参数-Dcglib.debugLocation=P:\java\jdkstudyidea\proxy\target\class
-
生成代理类的结构:
-
public class TargetDao E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB91064b0b implements TargetDao, Factory
-
public class TargetDaoImpl E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIBf99331 extends TargetDaoImpl implements Factory
-
- 总会实现一个Factory接口
- 代理类的方法都会有两个方法
-
一个是原生的方法 使用CGLIB X X X XXX XXXi自增的方式记录
- CGLIB$equals$0
- CGLIB$toString$1
- CGLIB$doSomething$4
-
一个是代理生成的方法
-
equals
-
toString
-
doSomething
-
-
代理的方法
public final String doSomething() { // 这个就是我们设置的setCallback实现类 MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; // 判断是否设置了,如果等于空,就设置一个默认的回调 if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } // 如果没有自定义,那么就执行原生的方法, // 如果定义了callback, 那么就执行定义的 return var10000 != null ? (String)var10000.intercept(this, CGLIB$doSomething$4$Method, CGLIB$emptyArgs, CGLIB$doSomething$4$Proxy) : super.doSomething(); }
案例
加jar:
- cglib-3.3.0.jar
- asm-9.2.jar
interface TargetDao {
String doSomething();
}
class TargetDaoImpl implements TargetDao {
@Override
public String doSomething() {
System.out.println("TargetDaoImpl 做一些事情");
return "成功";
}
}
public class CglibProxyClient {
public static void main(String[] args) {
TargetDao target = new TargetDaoImpl();
Enhancer enhancer = new Enhancer();
// 如果.class 是接口,则实现,如果.class 是类或抽象类,则继承
enhancer.setSuperclass(TargetDaoImpl.class);
enhancer.setCallback(new MethodInterceptor() {
/**
* 每调用一个方法都会执行这里
* 生成的代理类执行时是这样的
* var10000.intercept(this, CGLIB$doSomething$4$Method, CGLIB$emptyArgs, CGLIB$doSomething$4$Proxy)
* @param o 对应 this,即生成的代理类
* @param method 正在执行的方法
* @param params 正在执行的方法的参数
* @param methodProxy cglib定义的,方法代理,即生成的代理类中会出现两个方法
* 这个对象有 invoke 和 invokeSuper
* invoke 执行的是代理类中的被增强的方法 doSomething
* invokeSuper 执行的是代理类中的父类方法(原始没有被代理的方法) CGLIB$doSomething$4
* 但invokeSuper不能使用target对象,只能使用o对象(被代理的代理对象),因为代理类能转成目标类,而目标类不能转成代理类
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib 代理 前增强");
Object methodReturnVal = method.invoke(target, params);
// 代理类能转成目标类,而目标类不能转成代理类
// methodProxy.invokeSuper(target, params);
System.out.println("cglib 代理 前增强");
return methodReturnVal;
}
});
TargetDao o = (TargetDao)enhancer.create();
String s = o.doSomething();
System.out.println(s);
}
}
代理生成的.class
public class TargetDaoImpl$$EnhancerByCGLIB$$f99331 extends TargetDaoImpl implements Factory {
}
总结
-
jdk代理代理的是接口,而不是具体的实现类,从.class中可以看出,是实现需要代理的那个类的接口
所以,具体实现类的独有方法是不能被代理的,包括继承了某个类,那么父类的方法是不能够代理的
-
cglib代理代理的是具体类,即你需要代理哪个类他就是继承哪个类给你
所以,所有方法都能被代理到,包括上面说的父类的方法
-
jdk代理的是接口,cglib代理的是具体类