目录
说到代理我们要先知道代理的分类
代理大方向分为两类:
Ⅰ静态代理
Ⅱ动态代理
jdk动态代理
cglib动态代理
下面我们来看看各种代理之间的区别和实现方式吧
静态代理
静态代理的实现方法:
静态代理的实际应用:
动态代理
JDK动态代理
实现原理:
静态代理是一种代理模式,它通过手动编写代理类的代码,将被代理对象的方法调用委托给代理类,从而实现对被代理对象的控制。静态代理的实现方法包括两个关键角色:代理类和被代理类。
定义接口: 定义一个接口,包含被代理对象和代理对象都需要实现的方法。
public interface Subject {
void request();
}
实现被代理类: 被代理类实现接口,提供具体的业务逻辑。
public class RealSubject implements Subject {
@Override
public void request(){
System.out.println("RealSubject: Handling request.");
}
}
实现代理类: 代理类也实现接口,并在其中包含一个对被代理对象的引用,代理类中的方法实际上是对被代理对象方法的调用,可以在调用前后添加额外的逻辑。
public class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
System.out.println("ProxySubject: Before request.");
realSubject.request();
System.out.println("ProxySubject: After request.");
}
}
使用代理: 在客户端代码中使用代理对象。
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxy = new ProxySubject(realSubject);
proxy.request();
}
}
使用 java.lang.reflect.Proxy
类和 java.lang.reflect.
接口。
在运行时动态生成一个实现被代理接口的代理类。
通过 InvocationHandler
接口的 invoke
方法进行方法的调用处理。
被代理类必须实现接口。
适用于横切关注点与业务逻辑之间耦合较弱的情况。
1.定义一个被代理类
/**
* @author tong
*/
public class Calculator {
public int add(int i, int j) {
int result = i + j;
System.out.println("方法内部 result = " + result);
return result;
}
public int sub(int i, int j) {
int result = i - j;
System.out.println("方法内部 result = " + result);
return result;
}
public int mul(int i, int j) {
int result = i * j;
System.out.println("方法内部 result = " + result);
return result;
}
public int div(int i, int j) {
int result = i / j;
System.out.println("方法内部 result = " + result);
return result;
}
}
2.生产代理对象的工厂类 ProxyFactory:
/**
* 动态代理有两种:
* 1. jdk动态代理,要求必须有接口,最终生成的代理类和目标类实现相同的接口,在com.sun.proxy包下,类名为$proxy+数字 (例如:$proxy6)
* 2. cglib动态代理,最终生成的代理类会继承目标类,并且和目标类在相同的包下
*/
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 通过该方法可以生成任意目标类所对应的代理类
public Object getProxy(){
/**
* newProxyInstance():创建一个代理实例
* 其中有三个参数:
* 1、classLoader:指定加载动态生成的代理类的类加载器(注:所有引入的第三方类库以及
自己编写的java类 都是由 应用类加载器 负责加载的)
【根类加载器(Bootstrap)> 扩展类加载器(Extension)> 系统类加载器(System)
系统类加载器又称为应用类加载器】
* 2、interfaces:获取目标对象实现的所有接口的class对象所组成的数组
* 3、invocationHandler:设置代理对象实现目标对象的接口的方法的过程,即代理类中如何
重写接口中的抽象方法
*/
//第一个参数,获取代理对象的类加载器 (类加载器是程序中默认的类加载器,一般来说,Java
应用的类都是由它来完成加载。所以,此处通过代理类或者被代理类获取到的类加载器都是同一个,
或通过任何一个类获取到的类加载器都是同一个。)
ClassLoader classLoader = this.getClass().getClassLoader();
//第二个参数,被代理对象实现的所有接口数组
Class>[] interfaces = target.getClass().getInterfaces();
//当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
//代理实例的调用处理程序
//第三个参数InvocationHandler的实现类,这里用了匿名内部类的方式
InvocationHandler invocationHandler = new InvocationHandler() {
//重写InvocationHandler的invoke方法,他有三个参数可以供我们使用
@Override
public Object invoke(Object proxy, Method method, Object[] args){
/**
* proxy:表示代理对象
* method:表示要执行的方法(代理对象需要实现的抽象方法,即其中需要重写的和
目标类同名的方法)
* args:表示要执行的方法的参数列表(method所对应方法的参数列表)
*/
Object result = null;
try {
//method.getName(): 返回此method对象表示的方法的名称,作为字符串
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
//通过反射调用method对象所表示的方法,并获取该方法的返回值
//在具有指定参数的指定对象上调用此method对象表示的底层方法。
//此处就是通过target来确定调用的是具体哪个类中的方法
result = method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结 果:"+ result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
} finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
}
return result;
}
};
//返回指定接口的代理类的实例,该实例将方法调用分派给指定的调用处理程序。
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
}
}
3.测试类ProxyTest
package com.tong.proxy;
import com.tong.spring.calculator.Calculator;
import com.tong.spring.calculator.proxy.ProxyFactory;
import org.junit.Test;
/**
* @author tong
*/
public class ProxyTest {
@Test
public void testDynamicProxy(){
ProxyFactory factory = new ProxyFactory(new Calculator());
Calculator proxy = factory.getProxy();
/*创建好了代理对象,代理对象就可以执行被代理类实现的接口的方法;当通过代理类的对象
发起对被重写的方法的调用时,都会转换为对调用处理器实现类中的invoke方法的调用,
invoke方法中就可以对被代理类进行功能增强.*/
proxy.add(1, 5);
// proxy.div(1,0);
}
}
使用第三方库,通过修改字节码生成目标类的子类。
在子类中对方法进行增强
由于生成的是类的子类,因此可以代理没有实现接口的类。
被代理类不需要实现接口。
适用于横切关注点与业务逻辑之间耦合较紧密的情况。
1.创建被代理类
package com.tong;
/**
* @author tong
* @Description:被代理类
*/
public class Star {
public void sing() {
System.out.println("唱.......");
}
public void dance() {
System.out.println("跳......");
}
}
2.生成代理工厂
package com.tong;
import com.oracle.jrockit.jfr.Producer;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @author tong
* @Description:生成代理类的工厂
*/
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 通过该方法可以生成任意目标类所对应的代理类
public static Object getProxy(Object target) {
// proxy就是我们创建的代理对象,这个对象可以执行被代理类中所有的方法,并且我们可以
在代理对象中对被代理类的方法进行增强
Object proxy = Enhancer.create(target.getClass(), new MethodInterceptor() {
/**
* 这一步是整个过程的关键,代理类的实现要通过Enhancer类,我们需要通过Enhancer
类中的create方法创建一个代理对象
* @param o 是一个代理对象的引用 (即:增强对象)
* @param method 是当前执行,即被拦截的被代理类方法
* @param objects 是当前执行方法所用的参数,索引顺序即为方法定义时参数的顺序
* @param methodProxy 指的是当前执行的方法的代理对象
* @return 通过反射调用method对象所表示的方法, 并获取该方法的返回值
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) {
Object result = null;
try {
// 提供增强代码
System.out.println("[cglib动态代理][日志] " + method.getName() +"
,参数:" + Arrays.toString(objects));
//通过反射调用method对象所表示的方法,并获取该方法的返回值
//在具有指定参数的指定对象上调用此method对象表示的底层方法。
//此处就是通过target来确定调用的是具体哪个类中的方法
result = method.invoke(target, objects);
System.out.println("[cglib动态代理][日志] " + method.getName() + ",
结果
:" + result);
} catch (Exception e) {
e.printStackTrace();
System.out.println("[cglib动态代理][日志] " + method.getName() + ",
异常:
" + e.getMessage());
} finally {
System.out.println("[cglib动态代理][日志] " + method.getName() + ",
方法执行完毕");
}
return result;
}
});
// 返回代理对象
return proxy;
}
}
3.生成测试类
package com.tong;
import org.junit.Test;
/**
* @author tong
* @Description:测试类
*/
public class ProxyTest {
@Test
public void test(){
Star star = new Star();
/* proxy就是我们创建的代理对象,这个对象可以执行被代理类中所有的方法,
并且我们可以在代理对象中对被代理类的方法进行增强,
注意这里使用了强转,因为getProxy方法的返回值是Object类型的对象*/
Star proxy = (Star)ProxyFactory.getProxy(star);
proxy.sing();
System.out.println("-----------------分割线------------------");
proxy.dance();
System.out.println("-----------------分割线------------------");
}
}
4.运行结果
①JDK 动态代理是基于接口的代理,生成的代理类实现了被代理接口。
②CGLIB 动态代理是通过生成目标类的子类,代理类继承了被代理类。
①JDK 动态代理适用于被代理类实现了接口的情况。
②CGLIB 动态代理适用于被代理类没有实现接口的情况。
①JDK 动态代理的性能较好,因为它是基于接口的代理,直接使用 java.lang.reflect 包中的方法。
②CGLIB 动态代理生成的字节码较大,可能在性能上略逊于 JDK 动态代理,但在某些场景下性能差异并不明显。
①JDK 动态代理需要实现 InvocationHandler 接口,并使用 Proxy.newProxyInstance 方法创建代理对象。
②CGLIB 动态代理直接使用第三方库,不需要实现接口,通过 Enhancer.create 方法创建代理对象。
总体来说,选择使用 JDK 动态代理还是 CGLIB 动态代理取决于具体的业务场景和需求。如果被代理类已经实现接口,推荐使用 JDK 动态代理;如果被代理类没有实现接口或者想要在运行时对类进行增强,可以考虑使用 CGLIB 动态代理。
①静态代理: 手动编写代理类的代码。
②动态代理: 在运行时生成代理类的字节码,无需手动编写代理类。
①静态代理: 代理类在编译时就确定,不够灵活。
②动态代理: 代理类在运行时生成,可以根据需要动态地创建代理类。
①静态代理: 每添加一个被代理类,都需要手动编写一个对应的代理类。
②动态代理: 通过动态生成代理类,可以更容易地维护和扩展。
①静态代理: 在编译时就确定了代理类,运行时性能相对较好。
②动态代理: 生成代理类的过程可能会带来一些性能开销。
动态代理常用的实现方式有基于java.lang.reflect.Proxy和基于CGLIB的动态代理。在实际应用中,根据需要选择静态代理或动态代理。