Java代理机制之初见(理解及实现)
都知道Java中的Spring,有一重要思想:AOP,实现原理也就是Java的动态代理机制。初见代理这个名词时,觉得生活中常有代理的这一说法。
那么,在Java中,代理又是什么呢?它又是如何实现的?实现后是干什么的?
其实啊,Java中的代理也就是生活中代理的意思,我认为,在Java中,代理就是帮助类实现一些修改的动作。
也就是说,用代理类来修改已经存在的类。那么,问题又来了,为何这样做呢?在存在的类上修改不就完事了吗?
首先,这种想法时错的,Java中,已经编译好的类不要随随便便的修改,容易造成整个工程的一系列问题。所以,使用这种方法就不需要修改已存在的类了,可以对原类进行相应的动作。这样才符合编程的需求。
一、下面来具体说说代理:
大的方向来说代理可分为:静态代理和动态代理。而动态代理又有两种产生代理的方式:JDK和CGLiB。
二、静态代理的实现:
首先创建一个接口,然后创建具体实现类来实现这个接口,在创建一个代理类同样实现这个接口,不同之处在于,
具体实现类的方法中需要将接口中定义的方法的业务逻辑功能实现,而代理类中的方法只要调用具体类中的对应方法即可,
这样我们在需要使用接口中的某个方法的功能时直接调用代理类的方法即可,将具体的实现类隐藏在底层。
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
1.首先需要创建的接口:
1 package com.xupt.proxy.classes; 2 3 public interface IStudentProxyId { 4 void setId(String id); 5 String getId(); 6 }
1 package com.xupt.proxy.classes; 2 3 public interface IStudentProxyName { 4 void setName(String name); 5 String getName(); 6 }
2.实现这个接口的类:
1 package com.xupt.proxy.classes; 2 3 public class StudentMessage implements IStudentProxyId,IStudentProxyName { 4 5 private String id; 6 private String name; 7 8 public StudentMessage() {} 9 10 @Override 11 public void setName(String name) { 12 this.name = name; 13 14 System.out.println("StudentMessage.setName()"); 15 } 16 17 @Override 18 public String getName() { 19 System.out.println("StudentMessage.getName()"); 20 21 return name; 22 } 23 24 @Override 25 public void setId(String id) { 26 this.id = id; 27 28 System.out.println("StudentMessage.setId()"); 29 30 } 31 32 @Override 33 public String getId() { 34 System.out.println("StudentMessage.getId()"); 35 36 return id; 37 } 38 39 }
无用框。。。。。。
3.实现类的代理类:
1 package com.xupt.proxy.proxy; 2 3 import com.xupt.proxy.classes.IStudentProxyId; 4 import com.xupt.proxy.classes.IStudentProxyName; 5 import com.xupt.proxy.classes.StudentMessage; 6 7 public class StudentProxy implements IStudentProxyId,IStudentProxyName{ 8 9 StudentMessage message; 10 11 public StudentProxy() { 12 } 13 14 public StudentProxy(StudentMessage message) { 15 this.message = message; 16 } 17 18 @Override 19 public void setName(String name) { 20 21 System.out.println("置前拦截StudentProxy.setName()"); 22 message.setName(name); 23 System.out.println("置后拦截StudentProxy.setName()"); 24 } 25 26 @Override 27 public String getName() { 28 29 System.out.println("置前拦截StudentProxy.getName()"); 30 String name = message.getName(); 31 System.out.println("置后拦截StudentProxy.getName()"); 32 33 return name; 34 } 35 36 @Override 37 public void setId(String id) { 38 39 System.out.println("置前拦截StudentProxy.setId()"); 40 message.setName(id); 41 System.out.println("置后拦截StudentProxy.setId()"); 42 } 43 44 @Override 45 public String getId() { 46 System.out.println("置前拦截StudentProxy.getId()"); 47 String id = message.getId(); 48 System.out.println("置后拦截StudentProxy.getId()"); 49 50 return id; 51 } 52 53 }
4.定义测试类:
package com.xupt.proxy.demo; import com.xupt.proxy.classes.StudentMessage; import com.xupt.proxy.proxy.StudentProxy; public class StudentProxyTest { public static void main(String[] args) {
StudentProxy proxy = new StudentProxy(new StudentMessage());
//使用的是代理类的对象 proxy.setId("222666"); proxy.setName("张三"); System.out.println("id: " + proxy.getId() + " " + "name: " +proxy.getName()); } }
5.执行结果:
静态代理总结:
1. 可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2. 缺点:
代理类和委托类实现相同的接口,同时要实现相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。 实现一次就觉得挺简单的。但是,静态代理就是将接口、实现类、代理类一次全部手动执行。但细想一下,如果我们需要很多代理,每次都重新手动完成,是不是不现实呢?每一个都这么手动的去创建实属浪费时间,而且会有大量的重复代码。所以,动态代理就出现了。事情总有解决的办法。
三:动态代理的实现:
动态代理
动态代理有以下特点:
(1) 在运行期,通过反射机制创建一个实现了一组给定接口的新类
(2)在运行时生成的class,必须提供一组interface给它,然后该class就宣称它实现了
这些 interface。该class的实 例可以当作这些interface中的任何一个来用。
(3)动态代理也叫做:JDK代理,接口代理
(4)接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理
(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以
进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用
使我们的类职责更加单一,复用性更强。
1、JDK动态代理的实现:
1.1.定义接口类:这个接口类和静态代理的接口没有区别。
1 package com.xupt.proxy.classes; 2 3 public interface IStudentProxyId { 4 void setId(String id); 5 String getId(); 6 }
1 package com.xupt.proxy.classes; 2 3 public interface IStudentProxyName { 4 void setName(String name); 5 String getName(); 6 }
1.2.接口的实现类;也是和静态代理一样。
1 package com.xupt.proxy.classes; 2 3 public class StudentMessage implements IStudentProxyId,IStudentProxyName { 4 5 private String id; 6 private String name; 7 8 public StudentMessage() {} 9 10 @Override 11 public void setName(String name) { 12 this.name = name; 13 14 System.out.println("StudentMessage.setName()"); 15 } 16 17 @Override 18 public String getName() { 19 System.out.println("StudentMessage.getName()"); 20 21 return name; 22 } 23 24 @Override 25 public void setId(String id) { 26 this.id = id; 27 28 System.out.println("StudentMessage.setId()"); 29 30 } 31 32 @Override 33 public String getId() { 34 System.out.println("StudentMessage.getId()"); 35 36 return id; 37 } 38 39 }
1.3.定义代理类:
1 package com.xupt.proxy.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class StudentsProxy { 8 9 @SuppressWarnings("unchecked") 10 public <T> T getProxy(Class<?> kalss) throws Exception { 11 12 return (T) getProxy(kalss,kalss.newInstance()); 13 } 14 15 @SuppressWarnings("unchecked") 16 public <T> T getProxy(Object object) { 17 18 return (T) getProxy(object.getClass(),object); 19 } 20 21 @SuppressWarnings("unchecked") 22 public <T> T getProxy(Class<?> klass,Object obj) { 23 return (T) Proxy.newProxyInstance( //返回值类型,用户可以给任意类型 24 klass.getClassLoader(), //原类的加载 25 klass.getInterfaces(), //原类实现的接口 26 new InvocationHandler() { //其实它是接口,需要实现它的invoke方法。(进入源码就可以查证)。 27 28 @Override 29 //利用反射中的参数分别为:代理类的对象、原类的方法(该方法时以后真的需要实现拦截的方法)、 30 //原类方法中的参数数组。 31 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 32 Object result = null; 33 34 //TODO 这里以后是真正的置前拦截操作,加上自己想要实现的东西。 35 System.out.println("置前拦截...."); 36 37 result = method.invoke(obj, args); 38 //由上面一句可以看到,是一个方法的反射,反射的是原类的方法,得到的结果却给代理类返回去, 39 //这就把原类的代理类来呢西到一起了。 40 41 //TODO 这里以后是真正的置后拦截操作,加上自己想要实现的东西。 42 System.out.println("置后拦截...."); 43 44 return result; 45 } 46 }); 47 } 48 }
1.4定义测试类:
1 package com.xupt.proxy.demo; 2 3 import com.xupt.proxy.classes.IStudentProxyId; 4 import com.xupt.proxy.classes.IStudentProxyName; 5 import com.xupt.proxy.classes.StudentMessage; 6 import com.xupt.proxy.proxy.StudentsProxy; 7 8 public class ProxyDemo { 9 10 public static void main(String[] args) { 11 IStudentProxyId ispi = null; 12 try { 13 ispi = new StudentsProxy().getProxy(StudentMessage.class); 14 ispi.setId("123456"); 15 System.out.println("id:" + ispi.getId()); 16 17 } catch (Exception e1) { 18 e1.printStackTrace(); 19 } 20 21 IStudentProxyName ispn = new StudentsProxy().getProxy(new StudentMessage()); 22 ispn.setName("张三"); 23 System.out.println("name:" + ispn.getName()); 24 25 26 27 } 28 29 }
执行结果:
由上面的代码可以看出,使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。那么,问题又有了,
若我这个类就是没有完成某个接口又该如何??还是,事情总有解决的办法,还有CGLib动态代理机制呢。
下面我们来看看CGLib动态代理:
2.动态代理CGLib的实现:
上面的静态代理和动态代理模式都是要求目标对象实现一个接口或者多个接口,但是有
时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用构建目标
对象子类的方式实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能
的扩展.
2.1:定义原类
1 package com.xupt.proxy.classes; 2 3 public class CglibClass { 4 private String id; 5 private String name; 6 7 public CglibClass() { 8 } 9 10 public void setId(String id) { 11 this.id = id; 12 13 System.out.println("CglibClass.setId(): "+ id); 14 15 } 16 17 public String getId() { 18 System.out.println("CglibClass.getId()"); 19 20 return id; 21 } 22 23 public String personName(String name) { 24 this.name = name; 25 26 System.out.println("这是原类的personName方法...: " + name); 27 28 return name; 29 } 30 }
2.2:定义原类的代理类:
1 package com.xupt.proxy.proxy; 2 3 import java.lang.reflect.Method; 4 5 import net.sf.cglib.proxy.Enhancer; 6 import net.sf.cglib.proxy.MethodInterceptor; 7 import net.sf.cglib.proxy.MethodProxy; 8 9 public class CGLibProxy { 10 11 public CGLibProxy() { 12 } 13 14 @SuppressWarnings("unchecked") 15 public <T> T getProxy(Class<?> kalss) throws Exception { 16 17 return (T) getProxy(kalss,kalss.newInstance()); 18 } 19 20 @SuppressWarnings("unchecked") 21 public <T> T getProxy(Object object) { 22 23 return (T) getProxy(object.getClass(),object); 24 } 25 26 @SuppressWarnings("unchecked") 27 public <T> T getProxy(Class<?> kalss,Object obj) { 28 Enhancer enhancer = new Enhancer(); 29 enhancer.setSuperclass(kalss); //这句可以看出被代理类是代理类的父类。 30 enhancer.setCallback(new MethodInterceptor() { //设置回调。 31 32 @Override 33 public Object intercept(Object object, Method method, Object[] args, MethodProxy methodPorxy) 34 throws Throwable { 35 Object result = null; 36 37 System.out.println("置前拦截原类的personName方法...."); 38 39 result = method.invoke(obj, args); 40 System.out.println("置后拦截原类的personName方法...."); 41 42 43 return result; 44 } 45 }); 46 47 return (T) enhancer.create(); //enhancer.create()制作代理的对象。 48 } 49 }
2.3定义测试类:
1 package com.xupt.proxy.demo; 2 3 import com.xupt.proxy.classes.CglibClass; 4 import com.xupt.proxy.proxy.CGLibProxy; 5 6 public class CGLibTset { 7 8 public static void main(String[] args) { 9 10 CglibClass cglibClass; 11 try { 12 cglibClass = new CGLibProxy().getProxy(CglibClass.class); 13 cglibClass.setId("666666"); 14 cglibClass.personName("张三"); 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 19 20 } 21 22 }
2.4执行结果:
以上就是我所总结的代理机制之静态代理、动态代理的JDK代理、动态代理的CGLib代理的实现方式。最后强调一点:
Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的子类.
代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会
被拦截,即不会执行目标对象额外的业务方法.(尝试过)
还有CGLib代理的实现必须导CGLib的cglib-nodep-2.1_3.jar包才能实现。
若此博文有任何问题请指正,也希望对您有帮助!