目录
1.代理模式
2.静态代理
2.1 通过继承实现静态代理
2.2 通过组合实现静态代理
3.动态代理
3.1 JDK动态代理
3.2 cglib动态代理
3.3 SpringAOP使用以及原理
4.原理理解
这段话引用至该文章
静态代理有两种实现方式:
通过继承实现
通过组合实现
通过继承被代理对象,重写被代理方法,可以对其进行代理。
优点:被代理类无需实现接口
缺点:只能代理这个类,要想代理其他类,要想代理其他类需要写新的代理方法。
cglib动态代理就是采用这种方式对类进行代理。不过类是由cglib
帮我们在内存中动态生成的。
public class Tank{
public void move() {
System.out.println("Tank moving cla....");
}
public static void main(String[] args) {
new ProxyTank().move();
}
}
class ProxyTank extends Tank{
@Override
public void move() {
System.out.println("方法执行前...");
super.move();
System.out.println("方法执行后...");
}
}
定义一个 Movable
接口被代理类需要和代理类都需要实现该接口。(接口在这里的目的就是起一个规范作用保证被代理类和代理类都实现了move()
方法)。代理类需要将该接口作为属性,实例化时需要传入该接口的对象,这样该代理类就可以实现代理所有实现Movable
的类了。
优点:可以代理所有实现接口的类。
缺点:被代理的类必须实现接口。
JDK动态代理就是采用的这种方式实现的。同样的代理类是由JDK自动帮我们在内存生成的。
public class Tank implements Movable{
@Override
public void move() {
System.out.println("Tank moving cla....");
}
public static void main(String[] args) {
Tank tank = new Tank();
new LogProxy(tank).move();
}
}
class LogProxy implements Movable{
private Movable movable;
public LogProxy(Movable movable) {
this.movable = movable;
}
@Override
public void move() {
System.out.println("方法执行前....");
movable.move();
System.out.println("方法执行后....");
}
}
interface Movable {
void move();
}
但是与静态代理不同的是,这个代理类我们自己定义的。而动态代理这个代理类是根据我们的提示动态生成的。
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
实现动态代理有几种方案:
通过java提供的Proxy
类帮我们创建代理对象。
优点:可以生成所有实现接口的代理对象
缺点:JDK反射生成代理必须面向接口, 这是由Proxy的内部实现决定的。生成代理的方法中你必须指定实现类的接口,它根据这个接口来实现代理类生成的所实现的接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 使用jdk的动态代理
*/
public class Tank implements Movable{
@Override
public void move() {
System.out.println("Tank moving cla....");
}
public static void main(String[] args) {
Tank tank = new Tank();
// reflection 反射 通过二进制字节码分析类的属性和方法
//newProxyInstance: 创建代理对象
// 参数一: 被代理类对象
// 参数二:接口类对象 被代理对象所实现的接口
// 参数三:调用处理器。 被调用对象的那个方法被调用后该如何处理
Movable o = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader()
,new Class[]{Movable.class}
,new LogProxy(tank));
o.move();
}
}
class LogProxy implements InvocationHandler {
private Movable movable;
public LogProxy(Movable movable) {
this.movable = movable;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法:"+method.getName()+"()执行前");
Object invoke = method.invoke(movable, args); // 此处相当于 movable.move()
System.out.println("方法:"+method.getName()+"()执行后");
return invoke;
}
}
interface Movable {
void move();
}
CGLib(Code Generate Library) 与JDK动态代理不同的是,cglib生成代理是被代理对象的子类。因此它拥有继承方法实现静态代理的优点:不需要被代理对象实现某个接口。
缺点:不能给final
类生成代理,因为final
类无法拥有子类。
使用cglib
生成代理类也很简单,只要指定父类和回调方法即可
首先需要引入依赖
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.2.12version>
dependency>
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 Main {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer(); // 增强者
enhancer.setSuperclass(Tank.class); // 指定父类
enhancer.setCallback(new TimeMethodInterceptor()); // 当被代理对象的方法调用的时候会调用 该对象的intercept
Tank tank = (Tank)enhancer.create(); // 动态代理的生成
tank.move(); // 生成之后会调用
}
}
class TimeMethodInterceptor implements MethodInterceptor{
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("生成的类名"+o.getClass().getName());
System.out.println("生成的类的父类"+o.getClass().getSuperclass().getName());
System.out.println("方法执行前,被代理的方法"+method.getName());
Object result = null;
result = methodProxy.invokeSuper(o, objects);
System.out.println("方法执行后,被代理的方法"+method.getName());
return result;
}
}
class Tank{
public void move(){
System.out.println("Tank moving clacla....");
}
}
关于SpringAOP的使用以及原理可以查看我的这篇文章
SpringAOP使用以及原理
jdk动态代理Proxy原理。
Proxy.newProxyInstance 根据用户传进来的参数调用 asm
来生成Proxy$0
这个代理类。并返回一个实现了接口的代理类,用户实际调用时是调用的代理类的move()
方法。
扩展:asm
提供了一套API,java可以通过它来直接操控内存中的字节码文件即.class文件
。有人说因为有了反射java成了动态语言可以操控class文件,但其实java是因为有了ASM才可以真正算得上动态语言!因为反射只能拿到类的内部信息和执行,但是ASM可以直接在内存中修改class文件。
为了看清Proxy代理到底帮我们生成了什么,可以执行下面这段代码
package com.zxh.proxy;
/**
* Created by zxh on 2022/1/20
*/
import sun.misc.ProxyGenerator;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;
public class Tank implements Movable{
@Override
public void move() {
System.out.println("Tank moving cla....");
try {
Thread.sleep(new Random().nextInt(9000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tank tank = new Tank();
// 1.8以上版本执行这段话
//System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
// 1.8及1.8以下执行这段话 具体的可以查看ProxyGenerator下的saveGeneratedFiles属性
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Movable o = (Movable)Proxy.newProxyInstance(Tank.class.getClassLoader(), new Class[]{Movable.class},new LogHandler(tank) );
o.move();
}
}
class LogHandler implements InvocationHandler{
Movable movable ;
public LogHandler(Movable movable) {
this.movable = movable;
}
// proxy
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method:"+method.getName()+" start.....");
Object invoke = method.invoke(movable, args); // 调用被代理对象 相当于tank.move()
System.out.println("method:"+method.getName()+" end!");
return null;
}
}
interface Movable {
void move();
}
执行成功之后会自动生成一个这个文件
代码没有仔细研究,不过还是可以很明显的看到生成的这个类实现了Movable
方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.zxh.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements Movable {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void move() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.zxh.proxy.Movable").getMethod("move");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}