超简单,超详细,超快速,一下就懂Java(JDK中的)代理模式
2018-02-04 11:49
435 查看
代理模式是指,为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标类对象之间起到中介的作用.
1.创建一个接口
2.再创建一个类实现这个接口(我们称他为目标类)
现在我想怎么做呢?我想让两个ISomeServiceImpl的实例化对象分别执行这两个方法,但是第二个对象在执行doFirst()的时候能够把打印出来的字母变成大写(功能增强).
于是我们的代理浓重登场
3.创建一个代理类
4.建个test来测试测试吧
看了这个例子,是不是觉得静态代理不再神秘了?那么接下来,再把它小改进:在代理类中增加一个有参构造
对代理类做了这样的改变后,在测试类我们就可以这样做了:
是不是感觉代理类和目标类的关系更加紧密0
了呢?
接口和实现类不变,但是我把ServiceProxy类删掉
直接上test类
下面我们把那个最长的方法处理出来看看:
其中:
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
在前面静态代理例子中:
我们通过代理类的构造方法得到一个代理类实例,通过这个代理对象执行它自己类中代理的方法从而得到目的.
那我们回过头来看看动态代理:
newProxyInstance()方法同样可以返回一个指定接口的代理类实例,只不过是Object类型的,所以要转型为代理类接口.
接着我们再重点关注一下第三个参数:
再简单说一下:第三个参数实际上是一个InvocationHandler的接口的实例,单接口不允许直接创建实例,所以我们创建一个匿名类不理来实现这个接口
进一步发现,这个InvocationHandler接口实际只有一个方法:
invoke方法在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 (这是JDK的中文API文档说的…)
这就是方法增强的关键:
有invoke,有method,是不是想到了反射?
于是我们使用反射的invoke方法:
这里传入代理对象以及参数,反射的method在newProxyInstance中传入的interfaces确定.具体就看最后调用哪个了.对反射有疑问的戳这里(Java反射的使用入门详解)
简言之,这一个方法执行了,就相当于目标类的方法执行了.
在本例子中,doFirst()是返回一个String类型,不过这里要求用Object来接收.
接下来就进行增强:
最后将增强后的结果返回
到这里就解释得差不多了.现在我调service.doFirst()得到的绝对是大写了哈哈哈!!!
但是还没完!我用service.doSecond()时却报了空指针异常..怎么回事呢?
原来我们在执行反射的invoke方法时,默认它是有返回值的(不如上面的result).
但doSeconed()却没有返回值,所以result=null.
然后null执行result.toString().toUpperCase()就报错了.
如何解决?
当然是在执行方法增强之前先判断一下result是否为空就好,不为空才执行(就是套一个if)
就是这样,喵~
1.1静态代理
先上代码有个直观感受1.创建一个接口
//主业务方法:本接口中的方法将要被代理增强 public interface ISomeService { String doFirst(); void doSecond(); }
2.再创建一个类实现这个接口(我们称他为目标类)
//目标类:代理类需要增强的类 public class ISomeServiceImpl implements ISomeService { @Override public String doFirst() { System.out.println("执行doFirst"); return "abcde"; } @Override public void doSecond() { System.out.println("执行doSeconed"); } }
现在我想怎么做呢?我想让两个ISomeServiceImpl的实例化对象分别执行这两个方法,但是第二个对象在执行doFirst()的时候能够把打印出来的字母变成大写(功能增强).
于是我们的代理浓重登场
3.创建一个代理类
//静态代理类 public class ServiceProxy implement d202 s ISomeService { //实现相同的接口 private ISomeService target; public ServiceProxy() { target = new ISomeServiceImpl(); //先来一个ISomeService的实例化对象 } @Override public String doFirst() { // 调用目标对象的目标方法,该方法返回全小写字母 String result = target.doFirst(); // 增强:在这里将目标方法全小写字母变成全大写 result = result.toUpperCase(); return result; } @Override public void doSecond() { target.doSecond(); //在这里并没有做更多的操作,代理的方法调用目标类的方法 } }
4.建个test来测试测试吧
public class MyTest { public static void main(String[] args) { // 创建一个目标对象 ISomeService iservice = new ISomeServiceImpl(); System.out.println(iservice.doFirst()); // print:"执行doFirst"和"abcde" iservice.doSecond(); // 执行doSeconed,print:执行doSeconed //同时我再创建一个代理类的话.. ISomeService pservice = new ServiceProxy(); System.out.println(pservice.doFirst()); // print:执行doFirst ABCDE pservice.doSecond(); // 还是print:执行doSeconed } }
看了这个例子,是不是觉得静态代理不再神秘了?那么接下来,再把它小改进:在代理类中增加一个有参构造
//静态代理类 public class ServiceProxy implements ISomeService { private ISomeService target;//还是先创建一个目标内的对象,代理人?哈哈 public ServiceProxy() { target = new ISomeServiceImpl(); } public ServiceProxy(ISomeService target) {//创建一个有参构造,传入目标类对象 super(); this.target = target; } @Override //下面和之前一样 public String doFirst() { // 调用目标对象的目标方法,该方法返回全小写字母 String result = target.doFirst(); // 增强:将目标方法全小写字母变成全大写 result = result.toUpperCase(); return result; } @Override public void doSecond() { target.doSecond(); } }
对代理类做了这样的改变后,在测试类我们就可以这样做了:
public class MyTest { public static void main(String[] args) { ISomeService target = new ISomeServiceImpl(); ISomeService service = new ServiceProxy(target); System.out.println(service.doFirst());// print:执行doFirst ABCDE //假设我还想调代理前的方法.. System.out.println(target.doFirst()); //print:执行doFirst abcde service.doSecond(); } }
是不是感觉代理类和目标类的关系更加紧密0
了呢?
1.2动态代理
动态代理和静态代理的区别就是:静态代理有代理类,动态代理么有代理类.没有了律师团,怎么帮我打官司呢?当然只好请律师了接口和实现类不变,但是我把ServiceProxy类删掉
直接上test类
public class MyTest { public static void main(String[] args) { final ISomeService target = new ISomeServiceImpl(); // 在内部类中不能引用一个非final的变量,这点需要注意 //重点来了: ISomeService service = (ISomeService) Proxy.newProxyInstance(target .getClass().getClassLoader(), // 目标类的类加载器 target.getClass().getInterfaces(),// 目标类所实现的所有接口 new InvocationHandler() {// 口怕,第三个参数居然是匿名内部类..其实就是一个实现了InvocationHandler接口的类 // proxy:代理对象 // method:目标方法 // args:目标方法的参数列表 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(target, args);// 这里运用到了反射的知识 String stresult = result.toString().toUpperCase(); return stresult; } }); System.out.println(service.doFirst());// print:"执行doFirst"和"ABCDE" System.out.println(target.doFirst()); }
下面我们把那个最长的方法处理出来看看:
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
其中:
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
在前面静态代理例子中:
ISomeService service = new ServiceProxy(target);
我们通过代理类的构造方法得到一个代理类实例,通过这个代理对象执行它自己类中代理的方法从而得到目的.
那我们回过头来看看动态代理:
newProxyInstance()方法同样可以返回一个指定接口的代理类实例,只不过是Object类型的,所以要转型为代理类接口.
ISomeService service = (ISomeService)Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
接着我们再重点关注一下第三个参数:
再简单说一下:第三个参数实际上是一个InvocationHandler的接口的实例,单接口不允许直接创建实例,所以我们创建一个匿名类不理来实现这个接口
进一步发现,这个InvocationHandler接口实际只有一个方法:
invoke(Object proxy, Method method,Object[] args)
invoke方法在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。 (这是JDK的中文API文档说的…)
这就是方法增强的关键:
有invoke,有method,是不是想到了反射?
于是我们使用反射的invoke方法:
method.invoke(target, args);
这里传入代理对象以及参数,反射的method在newProxyInstance中传入的interfaces确定.具体就看最后调用哪个了.对反射有疑问的戳这里(Java反射的使用入门详解)
简言之,这一个方法执行了,就相当于目标类的方法执行了.
在本例子中,doFirst()是返回一个String类型,不过这里要求用Object来接收.
Object result = method.invoke(target, args);
接下来就进行增强:
String stresult = result.toString().toUpperCase();
最后将增强后的结果返回
return stresult;//终于把原来的doFirst()方法增强了
到这里就解释得差不多了.现在我调service.doFirst()得到的绝对是大写了哈哈哈!!!
但是还没完!我用service.doSecond()时却报了空指针异常..怎么回事呢?
原来我们在执行反射的invoke方法时,默认它是有返回值的(不如上面的result).
但doSeconed()却没有返回值,所以result=null.
然后null执行result.toString().toUpperCase()就报错了.
如何解决?
当然是在执行方法增强之前先判断一下result是否为空就好,不为空才执行(就是套一个if)
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(target, args);// 这里运用到了反射的知识 if (result != null) result = result.toString().toUpperCase(); return result; }
就是这样,喵~
相关文章推荐
- 举两个例子以快速明白Java中的简单工厂模式
- 通过CGLIB实现AOP的浅析(顺便简单对比了一下JDK的动态代理)
- [教程]Java代理(静态,动态jdk和Cglib)简单应用
- 0104 Java设计模式03-动态代理(实战篇JDK和cglib)【进阶】
- 设计模式(2)--java动态代理及jdk和cglib的区别
- Java反射之JDK动态代理实现简单AOP
- 关于java中jdk中接口动态代理模式Proxy的剖析
- Java的简单代理设计模式样例
- java 代理模式,JDK动态代理,SpringAOP的实现
- 通过CGLIB实现AOP的浅析(顺便简单对比了一下JDK的动态代理)
- java动态代理模式Proxy之JDK动态代理机制
- 通过CGLIB实现AOP的浅析(顺便简单对比了一下JDK的动态代理)
- java动态代理模式(jdk和cglib)
- 简单代理模式 JAVA
- Java反射之JDK动态代理实现简单AOP
- JAVAProxy代理模式简单实现
- Java反射之JDK动态代理实现简单AOP
- java动态代理jdk与cglib详细讲解
- 代理角色java设计模式之静态代理详细介绍
- java Proxy代理模式简单讲解