java中的代理按照代理类的生成时期不同分为静态代理和动态代理。
(1)静态代理。由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。下面是静态代理的UML图。
具体的代码如下
- 1. interface Subject
- 2. {
- 3. void request();
- 4. }
- 5. class RealSubject implements Subject
- 6. {
- 7. public void request()
- 8. {
- 9. System.out.println("真实的请求");
- 10. }
- 11. }
- 12. public class Proxy implements Subject
- 13. {
- 14. RealSubject realSubject;
- 15. public void request()
- 16. {
- 17. if(realSubject==null)
- 18. {
- 19. realSubject=new RealSubject();
- 20. }
- 21. realSubject.request();
- 22. }
- 23.
- 24. public static void main(String[] args) {
- 25. new Proxy().request();
- 26. }
- 27. }
(2)动态代理。动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性。想必做java web开发的,会经常用到Spring,而Spring的2大核心之一是Spring AOP,Spring AOP就是利用动态代理实的(Spring利用动态代理技术实现的最重要的一个功能就是声明式事务)。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。那么接下来就利用动态代理实现上面的例子
- 1. import java.lang.reflect.InvocationHandler;
- 2. import java.lang.reflect.Method;
- 3. import java.lang.reflect.Proxy;
- 4.
- 5. interface Subject {
- 6. void request();
- 7. }
- 8.
- 9. class RealSubject implements Subject {
- 10. public void request() {
- 11. System.out.println("真实的请求");
- 12. }
- 13. }
- 14.
- 15. public class DynProxy implements InvocationHandler {
- 16.
- 17. private Object dele;
- 18.
- 19. public DynProxy(Object obj) {
- 20. this.dele = obj;
- 21. }
- 22.
- 23. public Object invoke(Object proxy, Method method, Object[] args)
- 24. throws Throwable {
- 25. doBefore();
- 26. // 在这里完全可以把下面这句注释掉,而做一些其它的事情
- 27. Object result = method.invoke(dele, args);
- 28. after();
- 29. return result;
- 30. }
- 31.
- 32. private void doBefore() {
- 33. System.out.println("before....");
- 34. }
- 35.
- 36. private void after() {
- 37. System.out.println("after....");
- 38. }
- 39.
- 40. public static void main(String[] args) {
- 41. RealSubject realSubject= new RealSubject();
- 42. DynProxy dynProxy= new DynProxy(realSubject);
- 43. Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), dynProxy);
- 44. subject.request();
- 45. }
- 46. }
这是JDK的动态代理,JDK的自动代理只能对接口实现自动代理,从代码当中也可以看出来realSubject.getClass().getInterfaces().JDK动态对接口进行实现,实现的方法是个空方法,那么动态代理又是怎么调用的realSubject相对应的实现方法呢,答案就是InvocationHandler,它关联了实现类,在上面的例子中也就是realSubject,它会调用realSubject相对应的实现方法。
JDK的动态代理有一个特别明显的不足,即只能对接口实现动态代理。cglib(Code Generation Library)的出现则弥补了JDK代理的不足,cglib能够实现对类进行动态代理。下面就用cglib来实现上面的例子。
- 1. import java.lang.reflect.Method;
- 2. import net.sf.cglib.proxy.Enhancer;
- 3. import net.sf.cglib.proxy.MethodInterceptor;
- 4. import net.sf.cglib.proxy.MethodProxy;
- 5.
- 6. class RealSubject {
- 7. public void request() {
- 8. System.out.println("真实的请求");
- 9. }
- 10. private void privateExample(){
- 11. System.out.println("这个是私有方法不能被子类继承");
- 12. }
- 13.
- 14. }
- 15.
- 16. public class MyMethodInterceptor implements MethodInterceptor {
- 17.
- 18. public Object intercept(Object object, Method method, Object[] args,
- 19. MethodProxy methodProxy) throws Throwable {
- 20. System.out.println(">>>MethodInterceptor start...");
- 21. Object result = methodProxy.invokeSuper(object, args);
- 22. System.out.println(">>>MethodInterceptor ending...");
- 23. return "hahahh";
- 24. }
- 25.
- 26. public Object createProxy(Class targetClass) {
- 27. Enhancer enhancer = new Enhancer();
- 28. enhancer.setSuperclass(targetClass);
- 29. enhancer.setCallback(new MyMethodInterceptor());
- 30. return enhancer.create();
- 31. }
- 32.
- 33. public static void main(String[] args) {
- 34. RealSubject target = new RealSubject();
- 35. MyMethodInterceptor test = new MyMethodInterceptor();
- 36. RealSubject proxyTarget = (RealSubject)test.createProxy(RealSubject.class);
- 37. proxyTarget.request();
- 38. }
- 39. }
cglib能实现对类的动态代理,产生的动态代理类是原始类,在上面的例子就是RealSubject的子类,那么又跑回到了java继承体系当中了,private方法 final方法是不能被子类继承和override的,所以这些方法是不会被动态代理的。像上面的方法privateExample(),就不会出现在main函数中的proxyTarget的方法列表中。
JDK动态代理和cglib动态代理的区别
1、JDK动态代理只能对接口实现动态代理,而cglib能对类实现动态代理
2、JDK动态代理生成动态代理类的速度或者说效率要比cglib快,但是生成后的动态代理类的效率则是cglib高,一般会高十倍以上,所以如果在Spring中使用动态代理,而Spring中的实例一般都是单例的,所以在Spring中生成动态代理类实例一般选用cglib比较好。这样生成的动态代理类的实例效率高。
闲扯几句:一般类的信息、常量信息会放在jvm内存中的方法区或者说是永久区,如果生成动态代理类过多的话,该区被占用的也就越多,有可能导致该区域的内存溢出(只是有可能,现实当中出现的几率非常小,就顺嘴提一句)。