静态代理与动态代理
静态代理:代理类在代码运行之前已经生成
动态代理:代理类对象是代码动态生成的(反射)
java中实现动态代理主要有两种方式,jdk动态代理与cglib动态代理。
jdk动态代理依靠实现类的接口实现,因此被代理的类必须要实现某个接口。
cglib依靠继承被代理的类的子类实现,因此被代理的类必须不能是final类型的类,且被代理的方法也不能是final类型方法,如果被代理的方法为final方法,则代理方法失效,直接运行原始方法。
spring中动态代理是依靠两者同时实现的,一般情况下spring会优先使用jdk动态代理,如果发现被代理的类不能使用jdk动态代理则使用cglib动态代理,当然spring配置时也可以强制使用cglib动态代理,配置方法:
<aop:config proxy-target-class="true">
使用jdk动态代理
首先定义一个被代理类
public interface IHello
{
public void say();
}
定义一个被代理类的实现
public class Hello implements IHello
{
private String name;
public Hello(String name)
{
super();
this.name = name;
}
public void say()
{
System.out.println("hello:" + name);
}
}
jdk动态代理类
public class MyInvocationHandler implements InvocationHandler
{
private Object target;
public MyInvocationHandler(Object target)
{
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
System.out.println("------------------before------------------");
// 执行目标对象的方法
Object result = method.invoke(target, args);
// 在目标对象的方法执行之后简单的打印一下
System.out.println("-------------------after------------------");
return result;
}
}
测试方法:
@org.junit.Test
public void test()
{
MyInvocationHandler invocationHandler = new MyInvocationHandler(new Hello("csdn"));
IHello hello = (IHello)Proxy.newProxyInstance(AppTest.class.getClassLoader(),
new Class[] {IHello.class},
invocationHandler);
hello.say();
}
以上就是jdk动态代理的一个简单的实例,要实现jdk动态代理需要被代理类实现一个接口,动态代理类其实是实现了这个接口的一个代理类,会调用代理类和被代理类的方法
cglib实现动态代理:
由于cglib不需要依靠接口实现,而是直接继承被代理类,因此不需要定义接口
被代理类:
public class Hello
{
private String name;
public Hello(String name)
{
super();
this.name = name;
}
public void say()
{
System.out.println("hello:" + name);
}
}
动态代理类:
public class CglibProxy implements MethodInterceptor
{
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable
{
// 添加切面逻辑(advise),此处是在目标类代码执行之前,即为MethodBeforeAdviceInterceptor。
System.out.println("------------------before------------------");
// 执行目标类add方法
Object object = proxy.invokeSuper(obj, args);
// 添加切面逻辑(advise),此处是在目标类代码执行之后,即为MethodAfterAdviceInterceptor。
System.out.println("------------------after------------------");
return object;
}
}
测试方法:
@org.junit.Test
public void testCglib()
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
// 回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法
enhancer.setCallback(new CglibProxy());
// 此刻,hello不是单纯的目标类,而是增强过的目标类
Hello hello = (Hello)enhancer.create(new Class[] {String.class}, new Object[] {"csdn"});
hello.say();
}
输出:
------------------before------------------
hello:csdn
------------------after------------------
cglib实现动态代理时,可以使用CallbackFilter来实现过滤,使得不同的方法使用不同的回调
修改Hello.java
public class Hello
{
private String name;
public Hello(String name)
{
super();
this.name = name;
}
public void say()
{
System.out.println("hello:" + name);
}
public void sayNotProxy()
{
System.out.println("not proxy hello:" + name);
}
}
实现一个CallbackFilter类
public class CglibCallbackFilter implements CallbackFilter
{
@Override
public int accept(Method method)
{
if ("say".equals(method.getName()))
{
return 1;
}
return 0;
}
}
测试方法:
@org.junit.Test
public void testCglib()
{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
// 设置回调filer,accept方法返回的值即为代理方法使用的回调类型
enhancer.setCallbacks(new Callback[] {NoOp.INSTANCE, new CglibProxy()});
enhancer.setCallbackFilter(new CglibCallbackFilter());
// 此刻,hello不是单纯的目标类,而是增强过的目标类
Hello hello = (Hello)enhancer.create(new Class[] {String.class}, new Object[] {"csdn"});
hello.say();
hello.sayNotProxy();
}
输出结果:
------------------before------------------
hello:csdn
------------------after------------------
not proxy hello:csdn
可见方法sayNotProxy并没有使用代理方法,回调类型的定义如下:
net.sf.cglib.proxy.FixedValue:在强制一个特定方法返回固定值,在特定场景下非常有用且性能高。
net.sf.cglib.proxy.NoOp:它直接透传到父类的方法实现。
net.sf.cglib.proxy.LazyLoader:在被代理对象需要懒加载场景下非常有用,如果被代理对象加载完成,那么在以后的代理调用时会重复使用。
net.sf.cglib.proxy.Dispatcher:与net.sf.cglib.proxy.LazyLoader差不多,但每次调用代理方法时都会调用loadObject方法来加载被代理对象。
net.sf.cglib.proxy.ProxyRefDispatcher:与Dispatcher相同,但它的loadObject方法支持传入代理对象。
关于cglib动态代理还需要注意一件事,网上很多地方说cglib动态代理时必须要有无参构造,其实这个说法是错误的,只要在创建代理时,指明参数类型与参数值,其实是可以动态生成没有无参构造类的代理对象的。
关于两者的性能比较,测试代码如下:
public class AppTest
{
private int preRunCount = 100000;
private int runCount = 1000;
@org.junit.Test
public void test()
throws InterruptedException
{
System.gc();
Thread.sleep(2000);
MyInvocationHandler invocationHandler = new MyInvocationHandler(new Hello());
IHello hello = (IHello)Proxy.newProxyInstance(AppTest.class.getClassLoader(),
new Class[] {IHello.class},
invocationHandler);
for (int i = 0; i < preRunCount; i++)
{
hello.say();
}
long start = System.currentTimeMillis();
for (int i = 0; i < runCount; i++)
{
hello.say();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
@org.junit.Test
public void testCglib()
throws InterruptedException
{
System.gc();
Thread.sleep(2000);
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Hello.class);
enhancer.setCallback(new CglibProxy());
Hello hello = (Hello)enhancer.create();
for (int i = 0; i < preRunCount; i++)
{
hello.say();
}
long start = System.currentTimeMillis();
for (int i = 0; i < runCount; i++)
{
hello.say();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
两者测试比较时jdk动态代理创建对象时的确比cglib快很多,但两者执行时的运行效率并没有像网上说的cglib运行效率是jdk动态代理的十倍,实际运行时两者的运行效率差不太多,有时jdk的运行效率反而比cglib代理时的要快