代理模式介绍

什么叫代理?

代理模式的英文叫做Proxy,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用 。 

代理分为静态代理和动态代理:

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 
动态代理:在程序运行时,运用反射机制动态创建而成。

静态代理:

静态代理的实例:

package com.wc.study.proxy.demo;
//一个普通的接口
public interface Subject {
	void sayHello(String username);
}
package com.wc.study.proxy.demo;
//接口真正的实现类
public class RealSubject implements Subject{

	@Override
	public void sayHello(String username) {
		System.out.println("hello :" + username);
	}

}
package com.wc.study.proxy.demo;
//代理类
public class SubjectProxy implements Subject{
	private Subject subject;
	
	public SubjectProxy(Subject subject) {
		this.subject = subject;
	}
	
	@Override
	public void sayHello(String username) {
		System.out.println("before");
		try {
			subject.sayHello(username);
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			System.out.println("after");
		}
	}
}
package com.wc.study.proxy.demo;

public class TestDemo {
	public static void main(String[] args) {
		SubjectProxy subjectProxy = new SubjectProxy(new RealSubject());
		subjectProxy.sayHello("啦啦啦");
	}
}

     

从上面可以看到的是代理类中实际的操作任然是调用的原来接口的实现类来完成的,代理类在执行的前后可以做一些处理。静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用

 

动态代理:jdk

JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。

动态代理也分为两类:基于接口的代理和基于继承的代理 
两类实现的代表是:JDK代理 与 CGlib代理

JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy类和InvocationHandler接口。 
JDK代理实现的三个要点:

  • 通过java.lang.reflect.Proxy类来动态生成代理类
  • 代理类要实现InvocationHandler接口
  • JDK代理只能基于接口进行动态代理的
package com.wc.study.proxy.demo2;

public interface Subject {
	void sayHello(String username);
}
package com.wc.study.proxy.demo2;

public class RealSubject implements Subject{

	@Override
	public void sayHello(String username) {
		System.out.println("hello :" + username);
	}
	
	public void sayBye(){
		System.out.println("bye bye");
	}

}
package com.wc.study.proxy.demo2;

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

public class ProxyHandler implements InvocationHandler{
	private Object target;
	/**
	 * @param target  传入具体的实现类
	 * @return
	 */
	public Object bind(Object target){
		this.target = target;
		//根据反射 创建一个代理类实例对象    用户在进行方法调用时使用
		//动态代理  需要传入实现类的类加载器  实现类的接口数组  以及代理类的处理逻辑对象 InvocationHandler具体实例
		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("before");
		Object result = method.invoke(target, args);
		System.out.println("after");
		return result;
	}

}
package com.wc.study.proxy.demo2;

public class TestDemo {
	public static void main(String[] args) {
		Subject subject = new RealSubject();
		Subject proxySubject = (Subject)new ProxyHandler().bind(subject);
		proxySubject.sayHello("123456");
		
		RealSubject realSubject = new RealSubject();
		RealSubject proxySubject1 = (RealSubject)new ProxyHandler().bind(realSubject);
		proxySubject1.sayBye();
		
	}
}

       JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。

 

动态代理的第二种实现方式: CGlib

cglib是针对来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。

package com.wc.study.proxy.demo2;

import java.lang.reflect.Method;

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

public class CGlibProxy implements MethodInterceptor{
	private Object target;
	
	public Object bind(Object target){
		this.target = target;
		//创建加强器 用来创建动态代理类
		Enhancer enhancer = new Enhancer();
		//为加强器指定当前的业务类为其父类
		enhancer.setSuperclass(target.getClass());
		//设置回调  对于业务类上的所有方法都会执行回调  在intercept中执行
		enhancer.setCallback(this);
		//创建代理类 并返回
		return enhancer.create();
	}
	/**
	 * Object obj 表示要增强的对象
	 * Method method 表示拦截的方法
	 * Object[] args 表示参数列表  基本数据类型包括其包装类型  int->integer long-> Long
	 * MethodProxy proxy 表示对方法的代理  invokeSuper表示对代理对象方法的调用
	 */
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("before");
		method.invoke(target, args);
		Object result = proxy.invokeSuper(obj, args);
		System.out.println("after");
		return result;
	}

}

 

package com.wc.study.proxy.demo2;

public class TestDemo2 {
	public static void main(String[] args) {
		Subject subject = new RealSubject();
		CGlibProxy proxy = new CGlibProxy();
		Subject proxySubjet = (Subject)proxy.bind(subject);
		proxySubjet.sayHello("123");
		
		RealSubject subject2 = new RealSubject();
		CGlibProxy proxy2 = new CGlibProxy();
		RealSubject proxySubjet2 = (RealSubject)proxy2.bind(subject2);
		proxySubjet2.sayBye();
	}

}

可以看到的是  在代理的方法中执行 method.invoke(target,args) 和 proxy.invokeSuper(obj,args)的结果一样  都是在执行原来的方法。而且由于cglib是将当前代理的对象当做父类,将代理对象当做代理对象的子类来处理。

总结:

 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法。

    JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法。

    CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理。

备注:在创建cglib的例子时需要额外的jar包支持   需要的cglib和asm需要匹配,否则回报错,我在测试的时候使用的是如下的jar。

<!-- https://mvnrepository.com/artifact/asm/asm -->
    <dependency>
      <groupId>asm</groupId>
      <artifactId>asm</artifactId>
      <version>3.3.1</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2</version>
    </dependency>

参考链接:https://www.cnblogs.com/ygj0930/p/6542259.html        https://blog.youkuaiyun.com/DoUUnderstand/article/details/78865385

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值