设计模式-代理模式

当年不做笔记,现在不会了,要重学框架,先把代理模式敲一遍。

1.概念

为其他对象提供一种代理以控制对这个对象的访问。代理对象起到中介作用,可去掉功能服务或增加额外的服务。有动态代理和静态代理两种实现方式,动态代理是Spring框架AOP思想的核心。

 

2.涉及到的角色

抽象角色:提供 一个接口或者抽象类 让真实/代理角色 实现或者继承

真实/目标角色:实现抽象类,实现需要,被代理

代理角色:在自己的方法里调用真实角色的方法,还可以补充扩展,充当中介作用。

 

3.静态代理

程序运行前就存在的代理类,即手写的

public class StaticProxyTest {
    public static void main(String[] args) {//静态代理
        Producer producer=new Store();
        ProducerProxy pp=new ProducerProxy(producer);
        producer.sell();
        pp.sell();
    }
}

interface Producer {
    public void sell();
}

class Store implements Producer{
    public void sell() {
        System.out.println("商店在卖货!");
    }
}

class ProducerProxy implements Producer{
    private Producer producer;
    public ProducerProxy(Producer producer) {
        this.producer=producer;
    }
    public void sell() {
        System.out.print("代理");//代理类对被代理类的方法增添的功能
        producer.sell();//在代理类里调用 被代理类的方法
    }
}

 

4.JDK动态代理

代理对象不需要实现接口,但是真实对象需要实现所有接口,

import java.lang.reflect.Proxy;

public class JDKProxyTest {
    public static void main(String[] args) {//JDK动态代理
        Producer producer=new Store();
        Producer pp=(Producer)Proxy.newProxyInstance(producer.getClass().getClassLoader(), 
                producer.getClass().getInterfaces(), (proxy,method,args1)->{
                    Object invoke=method.invoke(producer, args1);
                    return invoke;
                });
        pp.sell();
    }
}

interface Producer {
    public void sell();
}

class Store implements Producer{
    public void sell() {
        System.out.println("商店在卖货!");
    }
}

static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h);

//java.lang.reflect.Proxy的方法,API原话是“返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序”。说白了就是创建一个代理类的对象,可以使用真实类的方法。

对参数涉及到的几个方法了解一下

(1)ClassLoader getClassLoader();

获取类加载器

(2)类<?>[] getInterfaces();

Class的方法,获取 该对象表示的类 实现的接口

(3)InvocationHandler的实例化需要实现invoke()方法,这个方法很陌生,通过下面的代码来理解。

Object invoke(Object object,Object... args);//Method类的方法,在具有指定参数的 方法对象上调用此 方法对象表示的底层方法

import java.lang.reflect.Method;

public class Demo {
    
    public static void main(String[] args) throws Exception {
        Class c=Class.forName("demo.Test");//获取类
        Test test=(Test)c.newInstance();//创建Test类的实例对象test
        Method method = c.getMethod("run",String.class,Integer.class);//获取名叫"run"并且参数类型分别是String和Integer的方法
        method.invoke(test, null,336);//对象test调用run方法,参数可以0-无限多个
        //getMethod方法的名字、参数个数要正确,并且调用的invoke方法参数也要正确
    }
}

class Test {
    public void run(String s,Integer i) {
        System.out.println("s:" + s + " i:" + i);
    }
}
/*输出
s:null i:336
*/

 

较详细一些的JDK动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxyTest {
    
    public static void main(String[] args) {//JDK动态代理
        Producer producer=new Store();
        ClassLoader classLoader=producer.getClass().getClassLoader();
        Class<?>[] c=producer.getClass().getInterfaces();
        InvocationHandler h=new InvocationHandler(){
            public Object invoke(Object proxy,Method method,Object[] args) throws Exception {
                System.out.println("发财1");
                Object res=method.invoke(producer,args);//args是把原来的若干个参数都弄进数组
                System.out.println("发财2");
                return res;
            }
        };
        Producer pp=(Producer)Proxy.newProxyInstance(classLoader, c,h);
        System.out.println("发财3");
        pp.sell();//代理对象pp通过invoke方法调用真实类的方法
        System.out.println("发财4");
    }
}

interface Producer {
    public void sell();
}

class Store implements Producer{
    public void sell() {
        System.out.println("商店在卖货!");
    }
}
/*输出:
发财3
发财1
商店在卖货!
发财2
发财4
*/

 

5.Cglib动态代理

真实对象可以没有抽象类,是单独一个类

需要导入Spring-core包,使用某些类

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxyTest {

    public static void main(String[] args){
        Store store=new Store();
        Store p=new CgLibProxy(store).proxy();
        p.sell();
    }
}

class Store{
    public void sell() {
        System.out.println("商店在卖货!");
    }
}

class CgLibProxy implements MethodInterceptor {
    private Store store;
    
    public CgLibProxy(Store store){
        this.store=store;
    }
    
    public Store proxy() {
        Enhancer enhancer = new Enhancer();//Enhancer是一个字节码增强器,可以用来为无接口的类创建代理。
        enhancer.setSuperclass(Store.class);//设置代理的目标类
        enhancer.setCallback(this);//照抄,全网都没有解释
        return (Store)enhancer.create();
    }
    
    @Override //接口要实现的方法
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("发财1");
        Object res=method.invoke(store, args);//和JDK代理一样,通过invoke调用方法
        System.out.println("发财2");
        return res;
    }
}
/*输出:
发财1
商店在卖货!
发财2
*/

CglibProxy类就像是一个代理工厂,写一个proxy()方法来生产真实类的代理对象。

 

6.小结

  • 静态代理:可以做到在不修改目标对象的功能前提下,对目标功能扩展;代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
  • JDK动态代理:代理对象不需要实现接口;但目标类一定要实现接口
  • Cglib动态代理:目标类不需要实现接口,可以是单独的类。

 

7.JDK代理 vs Cglib代理

JDK动态代理使用Java的反射技术生成代理类,只能代理实现了接口的类,没有实现接口的类不能实现动态代理,CGLib会在运行时动态的生成一个被代理类的子类,子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,不需要被代理类对象实现接口,从而CGLIB动态代理效率比Jdk动态代理反射技术效率要高

 


 

参考&引用

https://www.jianshu.com/p/bacaafb5d02d

posted @ 2020-05-04 23:34  守林鸟  阅读(157)  评论(0编辑  收藏  举报