攻略大全
1. 粘贴攻略
2 造火箭攻略
3. 拧螺丝攻略
3.1 静态代理(Static Proxy Pattern)
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
代理抽象:
/**
* @author Huadao
* @date Created in 2022/4/13
* @desc 代理抽象
*/
public interface IAnimal {
void eat();
}
被代理类:
/**
* @author Huadao
* @date Created in 2022/4/13
* @desc 被代理类
*/
public class ImplHuman implements IAnimal {
@Override
public void eat() {
System.out.println("xxxx");
}
}
代理类:
/**
* @author Huadao
* @date Created in 2022/4/13
* @desc 代理类,实现被代理类的接口实现中所想代理的接口
*/
public class ProxyHuman implements IAnimal {
/**
* 持有被代理类的引用
*/
private IAnimal mIAnimal;
public ProxyHuman(IAnimal iAnimal) {
mIAnimal = iAnimal;
}
@Override
public void eat() {
// 代理对象可以做一些额外操作后再调用被代理对象的方法
beforeEat();
// 调用的是被代理类实例的方法
mIAnimal.eat();
// 代理对象调用被代理对象的方法后可以再接着做一些额外操作
afterEat();
}
private void beforeEat() {
System.out.println("吃之前");
}
private void afterEat() {
System.out.println("吃之后");
}
}
最终调用:
// 通过代理对象调用被代理对象的方法
ProxyHuman proxyHuman = new ProxyHuman(new ImplHuman());
proxyHuman.eat();
3.1.1 优点
- 可以隐藏委托类的实现。
- 可以实现客户端与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
3.1.2 缺点
- 一对一会出现静态代理对象量多、代码量大,从而导致代码复杂、可维护性差的问题。
- 一对多会出现代理对象扩展能力差的问题。
3.1.3 应用场景
3.2 动态代理(Dynamic Proxy Pattern)
代理类在程序运行时创建的代理方式被称为动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。
JDK的动态代理:
/**
* @author huadao
* @date 2021/7/23 15:13
* desc:
*/
public class JavaTest {
public static void main(String[] args) {
// 动态代理ImplHuman
proxyTest(new ImplHuman());
}
public static void proxyTest(Object target) {
// 类加载器
// 接口数组
// 方法回调的接口
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
new Class[]{IAnimal.class, IGod.class}, new MyHumanInvocationHandler(target));
// 此时,通过proxy实例调用被代理对象的方法
IAnimal implProxy = (IAnimal) proxy;
implProxy.eat();
// 此时,调用的是实例了IGod接口的动态代理类实例proxy自身的方法
IGod iGod = (IGod) proxy;
iGod.fly();
}
static class MyHumanInvocationHandler implements InvocationHandler {
/**
* 要代理的具体对象
*/
Object mTarget;
public MyHumanInvocationHandler(Object target) {
mTarget = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用的回调
// proxy:动态生成的代理类实例
// method:代理类实例调用的方法的名字
// args:代理类实例调用的方法的参数
// 可加入额外的操作处理
System.out.println("动态调用前");
Object invoke = null;
// 有被代理的具体实例时,是常用场景
if (mTarget != null) {
if ("eat".equals(method.getName())) {
invoke = method.invoke(mTarget, args);
}
} else {
// 没有被代理的具体实例时,如Retrofit
// 从invoke()可反推出,proxy实例所实现的接口方法中,只是为了传递proxy、method、args
// 是没有具体的业务逻辑实现的
// 可在此对所调用的空方法进行具体的逻辑实现
// 如Retrofit动态代理生成service时:loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
if ("fly".equals(method.getName())) {
invoke = invoke();
}
}
// 可加入额外的操作处理
System.out.println("动态调用后");
// 方法返回值
return invoke;
}
private Object invoke() {
System.out.println("fly");
return null;
}
}
}
动态调用前
eat ImplHuman
动态调用后
动态调用前
fly
动态调用后
3.2.1 优点
- 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码
- 更强的灵活性
- 设计动态代理类(
DynamicProxy
)时,不需要显式实现与目标对象类(RealSubject
)相同的接口,而是将这种实现推迟到程序运行时由JVM
来实现- 在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化
3.2.2 缺点
- 效率低
相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java
反射机制 从而 间接调用目标对象方法 - 应用场景局限
因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类
即只能动态代理 实现了接口的类
3.2.3 应用场景
- 基于静态代理应用场景下,需要代理对象数量较多的情况下使用动态代理
AOP
领域
- 定义:即
Aspect Oriented Programming
= 面向切面编程,是OOP
的延续、函数式编程的一种衍生范型- 作用:通过预编译方式和运行期动态代理实现程序功能的统一维护。
- 优点:降低业务逻辑各部分之间的耦合度 、 提高程序的可重用性 & 提高了开发的效率
- 具体应用场景:日志记录、性能统计、安全控制、异常处理等
3.2.4 cglib动态代理
JDK动态代理是通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类(通过修改字节码来实现代理)。 注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
3.3 JDK的动态代理源码解析
3.3.1 JDK11版本
Proxy.newProxyInstance 会创建一个Class,与静态代理不同,这个Class不是由具体的.java源文件编译而来,即没有真正的文件,只是在内存中按照Class格式生成一个实现了所有传进来的接口的Class,仅此而已。
经过一系列的参数检测与解析后会生成对应的class数组(生成的class数据数组会根据JDK的配置来决定是否输出到本地文件中),再调用native方法将class数据数组转化生成class实例并缓存到内存中的代理缓存。
-
试图从代理缓存中获取代理类的构造器:
-
查看构建者模式的构造过程:
-
native方法声明class实例
-
参数配置决定了生成class数据数组时是否输出对应代理类文件
3.3.2 Android版本
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
/*
* Look up or generate the designated proxy class.
*/
// 生成代理类
Class<?> cl = getProxyClass0(loader, intfs);
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
cons.setAccessible(true);
}
// 让Proxy实例持有InvocationHandl的实例引用
// 进而通过invoke()实现方法调用的信息扩散
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
/*
* Verify that the Class object actually represents an
* interface.
*/
// 接口类型检测,为什么我们说JDK的动态代理只能传入接口的原因
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
...
// 一系列的参数检测与数据解析处理后
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 调用native方法在内存中生成代理类并返回
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
@FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
// END Android-changed: How proxies are generated.