Java 代理是一种设计模式和机制,允许你通过代理对象间接访问目标对象。这种模式在许多场景下非常有用,比如实现方法调用拦截、添加额外功能(如日志、事务管理)、控制访问权限等。
Java 提供了两种主要的代理方式:静态代理和动态代理。
特点:代理类在编译时就已经确定。
实现步骤:
- 定义一个接口。
public interface Singer {
void singing();
int dance();
}
- 创建目标类实现该接口。
public class S implements Singer {
@Override
public void singing() {
System.out.println("s 唱歌");
}
@Override
public int dance() {
System.out.println("tiaowu");
return 0;
}
}
- 创建代理类实现相同接口,并在内部持有目标对象的引用。
- 在代理类的方法中调用目标对象的方法,并可添加额外逻辑。
public class daili implements Singer{
private Singer S = new S();
@Override
public void singing() {
System.out.println("收钱");
S.singing();
}
@Override
public int dance() {
System.out.println("收钱");
S.dance();
return 0;
}
}
2. 动态代理(JDK 动态代理)
特点:代理类在运行时动态生成,无需手动编写。
核心组件:
- InvocationHandler:处理代理对象方法调用的接口,需实现 invoke 方法。
- Proxy:生成代理对象的工具类,通过 Proxy.newProxyInstance() 创建代理。
实现步骤:
- 定义接口和目标类(同静态代理)。
public interface Singer {
void dance();
int singing();
}
public class S implements Singer {
@Override
public void dance() {
System.out.println("S 在跳舞");
}
@Override
public int singing() {
System.out.println("S 在唱歌");
return 0;
}
}
2.创建 InvocationHandler 实现类,在 invoke 方法中添加额外逻辑。
3.使用 Proxy.newProxyInstance() 生成代理对象。
public class Test {
public static void main(String[] args) {
S s =new S();
Ruler ruler = new Ruler(s);
//Proxy.newProxyInstance 是 Java 中用于创建动态代理实例的方法。它有三个参数:
//ClassLoader 获取 类 的类加载器。类加载器用于加载动态代理类的字节码。使用目标类的类加载器可以确保动态代理类与目标类在同一个加载环境中
//Class<?>[] 一个接口类数组,表示动态代理类需要实现的接口
//InvocationHandler InvocationHandler 是一个接口,用于处理代理对象上方法调用的逻辑。
//当通过代理对象调用接口方法时,调用会被转发到 InvocationHandler 的 invoke 方法中,由 ruler 来处理具体的逻辑。
//构建了 以原被代理类为基准的代理类的一个对象
// o 已经等同于 Singer singer = new daili(); 这个singer了
Object o = Proxy.newProxyInstance(S.class.getClassLoader(), new Class[]{Singer.class}, ruler);
System.err.println(o.getClass().toString());
//检查代理对象是否实现了 Singer 接口,然后将其强制转换为 Singer 类型。
if(o instanceof Singer){
Singer singer = (Singer)o;
singer.dance();
System.out.println("调用singing");
singer.singing();
singer.toString();
}
}
}
//额外逻辑
//Ruler 类实现了 InvocationHandler 接口,用于处理代理对象的方法调用。
class Ruler implements InvocationHandler{
private Singer s;
public Ruler(Singer s) {
this.s = s;
}
@Override
public Object invoke(Object p, Method method, Object[] args) throws Throwable {
// System.out.println(p.getClass().toString());
System.out.println("调用前");
//调用目标对象 s 的方法,方法名和参数由 method 和 args 指定。
Object returnVal = method.invoke(s, args);
System.out.println("调用后");
return returnVal;
}
}
3. CGLIB 动态代理特点:无需接口,通过继承目标类生成子类代理。
适用场景:目标类没有实现接口时。
核心组件:
- Enhancer:CGLIB 的核心类,用于生成代理对象。
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println("method1 running ...");
}
@Override
public void method2() {
System.out.println("method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("method3 running ...");
return i;
}
}
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
public class TargetProxy {
public static <T> Object getTarget(T t) {
//新构建了一个 新的 代理类的对象
return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
//比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
System.out.println("执行方法前...");
Object invoke = method.invoke(t, args);
System.out.println("执行方法后...");
return invoke;
}
});
}
}
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
print(target);
}
public static void print( Object target){
Class aClass = target.getClass();
Class cl = target.getClass();
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0)
System.out.print(modifiers + " ");
System.out.print("class:" + cl.getName());
if (supercl != null && supercl != Object.class)
System.out.print("extends:" + supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
System.exit(0);
}
public static void printConstructors(Class cl) {
Constructor[] consturctors = cl.getDeclaredConstructors();
for (Constructor c : consturctors) {
String name = c.getName();
System.out.println(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0)
System.out.print(modifiers + " ");
System.out.print(name + "(");
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0)
System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods) {
Class reType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0)
System.out.print(modifiers + " ");
System.out.print(reType.getName() + " " + name + "(");
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if (j > 0)
System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for (Field f : fields) {
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0)
System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}
4. 三种代理方式对比
特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|---|
代理类生成时机 | 编译时手动编写 | 运行时动态生成 | 运行时动态生成 |
是否需要接口 | 是 | 是 | 否(通过继承实现) |
性能 | 高(无反射) | 中(反射调用) | 高(ASM 字节码增强) |
复杂度 | 高(需手动编写代理类) | 低(反射 API 简洁) | 低(CGLIB API 简洁) |
总结
- 静态代理:简单但维护成本高,适用于代理类较少的场景。
- JDK 动态代理:基于接口,灵活但有局限性。
- CGLIB 动态代理:基于继承,功能强大,适合无接口的类。