Java设计模式-5、代理模式

代理模式用于解耦合服务提供者和使用者,分为静态和动态代理。静态代理在编译时创建类文件,但不灵活,需为每个目标类创建代理。动态代理则更灵活,能在运行时动态生成代理对象,JDK动态代理基于接口,CGLIB则可代理未实现接口的类。两者在性能和灵活性上有不同权衡。
摘要由CSDN通过智能技术生成

代理模式?

       代理模式的本质是⼀个中间件,主要⽬的是解耦合服务提供者和使⽤者。 使⽤者通过代理间接的访问服务提供者,便于后者的封装和控制。是⼀种 结构性模式。

       来自于大话设计模式的解释:为其他对象提供一种代理以控制对这个对象的访问

Subject: 定义 RealSubject 对外的接⼝,且这些接⼝必须被 Proxy 实现,
这样外部调⽤ proxy 的接⼝最终都被转化为对 realsubject 的调⽤。
RealSubject: 真正的⽬标对象。
Proxy: ⽬标对象的代理,负责控制和管理⽬标对象,并间接地传递外部对
⽬标对象的访问。
Remote Proxy: 对本地的请求以及参数进⾏序列化,向远程对象发送请
求,并对响应结果进⾏反序列化,将最终结果反馈给调⽤者;
Virtual Proxy: 当⽬标对象的创建开销⽐较⼤的时候,可以使⽤延迟或者
异步的⽅式创建⽬标对象;
Protection Proxy: 细化对⽬标对象访问权限的控制。

说道代理就会有动态代理和静态代理

静态代理和动态代理的区别

1. 灵活性 :动态代理更加灵活,不需要必须实现接⼝,可以直接代理实 现类,并且可以不需要针对每个⽬标类都创建⼀个代理类。另外,静态 代理中,接⼝⼀旦新增加⽅法,⽬标对象和代理对象都要进⾏修改,这 是⾮常麻烦的!
2. JVM 层⾯ :静态代理在编译时就将接⼝、实现类、代理类这些都变成 了⼀个个实际的 class ⽂件。⽽动态代理是在运⾏时动态⽣成类字节 码,并加载到 JVM 中的。
代理模式有静态代理和动态代理两种实现方式,我们 先来看一下静态代理模式的实现。

1. 静态代理

从 JVM 层面来说,  静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。所以代码固定。

静态代理实现步骤:

  1. 定义一个接口及其实现类;
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

代码如下:

1. 业务接口

/**
 * 业务接口
 * @author WHM
 *
 */
public interface IUserDao {
     void save();
}

2. 实现类,被代理对象

/**
 *   被代理对象
 * @author WHM
 *
 */
public class UserDao implements IUserDao {

	public void save() {
		System.out.println("保存数据");
	}
}

3. 代理类,这边的UserDao,可以当参数传入save方法中。

public class UserDaoProxy implements IUserDao{

	private IUserDao target = new UserDao();
	
    public void save() {
        System.out.println("开始事务...");
        target.save();//执行目标对象的方法
        System.out.println("提交事务...");
    }

}

4. 验证

/**
 *    因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
       如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
 * @author WHM
 *
 */
public class Tmain {

	public static void main(String[] args) {
        //代理对象
        UserDaoProxy proxy = new UserDaoProxy();

        //执行的是代理的方法
        proxy.save();
	}
}

5 结果:

2. 动态代理

在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成一个代理对象。

这个方法一共有 3 个参数:

  1. loader :类加载器,用于加载代理对象。
  2. interfaces : 被代理类实现的一些接口;
  3. h : 实现了 InvocationHandler 接口的对象;

要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

invoke() 方法有下面三个参数:

  1. proxy :动态生成的代理类
  2. method : 与代理类对象调用的方法相对应
  3. args : 当前 method 方法的参数

也就是说:你通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。

2.1 jdk代码如下:

1. 创建代理对象    部分代码引用之前静态代理的

/*
 * 创建动态代理对象
 * 动态代理不需要实现接口,但是需要指定接口类型
 */
public class ProxyFactory {
	//维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }

   //给目标对象生成代理对象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务2");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务2");
                        return returnValue;
                    }
                }
        );
    }
}

2. 验证类

public class Tmain {
public static void main(String[] args) {
	// 目标对象
    IUserDao target = new UserDao();
    // 原始的类型
    System.out.println(target.getClass());

    // 给目标对象,创建代理对象
    IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
    // 内存中动态生成的代理对象
    System.out.println(proxy.getClass());

    // 执行方法   【代理对象】
    proxy.save();
}
}

3. 结果

2 Cglib代理

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。

例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

你需要自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法。

主要要引入包:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
</dependencies>

  1. obj : 被代理的对象(需要增强的对象)
  2. method : 被拦截的方法(需要增强的方法)
  3. args : 方法入参
  4. proxy : 用于调用原始方法

你可以通过 Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用的是 MethodInterceptor 中的 intercept 方法。

CGLIB 动态代理类使用步骤

  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类;

2.2 cglib代码如下:

1. 定义对象  部分代码还是引用之前静态代理的

public class ProxyFactory implements MethodInterceptor {

	// 维护目标对象
	private Object target;

	public ProxyFactory(Object target) {
		this.target = target;
	}

	// 给目标对象创建一个代理对象
	public Object getProxyInstance() {
		// 1.工具类
		Enhancer en = new Enhancer();
		// 2.设置父类
		en.setSuperclass(target.getClass());
		// 3.设置回调函数
		en.setCallback(this);
		// 4.创建子类(代理对象)
		return en.create();
	}

	// 干预
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("开始事务3...");
		// 执行目标对象的方法
		Object returnValue = method.invoke(target, args);
		System.out.println("提交事务3...");
		return returnValue;
	}
}

2. 验证类

/**
 * 1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能
 * 2.代理的类不能为final,否则报错
 * 3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
 * @author WHM
 */
public class Tmain {
	public static void main(String[] args) {
		// 目标对象
		UserDao target = new UserDao();
		// 代理对象
		UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
		// 执行代理对象的方法
		proxy.save();
	}
}

3. 结果

总结:

1. 概念

1.静态代理:需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类,被代理类固定。

2.动态代理:代理对象,不需要实现接口,代理对象由JDK代理生成

3.cglib代理:并没有实现任何的接口,这个时候就可以使用以目标对象子类

2 对别

2.1JDK 动态代理和 CGLIB 动态代理对比

  1. JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
  2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

2.2 静态代理和动态代理的对比

  1. 灵活性 :动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。另外,静态代理中,接口一旦新增加方法,目标对象和代理对象都要进行修改,这是非常麻烦的!
  2. JVM 层面 :静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

3. 应用

在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理。

4. 优缺点

优点 1.增强额外的功能操作,即扩展目标对象的功能,符合开闭原则. 2.代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

缺点 1.在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢。 2.增加了系统的复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值