设计模式 之 代理模式- 2020-10-26 12:16- 设计模式: java,编程,设计模式,代理模式,CGLB,动态代理
代理模式分为两种 静态代理,动态代理。
代理中的角色: 代理类,目标方法,目标类
静态代理
原理
静态代理是由我们自己编写的代理类,代理类中指定引用的真实对象,使用真实对象去执行接口,在编译时期就确定了。
优点和缺点
优点:
1.因为是我们手动编写的代理类,可控性好,可针对指定接口做扩展,例如增加日志或是数据统计等。
2.避免了生成大对象,造成资源浪费
缺点:
1.手动编写 维护等成本增加,如果有多个类需要代理,则维护成本更增加。
2.代码耦合度高,不利于通用性。
3.违反 开闭原则 (开闭原则:增加扩展,避免更新)
因为代理类中耦合了被代理类,如果被代理的方法更新或者删除了,那么代理类中相应的也需要更新或删除,从而违反了开闭原则。
dome
/**
* 接口类
*
* @Author Bai
* @Date 2020-10-26 14:25
*/
public interface HelloService {
void say(String msg);
}
/**
* 真实对象:被代理对象
*
* @Author Bai
* @Date 2020-10-26 14:25
*/
public class HelloServiceImpl implements HelloService {
@Override
public void say(String msg) {
System.out.println("向大家say" + msg);
}
}
/**
* 代理类
*
* @Author Bai
* @Date 2020-10-26 14:26
*/
public class HelloServiceProxy {
/**
* 真实引用对象
*/
private HelloService target;
public HelloServiceProxy(HelloService target) {
this.target = target;
}
@Override
public void say(String msg) {
System.out.println("say proxy 开始");
target.say(msg);
System.out.println("say proxy 结束");
}
}
@Test
public void test1() {
HelloService helloService = new HelloServiceImpl();
HelloServiceProxy proxy = new HelloServiceProxy(helloService);
proxy.say("hello world");
}
动态代理
动态代理有种实现方式,JDK动态代理和CGLB。JDK动态代理 主要是java.lang.reflect包下的类,实现jdk动态代理需要实现InvocationHandler。CGLB是第三方字节码处理库,它的底层是基于ASM框架来处理字节码实现代理的。
JDK动态代理
原理
主要基于java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler来实现动态代理,所有生成的代理类都继承于Proxy,InvocationHandler是一个接口,类似于调用处理器,我们自己在实现动态代理时都必须要实现java.lang.reflect.InvocationHandler。jdk底层其实也是通过asm框架来实现对字节码的操作,读字节码并生生成对应的class文件。
1.获取被代理类的引用,并获取引用类的所有接口,通过反射获取。
2.Proxy基于被代理类的接口生成新的类,拥有被代理类的所有接口。
3.动态生成java代码,把增强逻辑加入到新代码中。
4.重新编译代理类生成新的class文件
5.加载代理类的class文件并重新运
优点和缺点
优点:
1.不需要手动编写代理类,运行时会自动生成代理类
缺点:
1.被代理的类 必须要实现接口,没有实现接口或是抽象类的 类 无法被代理
dome
1.实现InvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理handler
*
* @Author Bai
* @Date 2020-10-26 14:52
*/
public class HelloInvocationHandler implements InvocationHandler {
/**
* 真实对象:被代理的对象引用
*/
private Object target;
public HelloInvocationHandler(Object target) {
super();
this.target = target;
}
/**
* @param proxy 代理类
* @param method 代理类的方法
* @param args 入参
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("HelloInvocationHandler#invoke start");
//两者执行有何区别?target or proxy
//proxy 会造成死循环,猜测可能是因为invoke内部实现 需要生成proxy的实例,去调用proxy实例的invoke,就会造成循环调用
// Object invoke = method.invoke(proxy, args);
//这里要传入目标类
Object invoke = method.invoke(target, args);
System.out.println("HelloInvocationHandler#invoke end");
return invoke;
}
/**
* 获取代理对象
*
* @return
*/
public Object getProxy() {
//第一个入参 使用哪个loader将proxy对象加载到内存中,一般是跟被代理对象同个loader就可以
//第二个入参 proxy要代理的是哪些接口
Class<?> aClass = target.getClass();
saveProxyClass0();
return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), this);
}
private void saveProxyClass0() {
//通过反编译工具可以查看源代码
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{HelloService.class});
try {
FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
os.write(bytes);
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.测试
/**
* jdk动态代理
*/
@Test
public void jdkProxyTest() throws Throwable {
//真实对象
HelloService helloService = new HelloServiceImpl();
//代理handler
HelloInvocationHandler handler = new HelloInvocationHandler(helloService);
// 代理对象
HelloService helloProxyService = (HelloService) handler.getProxy();
//使用代理对象执行
System.out.println("--- helloProxyService.result ---");
helloProxyService.say("helloProxyService.jdk动态代理");
//使用handler执行
String[] args = new String[]{"helloProxyService.invoke"};
//对应 Object invoke = method.invoke(proxy, args)
// Object say = handler.invoke(helloProxyService, helloProxyService.getClass().getMethod("say", String.class), args);
//对应 Object invoke = method.invoke(target, args)
//入参: 代理对象,被调用的目标方法,入参
Object say = handler.invoke(helloProxyService, helloService.getClass().getMethod("say", String.class), args);
System.out.println("--- HelloProxyService.result ---" + say);
}
3.JDK动态生成的代理类源码
import com.xy.blog.test.poxy.HelloService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements HelloService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
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 say(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void eat(String var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("com.xy.blog.test.poxy.HelloService").getMethod("say", Class.forName("java.lang.String"));
m4 = Class.forName("com.xy.blog.test.poxy.HelloService").getMethod("eat", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
CGLB代理
原理 todo
第三方字节码处理库,底层是asm框架,如果要了解原理就需要了解asm框架,以及它的一些设计模式
cglb是通过生成被代理类的子类来实现代理。
优点和缺点todo
优点:
- 被代理对象无须实现接口,对类的入侵没有那么深
dome
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* cglb动态代理实现
*
* @Author Bai
* @Date 2020-10-26 16:00
*/
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
/**
* 获取代理类
*
* @param clazz 真实类
* @return
*/
public Object getProxy(Class clazz) {
//设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
//通过字节码技术动态创建子类实例
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("前置代理");
//通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置代理");
return result;
}
}
/**
* jdk动态代理
*/
@Test
public void cglbProxyTest() throws Throwable {
//真实对象
CglibProxy cglibProxy = new CglibProxy();
HelloService helloService = (HelloService) cglibProxy.getProxy(HelloServiceImpl.class);
helloService.say("cglb动态代理");
}
执行结果:
前置代理
向大家saycglb动态代理
后置代理
两者区别:JDK动态代理/CGLB动态代理
应用
aop
aop由JDK动态代理和CGLB动态代理实现,当需要代理的类实现了接口,既使用jdk动态代理,如果需要代理的类没有实现接口则自动切换为CGLB代理。<br /> jdk动态代理是基于反射来获取被代理的类,要去被代理的类必须要实现接口。<br /> cjlb是代码生成的类库,它是在运行过程中动态的生成类的子类来实现代理的,cglb是基于继承来实现的,所以如果一个类被final修饰,那么这个类将不能被cglb代理。
源码分析todo
问题整理
1.invoke接口的第一个入参proxy不是很经常用,为什么还要传过来呢?
2.静态代理为啥要构造参数里给出了来
3.动态代理为什么要实现接口?
参考资料:
http://hollischuang.gitee.io/tobetopjavaer/#/basics/java-basic/static-proxy
**