不得不会的23种Java设计模式(三)——代理模式

定义

  • 为其他对象提供一种代理,以控制对这个对象的访问。
  • 代理对象在客户端和目标对象之间起到中介作用。
  • 代理模式属于结构型设计模式,分为静态代理和动态代理

目的

  • 保护目标对象
  • 增强目标对象

静态代理

特点

  • 程序运行前代理类的.class文件就已经存在
  • 装饰者模式就是静态代理的一种体现。

案例

  • 被代理对象
package test23;

/**
 * 人有很多行为,比如谈恋爱
 */
public interface Person {
    void findLove();
}
package test23;

public class Son implements Person {
    @Override
    public void findLove() {
        System.out.println("儿子要求:肤白貌美");
    }
}
  • 代理对象
package test23;

/**
 * 父亲要帮儿子相亲
 */
public class Father {
    private Son son;

    public Father(Son son) {
        this.son = son;
    }
    public void findLove(){
        System.out.println("父亲物色对象");
        son.findLove();
        System.out.println("双方同意交往");
    }
}
  • 测试类
package test23;

public class Main {
    public static void main(String[] args) {
        Father father = new Father(new Son());
        father.findLove();
    }
}
  • 运行结果如下

在这里插入图片描述

缺点

  • 大量的代码重复。静态代理类和目标类实现了相同的接口,代理类通过目标类实现了相同的方法。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  • 静态代理对象只服务于一种类型的对象。 如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

动态代理

动态代理和静态代理的基本思路一致,只不过动态代理功能更加强大,随着业务的扩展适应性更强。

特点

字节码随用随创建,随用随加载。

动态代理的方式

  • 基于接口JDK代理
    提供者:JDK 官方的 Proxy 类。
    要求:被代理类最少实现一个接口
  • 基于子类CGLIB代理
    提供者:第三方的 CGLib,如果报 asmxxxx 异常,需要导入 asm.jar。
    要求:被代理类不能用 final 修饰的类(最终类)。

jdk实现方式

jdk动态代理生成对象的步骤

  • 1.获取被代理对象的引用,并且获取它的所有接口,反射获取
  • 2.jdk动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口
  • 3.动态生成Java代码,新加的业务逻辑方法由一定的逻辑代码调用。
  • 4.编译新生成的Java代码.class文件
  • 5.重新加载到JVM中运行。

案例

package test24;

/**
 * 人有很多行为,比如谈恋爱
 */
public interface Person {
    void findLove();
}
  • 被代理对象(目标对象)
package test24;

public class Customer implements Person {
    @Override
    public void findLove() {
        System.out.println("高富帅");
    }
}
  • 代理对象
package test24;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

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 obj = method.invoke(this.target,args);
        after();
        return obj;
    }

    private void before() {
        System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("如果合适的话,就准备办事");
    }
}
  • 测试类
package test24;

public class Main {
    public static void main(String[] args) {
        try {
            Person obj = (Person)new JDKMeipo().getInstance(new Customer());
            obj.findLove();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 运行结果如下

在这里插入图片描述

Cglib代理调用

案例

  • maven项目引入以下依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>
package test25;

/**
 * 人有很多行为,比如谈恋爱
 */
public interface Person {
    void findLove();
}
  • 被代理对象(目标对象)
package test25;

public class Customer implements Person {
    @Override
    public void findLove() {
        System.out.println("高富帅");
    }
}
  • 代理对象
package test25;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibMeipo implements MethodInterceptor {
    public Object getInstance(Class<?> clazz) throws Exception{
        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("我是媒婆,我要给你找对象,现在已经确认你的需求");
        System.out.println("开始物色");
    }

    private void after() {
        System.out.println("如果合适的话,就准备办事");
    }
}
  • 测试类
package test25;

public class CglibTest {
    public static void main(String[] args) {
        try{
            Customer obj = (Customer) new CglibMeipo().getInstance(Customer.class);
            obj.findLove();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 运行结果如下

在这里插入图片描述

Cglib和jdk动态代理对比

  • jdk动态代理实现了被代理对象的接口,Cglib代理继承了被代理对象。
  • jdk动态代理和Cglib代理在运行期生成字节码,jdk动态代理直接写Class字节码,Cglib代理使用asm框架写Class字节码,Cglib代理实现更复杂,生成代理类比jdk动态代理效率低。
  • jdk的动态代理调用代理方法是通过反射机制调用的,Cglib代理是通过FastClass机制直接调用的,Cglib代理的执行效率更高。

代理模式与Spring

代理模式在Spring源码中的应用

ProxyFactoryBean核心方法getObject(),源码如下:

/**
 * Return a proxy. Invoked when clients obtain beans from this factory bean.
 * Create an instance of the AOP proxy to be returned by this factory.
 * The instance will be cached for a singleton, and create on each call to
 * {@code getObject()} for a proxy.
 * @return a fresh AOP proxy reflecting the current state of this factory
 */
@Override
public Object getObject() throws BeansException {
	initializeAdvisorChain();
	if (isSingleton()) {
		return getSingletonInstance();
	}
	else {
		if (this.targetName == null) {
			logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
					"Enable prototype proxies by setting the 'targetName' property.");
		}
		return newPrototypeInstance();
	}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yemuxiaweiliang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值