代理简单来说就相当于中介。比如房产中介,我们想买房时,由于自身精力、时间等不能每个房源都去现场看,所以需要代理来帮我们从海量房源中筛选出来合适我们自己的房源,提供给我们。同样在代码中,我们每个类都最好做单一的事,比如在代码逻辑中加入日志、统计方法运行时间等逻辑,最好由其他类来实现。
代理的主要作用就是增强原有代码的功能。应用场景包括: 代码增加日志,统计方法运行时间,运行前后做代码增强操作等。
静态代理是代理模式中的一种,静态跟动态相对,静态是指代理类在编译阶段生成,在程序运行之前就已经存在了,动态代理则是在运行期生成代理类。
静态代理实现:
目标类和代理类实现了相同的接口,在代理类中依赖了目标类,代理类的方法中调用了目标类的方法,并做了一些增强性的工作。
接口
package com.ljx.splearn.Proxy;
public interface Calculate {
int add(int num1,int num2);
}
实现类(被代理类)
package com.ljx.splearn.Proxy;
public class Calculator implements Calculate {
@Override
public int add(int num1, int num2) {
System.out.println( "两数之和=" + num1 + num2);
return num1 + num2;
}
}
代理类
package com.ljx.splearn.Proxy;
public class CalculatorProxy implements Calculate {
private Calculator calculator;
public CalculatorProxy(Calculator calculator) {
this.calculator = calculator;
}
@Override
public int add(int num1, int num2) {
System.out.println("add前置操作");
int sum = calculator.add(num1, num2);
System.out.println("add后置操作");
return sum;
}
}
public static void main(String[] args) {
Calculator c = new Calculator();
CalculatorProxy calculatorProxy = new CalculatorProxy(c);
calculatorProxy.add(1,2);
}
调用方法时不使用目标类,而是使用代理类来进行调用,增强操作可以替换成其他代码或方法
add前置操作
两数之和=12
add后置操作
使用静态代理缺点是需要为每一个被代理的接口或类都编写一个代理类,工作量太大。
jdk动态代理是基于Java的反射机制实现的,使用jdk反射包下的Proxy和InvocationHandler实现代理对象的动态创建
(jdk动态代理要求目标对象必须实现接口)jdk动态代理实现原理是动态创建接口类的实现类,所以有此要求。实现过程类似上述的静态代理,不过是代码自动创建
package com.ljx.splearn.Proxy;
public interface PrintLog {
void printLog();
}
package com.ljx.splearn.Proxy;
/**
* @author lijianxi
* @date 2023年01月31日 10:09 上午
*/
public class PrintLogimpl implements PrintLog{
@Override
public void printLog() {
System.out.println("方法开始执行");
}
}
package com.ljx.splearn.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler {
private Object target = null;
//动态代理,目标对象是活动的,不是固定的,需要传入进来,是object类型所以可以传任何对象
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long t1 = System.currentTimeMillis();
Object res = method.invoke(target, args);
long t2 = System.currentTimeMillis();
System.out.println("方法运行时间是"+ (t2 - t1)+"ms");
return res;
}
public static void main(String[] args) {
//1、创建目标对象
Calculator calculator = new Calculator();
//2、创建invocationHandler对象
MyInvocationHandler invocationHandler = new MyInvocationHandler(calculator);
//3、获取代理对象,强转后只能是接口类型不能是实现类
Calculate proxyInstance = (Calculate)invocationHandler.getProxy();
//通过代理对象调用目标对象方法
proxyInstance.add(1, 3);
// 代理其他接口
//1、创建目标对象
PrintLogimpl p = new PrintLogimpl();
//2、创建invocationHandler对象
MyInvocationHandler invocationHandler1 = new MyInvocationHandler(p);
//3、获取代理对象,强转后只能是接口类型不能是实现类
PrintLog proxyInstance1 = (PrintLog)invocationHandler1.getProxy();
//通过代理对象调用目标对象方法
proxyInstance1.printLog();
}
}
两数之和=13
方法运行时间是1ms
方法开始执行
方法运行时间是0ms
cglib的原理是通过字节码
技术为一个类创建子类
,并在子类中采用方法拦截
的技术拦截所有父类方法的调用
。由于是通过创建子类来代理父类
,因此不能代理被final
修饰的类(代理final
修饰的类会抛异常,代理final
修饰的方法只会原样执行委托类的方法而不能做任何拦截)。
package com.ljx.splearn.Proxy;
/**
* @author lijianxi
* @date 2023年01月31日 10:48 上午
*/
public class Person {
public void sayHello(){
System.out.println("你好");
}
}
package com.ljx.splearn.Proxy;
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 Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强代码!");
methodProxy.invokeSuper(o, objects);
System.out.println("后置增强代码");
return null;
}
});
Person person = (Person) enhancer.create();
person.sayHello();
}
}
需要引入依赖
cglib
cglib
3.1
前置增强代码!
你好
后置增强代码