代理模式
1.静态代理
a. 角色分析
- 真实角色:被代理的角色;
- 代理角色:代理真实角色,代理真实角色后,一般我们会有附属操作,用来对真实角色的增强;
- 客户端:访问代理对象的人;
b.静态代理的好处
- 可以是真实对象的操作更加简单,不用去关注公共业务;
- 在不修改目标对象的前提下,可以通过代理对象对目标对象功能扩展;
c.静态代理的缺点
- 一个真实角色就需要一个代理角色;代码量翻倍;导致开发效率下降;
- 代理角色和真实角色实现了相同的接口,代理角色通过真实角色实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理角色也需要实现此方法。增加了代码维护的复杂度;
d.静态代理代码实现
-
先创建一个公共的接口Animal,提供一个sleep方法;
public interface Animal {
void sleep();
}
-
创建一个Dog类真实角色实现Animal接口,重写sleep方法;
public class Dog implements Animal {
@Override
public void sleep() {
System.out.println("小狗狗睡觉!!!");
}
}
-
创建一个Dog的代理角色DogProxy类;
public class DogProxy implements Animal {
private Dog dog;
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public void sleep() {
System.out.println("小狗狗睡觉前执行的代码");
dog.sleep();
System.out.println("小狗狗睡觉后执行的代码");
}
}
4.测试
?
public class App {
public static void main(String[] args) {
DogProxy dogProxy = new DogProxy();
dogProxy.setDog(new Dog());
dogProxy.sleep();
}
}
测试结果
D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA
小狗狗睡觉前执行的代码
小狗狗睡觉!!!
小狗狗睡觉后执行的代码
Process finished with exit code 0
2. 动态代理
① jdk代理(接口代理)
? 前提条件:代理角色不需要实现接口,但是真实角色一定要实现接口,否则不能用动态代理
? 所谓的jdk代理指的是借助jdk所提供的相关类来实现代理模式,其主要有两个类:java.lang.reflect InvocationHandler和java.lang.reflect Proxy。在实现代理模式时,只需要实现InvocationHandler接口即可;
-
Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。 public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- ClassLoader loader:指定当前真实对象使用类加载器,获取加载器的方法是固定的
- Class<?>[] interfaces:真实对象实现的接口的类型,使用泛型方式确认类型(接口字节码对象的数组)
- InvocationHandler h:调度方法,执行真实角色的方法时,会触发事件处理器的方法,会把当前执行真实角色的方法作为参数传入
-
InvocationHandler 是由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke 方法。 Object invoke(Object proxy,
Method method,
Object[] args)
- Object proxy:代理角色实例;
- Method method:真实角色执行的方法对象;
- Object[] args:真实角色执行的参数列表;
a.创建一个Ihadoop接口;
public interface Ihadoop {
void eating(String name);
}
b.创建一个真实角色并实现Ihadoop接口;
public class Hadoop implements Ihadoop {
@Override
public void eating(String name) {
System.out.println("大象再吃"+name);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
c.测试 import com.gec.targect.Ihadoop;
import com.gec.targect.impl.Hadoop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MainApp {
public static void main(String[] args) {
Ihadoop hadoopProxy = (Ihadoop) Proxy.newProxyInstance(Hadoop.class.getClassLoader()
,new Class[]{Ihadoop.class}
, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object res = method.invoke(new Hadoop(), args);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("耗时" + time);
return res;
}
}
);
hadoopProxy.eating("香蕉");
}
}
lambda表达式等价写法 import com.gec.targect.Ihadoop;
import com.gec.targect.impl.Hadoop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MainApp {
public static void main(String[] args) {
Ihadoop hadoopProxy = (Ihadoop) Proxy.newProxyInstance(Hadoop.class.getClassLoader()
,new Class[]{Ihadoop.class}
, (proxy, method, args1) -> {
long startTime = System.currentTimeMillis();
Object res = method.invoke(new Hadoop(), args1);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("耗时" + time);
return res;
}
);
hadoopProxy.eating("香蕉");
}
}
jdk动态代理后的结果 D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA
大象再吃香蕉
耗时3003
Process finished with exit code 0
输出的耗时时间就是对真实角色的eat()方法的增强;本身真实角色Hadoop只是普通的吃方法没有计算耗时功能;
② Cglib代理(子类代理)
a.前提条件
1.需要引入cglib的jar文件;
2.代理的类不能为final,否则报错; 3.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
b.Cglib概述
? Cglib代理是功能最为强大的一种代理方式,因为其不仅解决了静态代理需要创建多个代理类的问题,还解决了jdk代理需要被代理对象(真实角色)实现某个接口的问题。,根据Cglib实现原理,由于其是通过创建子类字节码的形式来实现代理的,如果被代理类的方法被声明final类型,那么Cglib代理是无法正常工作的,因为final类型方法不能被重写;它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
c.代码实现Cglib代理
a.创建一个真实角色(被代理对象)Hadoop1;
public class Hadoop1 {
public static void eating(String name) {
System.out.println("大象再吃"+name);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
b.编写cglib动态代理并测试
import com.gec.cglib.targect.Hadoop1;
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 MainApp {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hadoop1.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
Object res = methodProxy.invokeSuper(o, objects);
long endTime = System.currentTimeMillis();
long time = endTime - startTime;
System.out.println("耗时" + time);
return res;
}
});
Hadoop1 o = (Hadoop1) enhancer.create();
o.eating("苹果");
}
}
结果展示
D:\Development\jdk1.8.0_152\bin\java.exe "-javaagent:D:\Development\IntelliJ IDEA
大象再吃苹果
耗时3010
Process finished with exit code 0
动态代理优缺点:
- 优点:灵活易用,不用手动创建代理类。
- 缺点:如果频繁创建代理类,可能会让metaspace空间不足,从而触发频繁FullGC,因此如果我们需要自己用到动态代理时,一般将创建出来的代理对象进行缓存复用。
|