代理模式
为其他对象提供一种代理,以控制对这个对象的访问。
使用场景
- 保护目标对象
- 增强目标对象
静态代理
显式声明被代理对象
使用场景
代理类,只代理 一个被代理类
- 定义一个person接口
public interface Person {
void findLove();
}
- 被代理类
public class Son implements Person {
@Override
public void findLove() {
System.out.println("肤白 貌美 大长腿");
}
}
- 代理类
public class Father implements Person {
private Person person;
public Father(Son son) {
this.person = son;
}
@Override
public void findLove() {
before();
person.findLove();
after();
}
private void before() {
System.out.println("开始物色");
}
private void after() {
System.out.println("寻找完毕");
}
}
- 测试类
public class StaticProxyTest {
public static void main(String[] args) {
Father father = new Father(new Son());
father.findLove();
}
}
动态代理
动态配置和替换被代理对象。动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,可以代理很多类。
JDK代理
JDK动态代理,需要被代理类实现一个接口
- 代理类
public class JDKMeipo implements InvocationHandler {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object object = method.invoke(this.target, args);
after();
return object;
}
private void before() {
System.out.println("开始物色");
}
private void after() {
System.out.println("寻找完毕");
}
}
- 被代理类
public class Girl implements Person {
@Override
public void findLove() {
System.out.println("高富帅");
}
}
- 测试类
public class JDKProxyTest {
public static void main(String[] args) {
Object o = new JDKMeipo().getInstance(new Girl());
Person instance = (Person) o;
instance.findLove();
// 输出代理对象,通过jad反编译 查看代理类内部情况
// try {
// byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});
// FileOutputStream os = new FileOutputStream("F://$Proxy0.class");
// os.write(bytes);
// os.close();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
- 反编译出来的代理类
通过反编译可以看出来,生成的代理对象中实现了 被代理类所实现的接口,从而实现方法的增强
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
import com.hh.proxy.Person;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements Person
{
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void findLove()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("com.hh.proxy.Person").getMethod("findLove", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
CGLib代理
被代理类不用实现接口。
- 被代理类
public class Boy {
public void findLove() {
System.out.println("白富美");
}
}
- 代理类
public class CglibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) {
//相当于Proxy,代理的工具类
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o, objects);
after();
return obj;
}
private void before() {
System.out.println("开始物色");
}
private void after() {
System.out.println("寻找完毕");
}
}
- 测试类
public class CglibProxyTest {
public static void main(String[] args) {
Boy boy = (Boy) new CglibMeipo().getInstance(Boy.class);
boy.findLove();
}
}
CGLib代理与JDK代理对比
- JDK动态代理是实现了被代理对象的接口,CGLib是继承了被代理对象。
- JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM 框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低。
- JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法, CGLib 执行效率更高
静态代理和动态的本质区别
- 静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步 新增,违背开闭原则
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开 闭原则。
- 若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成, 无需修改代理类的代码
代理模式的优缺点
优点
- 代理模式能将代理对象与真实被调用的目标对象分离
- 一定程度上降低了系统的耦合度,扩展性好
- 可以起到保护目标对象的作用
- 可以对目标对象的功能增强
缺点
- 代理模式会造成系统设计中类的数量增加
- 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
- 增加了系统的复杂度