代理模式

代理模式分为静态代理和动态代理,静态代理很简单,像我们常用controller 就是,如下代码

 @Controller("/a")
public class AController{
     @autowired
     private AService a;

    @RequestMapping("/test")
     public void test(){
           //方法调用前干点事
            a.xx();
           //方法调用后干点事
     }
}

AController 可以代理很多service,但是每次都要改代码;而动态代理则是可以动态实现,不需要改代码,动态代理的实现又分为JDK,和CGLIB

JDK动态代理

package com.wei;

public interface AService {
    void xx();
}

package com.wei;
public class AServiceImpl implements AService {
    
    public void xx() {
        System.out.println("xx");
    }
}
package com.wei;
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
public class MyInvocationHandler implements InvocationHandler {  
      
    // 目标对象   
    private Object target; 
    
    public MyInvocationHandler(Object target) {  
        super();  
        this.target = target;  
    }  
    /** 
     * 获取目标对象的代理对象 
     * @return 代理对象 
     */  
    public Object getProxy() {  
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),   
                target.getClass().getInterfaces(), this);  
    }  
  
    /** 
     * 执行目标对象的方法 
     */  
    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;  
    }  
}  
public static void main(String[] args) {
         // 实例化InvocationHandler  
        MyInvocationHandler invocationHandler = new MyInvocationHandler(new AServiceImpl());  
          
        // 根据目标对象生成代理对象  
        AService proxy = (AService) invocationHandler.getProxy();  
          
        // 调用代理对象的方法  
        proxy.xx();  
    }

执行结果:

------------------before------------------
xx
-------------------after------------------

大家写过的都懂,即jdk生成一个代理类(用断点放到proxy可看到)实现AService接口,用代理类调用xx方法,这个类是在内存中,那我们把main方法改造下,加一个生产动态代理的方法,看看这个代理类长什么样

public static void main(String[] args) throws Exception {
        // 实例化InvocationHandler
        MyInvocationHandler invocationHandler = new MyInvocationHandler(new AServiceImpl());

        // 根据目标对象生成代理对象
        AService proxy = (AService) invocationHandler.getProxy();

        // 调用代理对象的方法
        proxy.xx();

        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[] { AService.class });
        FileOutputStream os = new FileOutputStream("E://$Proxy0.class");//生成的代理类放到E盘
        os.write(bytes);
        os.close();
    }

反编译E盘的这个类

import java.lang.reflect.*;  
  
