1、是什么
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
办事要求人,所以找代理。
结构型设计模式
2、干嘛用
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
远程代理
虚拟代理
安全代理
智能代理
(1) 当客户端对象需要访问远程主机中的对象时可以使用远程代理。
(2) 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
(3) 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
(4) 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
(5) 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
spring框架中的aop技术
3、怎么用
案例:
疫情期间,公司非法裁员,员工找律师代理跟公司打官司。
1、静态代理
程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
组成
一个委托类和代理类之间的约束接口,一个代理类,一个委托类,一个工厂类
代码实现:
首先得有一个接口,通用的接口是代理模式实现的基础,这个接口有打官司的功能
public interface Lawsuit {
void dolawsuit();
}
//被代理类
public class Mylawsuit implements Lawsuit {
@Override
public void dolawsuit() {
System.out.println("跟公司打官司");
}
}
//代理类
public class LawsuitBylawyer implements Lawsuit {
private Lawsuit lawsuit;
public LawsuitBylawyer(Lawsuit lawsuit) {
this.lawsuit = lawsuit;
}
@Override
public void dolawsuit() {
System.out.println("代理全权代理");
lawsuit.dolawsuit();
System.out.println("打得李晶丁卓满地找牙");
}
}
//代理工厂
public class SubjectStaticFactory {
public static Lawsuit getInstance() {
return new LawsuitBylawyer(new Mylawsuit());
}
}
//测试
public class Test {
public static void main(String[] args) {
Lawsuit instance = SubjectStaticFactory.getInstance();
instance.dolawsuit();
}
}
2、动态代理
基于JDK技术 动态代理类技术核心 Proxy类和一个 InvocationHandler 接口
基于CGlib 动态代理模式 基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术
基于 Aspectj 实现动态代理(修改目标类的字节,织入代理的字节,在程序编译的时候 插入动态代理的字节码,不会生成全新的Class )
基于 instrumentation 实现动态代理(修改目标类的字节码、类装载的时候动态拦截去修改,基于javaagent) -javaagent:spring-instrument-4.3.8.RELEASE.jar (类装载的时候 插入动态代理的字节码,不会生成全新的Class )
(1)、基于JDK技术 动态代理
XXXInvocationHandler 类 实现InvocationHandler接口,这个类中持有一个被代理对象(委托类)的实例target。
该类别JDK Proxy类回调InvocationHandler 接口中有一个invoke方法,当一个代理实例的方法被调用时,代理方法将被编码并分发到 InvocationHandler接口的invoke方法执行。
invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
…
Object result = method.invoke(target, args);
…
return result;
}
invoke参数解释
proxy 代表动态生成的 动态代理 对象实例
method 代表被调用委托类的接口方法,和生成的代理类实例调用的接口方法是一致的,它对应的Method 实例
args 代表调用接口方法对应的Object参数数组,如果接口是无参,则为null; 对于原始数据类型返回的他的包装类型。
代码实现
//通用的接口
public interface Lawsuit {
void dolawsuit();
}
//被代理类
public class Mylawsuit implements Lawsuit {
@Override
public void dolawsuit() {
System.out.println("跟公司打官司");
}
}
//动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 全能代理类
*/
public class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 在转调具体目标对象之前,可以执行一些功能处理
*/
System.out.println("被动态代理类回调执行, 代理类 proxyClass =" + proxy.getClass()
+ " 方法名: " + method.getName() + "方法. 方法返回类型:" + method.getReturnType()
+ " 接口方法入参数组: " + (args == null ? "null" : Arrays.toString(args)));
System.out.println("代理全权代理");
Object invoke = method.invoke(target, args);
System.out.println("打得李晶满地找牙");
return invoke;
}
}
//动态代理调用
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
System.out.println("-------------------第一种创建代理类方法--------------");
//创建一个实例对象,这个对象是被代理的对象,委托类
Mylawsuit mylawsuit = new Mylawsuit();
//创建一个与代理类相关联的InvocationHandler,每一个代理类都有一个关联的 InvocationHandler,并将代理类引用传递进去
InvocationHandler handler = new ProxyHandler(mylawsuit);
Lawsuit lawsuitProxy = (Lawsuit) Proxy.newProxyInstance(Lawsuit.class.getClassLoader(), new Class<?>[]{Lawsuit.class}, handler);
lawsuitProxy.dolawsuit();
System.out.println("-------------------第二种创建代理类方法--------------"); /**
* 动态代理对象步骤
* 1、 创建一个与代理对象相关联的 InvocationHandler,以及真实的委托类实例
* 2、Proxy类的getProxyClass静态方法生成一个动态代理类stuProxyClass,该类继承Proxy类,实现 Person.java接口;JDK动态代理的特点是代理类必须继承Proxy类
* 3、通过代理类 proxyClass 获得他的带InvocationHandler 接口的构造函数 ProxyConstructor
* 4、通过 构造函数实例 ProxyConstructor 实例化一个代理对象,并将 InvocationHandler 接口实例传递给代理类。
*/
// 1、创建 InvocationHandler 实例并设置代理的目标类对象
Mylawsuit mylawsuit2 = new Mylawsuit();
InvocationHandler Handlertwo = new ProxyHandler(mylawsuit2);
// 2 创建代理类,是一个字节码文件, 把 proxyClass 保存起来就能看到 他继承Proxy 类,实现Person接口
Class<?> proxyClass = Proxy.getProxyClass(Lawsuit.class.getClassLoader(), new Class<?>[]{Lawsuit.class});
/** 代理类信息 */
System.out.println("package2 = " + proxyClass.getPackage() + " SimpleName = " + proxyClass.getSimpleName()
+ " name =" + proxyClass.getName() + " CanonicalName = " + "" + proxyClass.getCanonicalName()
+ " 实现的接口 Interfaces = " + Arrays.toString(proxyClass.getInterfaces())
+ " superClass = " + proxyClass.getSuperclass() + " methods =" + Arrays.toString(proxyClass.getMethods()));
// 3、 通过 proxyClass 获得 一个带有InvocationHandler参数的构造器constructor
Constructor<?> ProxyConstructor = proxyClass.getConstructor(InvocationHandler.class);
// 4、通过构造器创建一个 动态代理类 实例
Lawsuit stuProxy = (Lawsuit) ProxyConstructor.newInstance(Handlertwo);
/** 检测生成的类是否是代理类 */
System.out.println("stuProxy isProxy " + Proxy.isProxyClass(stuProxy.getClass()));
/** 获取 代理类关联的 InvocationHandler 是哪个*/
InvocationHandler handlerObject = Proxy.getInvocationHandler(stuProxy);
System.out.println(handlerObject.getClass().getName());
stuProxy.dolawsuit();
}
}
(2)、基于CGlib 技术动态代理代理类实现 (基于继承)
Cglib是针对类来实现代理的,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。
但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。
导入maven 依赖
<!-- cglib 动态代理依赖 begin -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
<!-- cglib 动态代理依赖 stop -->
//被代理类,普通类
public class Mylawsuit {
public void dolawsuit() {
System.out.println("跟公司打官司");
}
}
方法拦截器 实现 MethodInterceptor 接口
一个切面,用于在方法拦截器中intercept 方法中调用真正业务方法之前 之后处理逻辑
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 CglibMethodInterceptor implements MethodInterceptor {
/**
* 用于生成 Cglib 动态代理类工具方法
*
* @param target 代表需要 被代理的 委托类的 Class 对象
* @return
*/
public Object proxyInterceptor(Class target) {
/** 创建cglib 代理类 start */
// 创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
// 为代理类指定需要代理的类,也即是父类
enhancer.setSuperclass(target);
// 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截
enhancer.setCallback(this);
// 获取动态代理类对象并返回
return enhancer.create();
/** 创建cglib 代理类 end */
}
/**
* 功能主要是在调用业务类方法之前 之后添加统计时间的方法逻辑.
* intercept 因为 具有 MethodProxy proxy 参数的原因 不再需要代理类的引用对象了,直接通过proxy 对象访问被代理对象的方法(这种方式更快)。
* 当然 也可以通过反射机制,通过 method 引用实例 Object result = method.invoke(target, args); 形式反射调用被代理类方法,
* target 实例代表被代理类对象引用, 初始化 CglibMethodInterceptor 时候被赋值 。但是Cglib不推荐使用这种方式
*
* @param obj 代表Cglib 生成的动态代理类 对象本身
* @param method 代理类中被拦截的接口方法 Method 实例
* @param args 接口方法参数
* @param proxy 用于调用父类真正的业务类方法。可以直接调用被代理类接口方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("代理全权代理");
Object invokeSuper = proxy.invokeSuper(obj, args);
System.out.println("打得李晶满地找牙");
return invokeSuper;
}
}
Cglib测试类
public class CglibTest {
public static void main(String[] args) {
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
Mylawsuit o = (Mylawsuit) cglibMethodInterceptor.proxyInterceptor(Mylawsuit.class);
o.dolawsuit();
}
}
Cglib 总结
CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类.
由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的
做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用.
提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活。
CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone。