Java代理模式实现总结

代理是一种设计模式,代理分成三种:静态代理,动态代理,CGLIB动态代理。

代理的概念:

1、代理对象存在的价值主要是用于拦截真实业务对象的访问。
2、代理对象应该具有和目标对象(真实业务对象)相同的方法。
静态代理:
    1、需要定义接口或者父类。
    2、被代理的对象与代理对象一起实现相同的接口或者继承相同的父类。
静态代理小案例:

   public class Test {
        public static void main(String[] args) {
            Human p=new Human();
            Proxy proxy=new Proxy(p);
            proxy.sing();
        }
    }
    /*
     * 接口
     */
    interface Sing{
        void sing();//唱歌
    }
    class Human implements Sing{
        @Override
        public void sing() {
            System.out.println("唱歌");
        }
    }
    class Proxy implements Sing{
        //接收一个需要代理的对象
        Human p=new Human();//p为一个歌手
        public Proxy(Human p) {
            super();
            this.p = p;
        }
        @Override
        public void sing() {
            p.sing();
        }
    }

静态代理特点分析:
    静态代理通常只代理一个类,静态代理要实现知道要代理什么。
    因为代理对象需要与目标对象实现一样的接口,所以会有很多的代理类,一旦接口增加方法,那么目标类与代理类都要进行修改,不利于维护。
动态代理:
java.lang.reflect.Proxy类的介绍:
java在jdk1.5之后提供了一个java.lang.reflect.Proxy类的newProxyInxtance()方法创建一个代理对象。

   public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

注:newProxyInstance方法返回一个代理对象,此方法有三个参数,ClassLoader loader用于指明生成代理对象使用哪个类加载器,Class[] interfaces用于指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某个对象的代理对象了。

模拟动态代理小案例:
在java中规定,要想生成一个对象的代理对象,那么这个对象必须要有一个接口,这个接口定义这个对象所具有的行为。


接口:


    public interface Person {

        String sing(String name);
        String dance(String name);
    }

歌手:

  public class Singer implements Person {
        @Override
        public String sing(String name) {
            System.out.println("歌手唱"+name+"歌!!");
            return "演出结束,谢谢大家";
        }
        @Override
        public String dance(String name) {
            System.out.println("歌手跳"+name+"舞蹈!!");
            return "演出结束,谢谢大家";
        }
    }

生成代理类:

    public class SingProxy {
        //代理类需要代理的目标
        private Person s=new Singer();
        
        public Person getProxy(){
            //SingProxy.class.getClassLoader():指明生成代理对象使用哪个类加载器
            //s.getClass().getInterfaces():指明生成哪个对象的代理
            //new InvocationHandler指明代理需要做的事情
            return (Person) Proxy.newProxyInstance(SingProxy.class.getClassLoader(),s.getClass().getInterfaces() , new InvocationHandler(){
                //使用到反射的知识
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(s, args);
                }
                
            });
        }
    }

测试类:

  public class Test {
        public static void main(String[] args) {
            SingProxy proxy=new SingProxy();
            //获取代理对象
            Person p=proxy.getProxy();
            //调用代理的sing方法
            String sing =p.sing("冰雨");
            System.out.println(sing);
            //调用代理的sing方法
            String dance=p.dance("巴拉巴拉");
            System.out.println(dance);
        }
    }

面试题:写一个ArrayList的动态代理类
  

  public class Test {
        //定义一个需要代理的对象
        static List list=new ArrayList<>();
        //list.getClass().getClassLoader():指明生成代理对象使用哪个类加载器
        //list.getClass().getInterfaces():指明生成哪个对象的代理,接口
        @SuppressWarnings("unchecked")
        static List listProxy=(List) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler(){
            {
        }

            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                method.invoke(list, args);
                return list;
            }
        });
        public static void main(String[] args) {
            listProxy.add("hello");
            System.out.println(list);
        }
    }

动静态代理的区别,什么场景下使用?
    1、静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类
    2、静态代理事先知道代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才会知道。
    3、动态代理是实现jdk中的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的
        业务类必须要实现的接口,通过Proxy里的newProxyInstance得到代理对象。


 CDLIB动态代理:
        动态代理CDLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态的修改字节码达到修改类的目的。
       上面的静态代理与动态代理模式都要求目标对象实现了一个接口。CGLIB代理,也叫子类代理,代理效率高于jdk,它是在内存中构建一个对象从而实现对目标对象功能的扩展。

cglib动态代理有如下特点:

    1. jdk的动态代理有一个限制,就是使用动态代理的对象必须实现一个或者多个接口,如果没有实现接口的类需要代理可以使用chlib实现。
    2. cglib是一个强大的高性能的代码包,它可以在运行期间扩展java类与实现java接口,它广泛的被许多的AOP的框架使用。
    3. cglib的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类,不鼓励直接使用ASM,因为它要求你          必须对JVM内部结构,包括class文件的格式和指令集都很熟悉。
    4. 需要引入cglib的jar文件,但是Spring的核心包中已经包含Cglib功能,所以直接引入pring-core-3.3.5,jar即可。
    5. 引入功能包之后就可以在内存中动态的构建子类。
    6. 代理的类不能为final,否则报错。
    7. 目标对象的方法如果为final/static,那么就不会执行目标对象额外的业务方法。
    
cglib动态代理小案例:

    public class Test {
        public static void main(String[] args) {
            Singer1 singer=new Singer1();
            ProxyFactory pf=new ProxyFactory(singer);
            Singer1 singer1=(Singer1) pf.getProxy();
            singer1.sing();
        }
    
    }
    class Singer1{
        void sing(){
            System.out.println("歌唱");
        }
    }
    
    class ProxyFactory implements MethodInterceptor{
    
        //维护目标对象
        private Object obj=new Object();
        
        public ProxyFactory(Object target) {
            super();
            this.obj = target;
        }
    
        //提供方法获取代理
        public Object getProxy(){
            //工具类
            Enhancer en=new Enhancer();
            //设置父类
            en.setSuperclass(obj.getClass());
            //设置回调
            en.setCallback(this);
            //创建子类(代理对象)
            return en.create();
        }
    
        @Override
        public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
            return method.invoke(obj, args);
        }
    }

注:AOP编程就是基于动态代理实现的,比如著名的Spring框架,Hibernate框架都是动态代理的例子。

参考文章:https://blog.csdn.net/yangsnow_rain_wind/article/details/79291256

你可能感兴趣的:(Java)