public final class $Proxy0 extends Proxy  
    implements AService  
{  
  
    // 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例  
    public $Proxy0(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    }  
  
    public final boolean equals(Object obj)  
    {  
        try  
        {  
            return ((Boolean)super.h.invoke(this, m1, new Object[] {  
                obj  
            })).booleanValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    /** 
     * 这个方法是关键部分 
     */  
    public final void xx()  
    {  
        try  
        {  
            // 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法
            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    public final int hashCode()  
    {  
        try  
        {  
            return ((Integer)super.h.invoke(this, m0, null)).intValue();  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    public final String toString()  
    {  
        try  
        {  
            return (String)super.h.invoke(this, m2, null);  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  
  
    private static Method m1;  
    private static Method m3;  
    private static Method m0;  
    private static Method m2;  
  
    // 在静态代码块中获取了4个方法:Object中的equals方法、AService中的xx方法、Object中的hashCode方法、Object中toString方法  
    static   
    {  
        try  
        {  
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  
                Class.forName("java.lang.Object")  
            });  
            m3 = Class.forName("dynamic.proxy.AService").getMethod("xx", new Class[0]);  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
        }  
        catch(NoSuchMethodException nosuchmethodexception)  
        {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        }  
        catch(ClassNotFoundException classnotfoundexception)  
        {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
}  

可以看到 xx() 方法就是调用了super.h.invoke(this, m3, null); 那h到底是什么,先看构造方法

 public $Proxy0(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    } 

,通过new 的对象传入的h=传入的invocationhandler,
再回到上面的main方法
invocationHandler.getProxy(),打开这个方法,看到

public Object getProxy() {  
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),   
                target.getClass().getInterfaces(), this);  
    }  

打开newProxyInstance 这个方法
···
public static Object newProxyInstance(ClassLoader loader,
Class[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
....
通过传过来的参数找到对应的代理类,并且实例化对象,传入h,即上一步的this对象
return cons.newInstance(new Object[]{h});
...
···
由此可知,最终proxy.xx();调用的是MyInvocationHandler.invoke 方法
当然

 // 执行目标对象的方法  
        Object result = method.invoke(target, args);  

这段代码是通过反射找到目标方法进行执行,所以说执行效率肯定不高

我们可以自己手写一个MyProxy和MyClassLoader 来模拟jdk的过程

package com.wei;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class MyProxy {

    public static Object newProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler h) {
        try {
            // 1.动态生成java文件
            String src = generateSoure(interfaces);
            String filePath = MyProxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            // 2.java文件写入文件流
            FileWriter fw = new FileWriter(f);
            fw.write(src);
            fw.flush();
            fw.close();
            // System.out.println(f);
            // 3.把生成的java文件编译成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manage.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
            task.call();
            manage.close();
            // 4.生成的class文件加载到 JVM
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
            f.delete();
            // 5.加载的class重新实例化对象
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String generateSoure(Class[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.wei;");
        sb.append("import java.lang.reflect.Method;");
        // 这里只生成第一个接口的方法
        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{");
        sb.append("MyInvocationHandler h;");
        sb.append("public $Proxy0(MyInvocationHandler h) { ");
        sb.append("this.h = h;");
        sb.append("}");
        for (Method m : interfaces[0].getMethods()) {
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {");
            sb.append("try{");
            sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName()
                    + "\",new Class[]{});");
            sb.append("this.h.invoke(this,m,null);");
            sb.append("}catch(Throwable e){");
            sb.append("e.printStackTrace();");
            sb.append("}");
            sb.append("}");
        }
        sb.append("}");
        System.out.println(sb);
        return sb.toString();
    }
}
package com.wei;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class MyClassLoader extends ClassLoader {
    private File classPathFile;

    public MyClassLoader() {
        String classPath = MyClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    protected Class findClass(String name) throws ClassNotFoundException {
        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        if (classPathFile != null) {
            File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
            if (classFile.exists()) {
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try {
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int len;
                    while ((len = in.read(buff)) != -1) {
                        out.write(buff, 0, len);
                    }
                    //通过父类的defineClass方法找到我们生成的class文件加载并返回
                    return defineClass(className, out.toByteArray(), 0, out.size());
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != in) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    if (out != null) {
                        try {
                            out.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return null;
    }
}

改动

 public Object getProxy() {  
        return MyProxy.newProxyInstance(new MyClassLoader(),   
                target.getClass().getInterfaces(), this);  
    }  

运行效果是一样的

------------------before------------------
xx
-------------------after------------------

CGLIB

public class MyInvocationHandler implements MethodInterceptor{
    
    public Object getInstance(Class clazz) throws Exception{
        Enhancer enhancer = new Enhancer();
        //把传过来的类设置为父类
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
      
    /** 
     * 执行目标对象的方法 
     */  
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws
    Throwable {  
        // 在目标对象的方法执行之前简单的打印一下  
        System.out.println("------------------before------------------");  
        // 执行父类的方法    
        Object result = methodProxy.invokeSuper(o,objects);
        // 在目标对象的方法执行之后简单的打印一下  
        System.out.println("-------------------after------------------");  
        return result;  
    }  
}  
public static void main(String[] args) throws Exception {
        // 实例化InvocationHandler
        AServiceImpl proxy = (AServiceImpl)new MyInvocationHandler().getInstance(AServiceImpl.class);
        // 调用代理对象的方法
        proxy.xx();
    }

运行结果是一样的,只不过CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象来重写代理类,性能为什么这么快,因为cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法。

CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高

静态代理和动态的本质区别
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码

代理模式的优缺点
优点:
1、代理模式能将代理对象与真实被调用的目标对象分离
2、一定程度上降低了系统的耦合度,扩展性好
3、可以起到保护目标对象的作用
4、可以对目标对象的功能增强
缺点:
1、代理模式会造成系统设计中类的数量增加
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
3、增加了系统的复杂度

你可能感兴趣的:(代理模式)