代理模式
专业的解释:代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
通俗的话讲,就是代理类和被代理类拥有/实现了相同的接口类,并且代理类中持有被代理类的实例对象,被代理类通过代理类来调用自己的方法,并且可以在调用自己方法前后做一些事情。
代理模式又分为静态代理和动态代理,静态代理需要自己编写代理类,并且一个代理类对应一个被代理类,代码繁琐冗余,这里不做多余介绍。
动态代理则是指代理类在程序运行时才被创建的代理模式,动态代理常用的有两种 JDK动态代理、 CGLib动态代理
JDK动态代理
JDK动态代理两个核心类:Proxy类 和 InvocationHandler接口 都位于java.lang.reflect包下
创建动态代理对象的步骤:
1、创建一个InvocationHandler对象(创建一个与代理对象相关联的InvocationHandler)
2、使用Proxy类的getProxyClass静态方法生成一个动态代理类
3、获得stuProxyClass 中一个带InvocationHandler参数的构造器constructor
4、通过构造器constructor来创建一个动态实例stuProxy
public interface ActionIng {
/**
* 执行动作
*/
void doAction();
}
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class StudentIng implements ActionIng {
private String name;
@Override
public void doAction() {
try {
Thread.sleep(1000);
} catch (Exception e) {
System.out.println("嘀嘀嘀嘀哒哒~~~~~~~");
}
System.out.println(name + "正在上课");
}
}
public class StudentInvocationHandler<T> implements InvocationHandler {
T target;
public StudentInvocationHandler(T target) {
this.target = target;
}
/**
* @param proxy 是在代理实例上调用该方法的代理实例
* @param method 是在代理实例上调用的接口方法对应的{@code method}实例。{@code Method}对象的声明类将是该方法被声明的接口,该接口可能是代理类继承该方法所通过的代理接口的超接口。
* @param args 一个对象数组,包含在代理实例上的方法调用中传递的参数值,或者如果接口方法不接受参数,则{@code null}。基本类型的参数被包装在适当的基本包装器类的实例中,例如{@code java.lang。Integer}或{@code java.lang.Boolean}。
* @return @throw Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行" + method.getName() + "方法");
// 其他操作
try {
Thread.sleep(2000);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return method.invoke(target, args);
}
}
public class StaticProxy {
@Test
public void testDynamicProxy() {
StudentIng studentIng = new StudentIng("宋昕冉");
InvocationHandler invocationHandler = new StudentInvocationHandler<ActionIng>(studentIng);
ActionIng proxyInstance = (ActionIng) Proxy.newProxyInstance(ActionIng.class.getClassLoader(), new Class<?>[]{ActionIng.class}, invocationHandler);
proxyInstance.doAction();
}
}
JDK1.8 Proxy中的newProxyInstance()方法
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
JDK动态代理为什么只能代理接口,因为生成的代理类$Proxy0 extends Proxy implements 代理接口,代理类继承了Proxy类,Java的继承机制(单继承)注定了JDK动态代理只能代理接口。
CGLib动态代理
CGLib动态代理是通过代理目标类的,通过代理类继承目标类,然后重写目标类的方法。核心类是Enhancer类和MethodInterceptor接口,后者是方法增强的地方,类似于InvocationHandler接口
@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
@NoArgsConstructor
public class Worker {
private String name;
private Integer age;
private String sex;
public void work(String name, Integer age, String sex) {
System.out.println("I am working, name is: " + name + ", age is: " + age + ", sex is: " + sex);
}
public final void eat() {
System.out.println("I am eating when I work");
}
}
public class WorkerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("对方法进行增强----------CGLib");
try {
Thread.sleep(2000);
System.out.println("增强方法执行完毕");
} catch (Exception e) {
System.out.println("方法执行异常");
}
return methodProxy.invokeSuper(o, args);
}
}
@Test
public void testCGLibProxy() {
// 设置生成的动态代理类保存路径
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\javaProjects");
// 创建Enhancer对象 类似JDK动态代理下的Proxy类
Enhancer enhancer = new Enhancer();
// 设置被代理类的字节码文件
enhancer.setSuperclass(Worker.class);
// 设置回调函数
enhancer.setCallback(new WorkerMethodInterceptor());
// 创建代理对象
Worker workerProxy = (Worker) enhancer.create();
// 调用代理对象的方法
workerProxy.work("宋昕冉", 25, "女");
workerProxy.eat();
}



第一步:是经过一系列操作(new一个Enhancer对象,然后设置被代理类的字节码文件,enhancer.setSuperClass(被代理的class对象;设置回调函数,enhancer.setCallback(new一个实现了MethodInterceptor的方法拦截器)))实例化出了Enhance对象,并设置了所需要的参数然后enhancer.create()成功创建出来了代理对象
第二步:调用代理对象的方法,会进入到方法拦截器的intercept()方法,在这个方法中会调用proxy.invokeSuper(obj, args);方法
第三步:invokeSuper中,通过FastClass机制调用目标类的方法
方法拦截器中只有一个invoke方法,这个方法有四个参数,obj表示代理对象,method表示目标类中的方法,args表示方法参数,proxy表示代理方法的MethodProxy对象


总而言之,CGLib很好的补充了JDK动态代理只能代理接口的限制,我们在项目中可以酌情使用,而目前SpringBoot 2.x版本默认使用的动态代理就是CGLib
需要补充一点,CGLib无法代理被代理类中使用final修饰的方法,但不是说代理类就不能调用该方法,而是CGlib生成的子类会直接继承这个方法不会做任何修改或者代理,也就是不会被拦截不会被增强,final方法在被调用的时候会直接执行被代理类中的final方法,而不是通过代理机制去调用。

浙公网安备 33010602011771号