什么是代理?
代理其实就是使用一个代理对象去访问目标对象,用来实现对目标对象中功能的增强。我个人的理解就是在使用代理就是在目标对象指定方法的前后去添加其他的功能。
举个例子:现有一个目标类中有一个输出方法,输出“我要开始打游戏了”,那我我可以使用代理对该方法进行增强,增强后可以让其输出:
“我要打开电脑”
“我要开始打游戏了”
“我打完游戏了”
实现代理可以使用静态代理和动态代理两种方式,
静态代理
静态代理的方式就是在我们代码运行前代理类就已经存在,基本上一个目标类就需要一个对应的代理类。
下面来简单实现一个静态代理
首先我们创建一个接口
public interface TargetInteface {
void method1();
void method2();
int method3(Integer i);
}
创建一个目标类实现接口
public class Target implements TargetInteface {
@Override
public void method1() {
System.out.println(" Target method1 running ...");
}
@Override
public void method2() {
System.out.println("Target method2 running ...");
}
@Override
public int method3(Integer i) {
System.out.println("Target method3 running ...");
return i;
}
}
然后我们创建一个代理类,代理类同样实现该接口,并创建目标类对象,在方法中增强其功能。
public class TargetProxy implements TargetInteface {
private Target target =new Target();
@Override
public void method1() {
System.out.println("执行方法前...");
target.method1();
System.out.println("执行方法后...");
}
@Override
public void method2() {
System.out.println("执行方法前...");
target.method2();
System.out.println("执行方法后...");
}
@Override
public int method3(Integer i) {
System.out.println("执行方法前...");
int method3 = target.method3(i);
System.out.println("执行方法后...");
return method3;
}
}
我们尝试写一个主方法调用增强后的方法:
public class TargetUser {
public static void main(String[] args) {
TargetInteface target = new TargetProxy();
target.method1();
System.out.println("-----------------------------");
target.method2();
System.out.println("-----------------------------");
System.out.println(target.method3(3));
}
}
结果如下:
可以发现我们成功对功能进行了增强。
但是我们可以发现这种静态代理的方法有一些弊端,就是我们想要增强一个类,就必须再写一个对应的代理类,当很多类需要被代理时,我们就需要创建很多个代理类,这样就很麻烦。因此我们在使用时一般会使用动态代理。
动态代理
在java中有两种动态代理,分别是jdk的动态代理和Cglib的动态代理
JDK动态代理
jdk的动态代理只要通过反射的机制来实现,会在代码运行时动态地生成代理对象,实现对目标类地增强。
其原理为先创建一个实现了InvocationHandler接口的代理类工厂,该类会新构建了一个新的代理类的对象并将其返回,在这个新的对象中会重写invoke方法,其中传参会有目标对象,想要调用目标对象中的方法以及目标对象方法中参数。通过重写invoke方法,我们添加了对目标类中方法的增强。
随后当我们在调用代理对象中的方法时就会自动触发invoke方法,实现对目标方法的增强。
下面为简单实现jdk动态代理
首先还是先写一个目标类和接口
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);
}
}
可以发现同样实现了增强
Cglib动态代理
CGlib 是一个强大的,高性能的,开源的,广泛使用的 Java 字节码生成库,它扩展了 Java 动态代理功能。相比于 JDK 自带的动态代理,CGlib 动态代理更加灵活,但也更加复杂。
CGlib 主要通过操作字节码实现动态代理,它不要求被代理类实现接口,而是通过继承被代理类并重写方法来实现代理。
使用 CGlib 动态代理的步骤如下:
-
定义一个方法拦截器,实现 MethodInterceptor 接口,并重写 intercept 方法,该方法用于拦截被代理类的方法调用,并在方法调用前后执行额外的逻辑。
-
使用 Enhancer 类创建代理对象,设置被代理类、方法拦截器等属性。
-
调用代理对象的方法,代理对象会自动调用方法拦截器中的 intercept 方法进行方法拦截。
下面来简单实现:
创建一个代理类完成创建Enhancer对象的定义方法拦截器的操纵,这样在使用时直接使用该类即可。
public class TargetProxy {
public static <T> Object getProxy(T t) {
Enhancer en = new Enhancer(); //帮我们生成代理对象
en.setSuperclass(t.getClass());//设置要代理的目标类
en.setCallback(new MethodInterceptor() {//代理要做什么
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("执行方法前。。。");
//调用原有方法
Object invoke = methodProxy.invokeSuper(object, args);
// Object invoke = method.invoke(t, args);// 作用等同与上面。
System.out.println("执行方法后。。。");
return invoke;
}
});
return en.create();
}
}
调用进行测试:
public class TargetUser {
public static void main(String[] args) {
Target target = (Target) TargetProxy.getProxy(new Target());
System.out.println(target.getClass().getName());
target.method1();
print(target);
}
}
成功实现代理: