Java的代理机制在很多常用的框架中都有应用,如Spring AOP。Java的代理主要作用是在不修改别人的代码基础上,完成功能的扩展。Java中包含三种代理模式,分别是静态代理、JDK动态代理、Cglib代理
静态代理
静态代理的特点是:委托类和代理类实现实现相同的接口,代理类包含委托类类型的属性。实际例子如下:
接口:
public interface Interface {
public void test();
}
委托类:
public class Subject implements Interface{
@Override
public void test() {
System.out.println("代理中.....");
}
}
代理类:
public class MyProxy implements Interface{
private Subject subject;
public MyProxy(Subject subject){
this.subject = subject;
}
@Override
public void test() {
System.out.println("代理之前......");
subject.test();
System.out.println("代理之后......");
}
}
测试类:
public class Client {
public static void main(String[] args) {
Subject subject = new Subject();
MyProxy proxy = new MyProxy(subject);
proxy.test();//实际调用委托类的test方法
}
}
静态代理的缺点:
- 代理类和委托类需要实现相同的接口,这种一对一的关系导致当委托类很多的时候,代理类也会很多;
- 如果接口类增加或减少方法时,委托类和代理类都需要修改
JDK动态代理
JDK动态代理的特点:我们不需要定义上面的代理类,即MyProxy类,只需调用JDK中的API来动态的生成该类。API是java.lang.reflect.Proxy的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler)
- ClassLoader loader:委托类对象的类加载器,即上面的Subject.class.getClassLoader()
- Class<?>[] interfaces:委托类对象实现的接口类型,即上面的Interface.class.getInterfaces(),如果委托类实现多个接口,可以穿入多个接口类型(new Class[]{InterfaceA.class,InterfaceB.class}),这样可代理多个接口中的任何一个方法
- InvocationHandler handler:事件处理,调用委托类的方法时,会执行该对象中的invoke(Object proxy,Method method,Object[] args)方法,并将委托类方法的参数传入
示例如下:
接口:
public interface Interface {
public void test();
}
委托类:
public class Subject implements Interface{
@Override
public void test() {
System.out.println("代理中.....");
}
}
处理类:
public class MyInvocationHandler implements InvocationHandler {
private Subject subject;
public MyInvocationHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行方法------" + method.getName());
Object result = method.invoke(subject,args);
System.out.println("执行结束....");
return result;
}
}
测试类:
public class Client {
public static void main(String[] args) {
Subject subject = new Subject();
MyInvocationHandler handler = new MyInvocationHandler(subject);
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[]{Interface.class},handler);
proxy.test();//实际调用hangler的invoke方法,proxy对象是类型是com.sun.proxy.Proxy$0
}
}
优点:代理对象动态生成,不需要实现接口;只需委托类实现接口即可;不许定义那么多的代理类
动态代理原理
先来看看Proxy.newProxyInstance方法源码:
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);
}
//constructorParams是Proxy中的静态变量Class[] constructorParams = {InvocationHandler.class}
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);
}
}
代理类是动态,可以用过工具类打印出来看看动态生成的代理类是怎样的:
import com.heyu.shiro.proxy.Interface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Interface
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);//调用java.lang.reflect.Proxy的构造方法,并初始化属性
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void test()
throws
{
try
{
this.h.invoke(this, m3, null);//继承父类java.lang.reflect.Proxy的属性InvocationHandler,并调用InvocationHandler的invoke方法
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//该静态块完成代理类属性的初始化,发现除了java.lang,Object中的方法,还重写了Interface接口中的test方法
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.heyu.shiro.proxy.Interface").getMethod("test", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
动态的生成$Proxy0这个类,并通过反射机制创建 该类的对象,并调用该类重写接口的test()方法,方法内部实际调用InvocationHandler的invoke方法
Cglib代理
JDK的动态代理要求委托类要实现接口,但是很多时候我们并不需要接口,这个时候JDK动态代理就没法使用了。Cglib就派上用场了。先上实例:
委托类:
public class Subject {
public String test() {
System.out.println("代理中.....");
return "hello";
}
}
处理类:
public class CgPorxy implements MethodInterceptor {
public Object getProxy(Class clazz){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);//设置父类
enhancer.setCallback(this);//设置回调
return enhancer.create();//生成代理类对象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理开始....");
Object result = methodProxy.invokeSuper(o,objects);//会调用代理类对象中的方法,如果方法有返回值,会返回
System.out.println("代理结束....");
return result;
}
}
测试类:
public class Client {
public static void main(String[] args) {
CgPorxy porxy = new CgPorxy();//创建处理类
Subject subject = (Subject) porxy.getProxy(Subject.class);
System.out.println(subject.test());
}
}
cglib生成的代理类的内容:
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class Subject$$EnhancerByCGLIB$$a539bb91
extends Subject
implements Factory
{
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;//拦截器
private static Object CGLIB$CALLBACK_FILTER;//被代理方法
private static final Method CGLIB$test$0$Method;//被代理方法
private static final MethodProxy CGLIB$test$0$Proxy;//代理方法
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
static void CGLIB$STATICHOOK1()
{
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("com.heyu.shiro.proxy.Subject$$EnhancerByCGLIB$$a539bb91");//代理类
Class localClass2;//被代理类
Method[] tmp83_80 = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$1$Method = tmp83_80[0];
CGLIB$equals$1$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
Method[] tmp103_83 = tmp83_80;
CGLIB$toString$2$Method = tmp103_83[1];
CGLIB$toString$2$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
Method[] tmp123_103 = tmp103_83;
CGLIB$hashCode$3$Method = tmp123_103[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$3");
Method[] tmp143_123 = tmp123_103;
CGLIB$clone$4$Method = tmp143_123[3];
CGLIB$clone$4$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
tmp143_123;
Method[] tmp191_188 = ReflectUtils.findMethods(new String[] { "test", "()Ljava/lang/String;" }, (localClass2 = Class.forName("com.heyu.shiro.proxy.Subject")).getDeclaredMethods());
CGLIB$test$0$Method = tmp191_188[0];//被代理方法
CGLIB$test$0$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "test", "CGLIB$test$0");
tmp191_188;
}
通过源码发现,道理类所有从父类继承的方法,都会有MethodProxy与之对应。例如:CGLIB$test$0$Method、CGLIB$test$0$Proxy,再来看看代理类的test()方法
final String CGLIB$test$0()
{
return super.test();
}
public final String test()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;//这个变量实质是我们的CgProxy类
if (tmp17_14 != null) {
return (String)tmp17_14.intercept(this, CGLIB$test$0$Method, CGLIB$emptyArgs, CGLIB$test$0$Proxy);//执行CgProxy中的intercept方法
}
return super.test();
}
methodInterceptor.intercept()方法会调用上面的CGLIB$test$0()方法,及调用父类的test方法。
JDK动态代理和Cglib代理的区别:
- JDK动态代理是动态生成实现接口的类,并实现接口中的方法,方法内容是调用InvocationHandler的invoke方法。
- Cglib代理是动态生成委托类的子类,并重写父类方法,内容是调用自定义的实现接口MethodInterceptor的intercept方法,methodProxy.invokeSuper()方法会调用父类的方法。
注意:因为Cglib代理会重写父类的方法,因此代理方法不能是static 或final的,会导致无法代理;Cglib会动态生成委托类的子类,因此委托类不能是final的
Spring Aop
spring中的切面编程使我们常用的功能,spring aop支持JDK动态代理,支持Cglib代理。如果委托类实现了接口,那么采用JDK动态代理;如果没有实现接口,那么采用Cglib代理模式。
Spring aop Cglib动态代理
测试Spring Aop,先看委托类不实现接口
定义切面:
@Aspect
@Component
public class TestAspect {
@Pointcut("execution(* com.example.demo.aspact.Proxy.*(..))")
public void point(){}
@Before("point()")
public void test(){
System.out.println("执行方法之前");
}
}
定义委托类:
@Component
public class Proxy {
public void test(){
System.out.println("========");
}
}
定义测试类:
@Component
public class BaseService {
@Autowired
private Proxy proxy;
public void service(){
System.out.println(proxy.getClass());
proxy.test();
}
}
定义单元测试启动项目:
@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
@Autowired
private BaseService baseService;
@Test
public void contextLoads() {
baseService.service();
}
}
运行单元测试结果如下:
class com.example.demo.aspact.Proxy$$EnhancerBySpringCGLIB$$599e29ff
执行方法之前
========
从结果看proxy.getClass()输出的是Cglib生成的动态子类;证明委托类没有实现接口时采用的是Cglib动态代理
Spring aop JDK动态代理
如果使用springboot开发,会默认使用Cglib动态代理,我们需要先修改配置。在appplication.properties中:
spring.aop.proxy-target-class=false //默认为true,为true时会使用Cglib动态代理
在增加接口:
public interface Interface {
public void test();
}
修改委托类如下:
@Component
public class Proxy implements Interface{
@Override
public void test(){
System.out.println("========");
}
}
测试类修改如下:
@Component
public class BaseService {
@Autowired
private Interface proxy;//修改为Interface类型,否则会报错
public void service(){
System.out.println(proxy.getClass());
proxy.test();
}
}
测试结果如下:
class com.sun.proxy.$Proxy61
执行方法之前
========
发现此时使用动态代理