代理模式有静态代理和动态代理两种
一:静态代理
静态代理可以帮助我们提高对代码的复用,且可以隐藏具体对象的信息
几个类(这里叫做用户实现类)实现了同一个接口(这里叫做用户接口)时,当他们都要处理同一个业务逻辑时,可以利用代理类来进行同一个业务逻辑的处理,调用时就不再是直接调用用户实现类的方法,这是类似于装饰模式的对方法的增强,但代理模式最主要的还是在于其用户实现类不用直接与调用者交互,而是代理类与调用者交互,这样有利于当不方便直接调用用户实现类时,可以委托给代理类来做这些事.
这里定义了一个用户接口,我们知道肯定不止一个用户即不止一个用户实现类且他们处理login方法时一般会有不同的操作
package org.example;
public interface Admin {
public void login();
}
这里定义两个用户实现类
package org.example;
public class User1 implements Admin {
@Override
public void login() {
System.out.println("用户1执行登录了");
}
}
package org.example;
public class User2 implements Admin {
@Override
public void login() {
System.out.println("用户2执行登录了");
}
}
此时我们想对用户实现类执行 login方法前进行一个初始化操作,有以下几个方式
1.直接在login方法中添加初始化代码,但是这样一是代码复用率不高,二是如果初始化业务发生改变,需要更改源代码,不符合ocp原则
2.在login方法中调用工具类对初始化进行操作,这样可以提高代码复用率,并且使用多态定义初始化工具类接口,用不同的工具类实现,哪怕业务逻辑改变也可以在实际调用中达到不同的初始化,但是这样就会有本该不属于用户实现类属性存在于用户实现类,不太优雅。
此时我们就需要引入静态代理的的概念,静态代理就是将用户实现类交给代理类,通过调用代理类的方法中嵌套调用用户实现类的方法并在之前完成一些扩展业务达到目的
如下定义了一个静态代理类
package org.example;
public class StaticAdminProxy1 implements Admin {
Admin user1;
public StaticAdminProxy1() {
this.user1 = new User1();
}
@Override
public void login() {
System.out.println("StaticProxy1为用户执行登录初始化");
user1.login();
}
}
代理类也是需要实现用户接口以达到统一性。
当我们需要实现不同的初始化操作时,只需要用不同的代理类来实现用户接口。
此处是对静态代理的测试
public class ProxyTest {
@Test
public void test1(){
Admin user1=new User1();
user1.login();
Admin user2=new User2();
user2.login();
Admin proxy1=new StaticAdminProxy1();
proxy1.login();
Admin proxy2=new StaticAdminProxy2();
proxy2.login();
}
}
//执行结果
/*
用户1执行登录了
用户2执行登录了
StaticProxy1为用户执行登录初始化
用户1执行登录了
StaticProxy2为用户执行登录初始化
用户2执行登录了
*/
但是这样就真的达到了最好的效果了吗?可以发现前文所有用户实现类和代理类都是围绕着Admin这个用户接口来进行扩展的,但是此时如果有Admin2,Admin3,那是不是又要定义一大堆的代理类来处理
这样又是对代码的复用没达到极致。所以此时我们引入了动态代理的概念
二:动态代理
动态代理我们不需要再写代理类了,代理类将有 Proxy.newProxyInstance()方法创建。内部大致原理是动态生成一个字节码文件通过类加载器实例化到内存中,源码分析比站上有。
动态代理的好处就是我们只需要管业务如何增强扩展,把他写到InvocationHandelr的实现类中,至于如何将其与需扩展业务逻辑的类编织在一起是Proxy类的事
具体操作如下
首先定义一个自己的InvocationHandelr,其中invoke方法就是我们要写的增强代码
package org.example;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyHandler implements InvocationHandler {
private Object target;
public Object getMapper(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("初始化成功");
method.invoke(target,args);
return null;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
}
通过如下测试,调用login方法也同时运行了MyHandler中的invoke方法,动态代理起作用了。
且只要setTarget的实参不同,我们就能代理不同的接口
public void test1(){
MyHandler ih=new MyHandler();
setTarget(ih);
Admin user1Mapper= (Admin) ih.getMapper();
user1Mapper.login();
}
private void setTarget(MyHandler ih) {
ih.setTarget(new User1());
}
/*
运行结果
初始化成功
用户1执行登录了
*/
那么这里面的原理还是需要看动态生成的对象干了什么,通过反编译,我们获取到了动态生成对象的java源文件
如下是通过反编译生成的jdk动态生成类的源文件
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.example.Admin;
public final class Proxy0 extends Proxy implements Admin {
private static Method m1;
private static Method m2;
private static Method m3;
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 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 void login() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("org.example.Admin").getMethod("login");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
动态生成的对象内部具有用户实现类的所有field,在调用动态生成的对象时它会调用invoke并且传递进相应参数。
动态代理提供的好处是当业务有需要扩展时我们只需要实现不同的invoke方法,就可以灵活处理
反编译代码如下,使用jdk1.8,高版本ProxyGenerator类不能import
public void test4(){
Admin user1=new User1();
MyHandler ih=new MyHandler(user1);
Admin user1Mapper= (Admin) ih.getMapper();
byte[] b= ProxyGenerator.generateProxyClass("Proxy0",user1Mapper.getClass().getInterfaces(),Modifier.PUBLIC | Modifier.FINAL);
m1(b);
}
public static void m1(byte[] b){
File file=new File(Thread.currentThread().getContextClassLoader().getResource("").toString().substring(Thread.currentThread().getContextClassLoader().getResource("").toString().indexOf("/")+1)+"Proxy0.class");
if (!file.exists()){
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
FileOutputStream fileOutputStream=new FileOutputStream(file);
try {
fileOutputStream.write(b);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}