Java设计模式学习记录-代理模式

代理模式

代理模式是常见设计模式的一种,代理模式的定义是:为其余对象提供一种代理以控制对这个对象的访问html

在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。java

静态代理

理解设计模式是比较枯燥的,因此仍是以举例子的方式来进行理解,spring

例如:公司开年会想找个明星来表演,那么并不会直接联系明星(主要仍是联系不上),而是会联系明星的经纪人,明星就是被代理的对象,而经纪人就是代理对象。明星只须要准备来参加年会时应该表演什么节目就能够,其余的出场费之类的事情就交给经纪人来处理就行了。代理对象能够理解为被代理对象的扩展,能作被代理对象不能作的事情,也能够调用代理对象作事情。设计模式

那么用代码实现这个场景是什么样子的呢?框架

执行合做方法的接口ide

/**
 * @Description: 经纪公司接口,代理对象和被代理对象都须要实现的接口
 */
public interface Company {
    /** 合做 */
    void cooperation();
}

被代理对象函数

/**
 * @Description: 目标对象-明星(被代理对象)
 */
public class Start implements Company {

    @Override
    public void cooperation() {
        System.out.println("is show time");
    }
}

代理对象工具

/**
 * @Description: 经纪人(代理对象)
 */
public class Agent implements Company {

    private Company company;

    public Agent(Company company)
    {
        this.company = company;
    }

    @Override
    public void cooperation()
    {
        System.out.println("收出场费,化妆等等");
        company.cooperation();
        System.out.println("收拾行李,打道回府");
    }
}

测试类oop

import org.junit.Test;

/**
 * @Description: 测试类
 */
public class ProxyTest {

    @Test
    public void AnnualMeeting()
    {
        //目标对象
        Start start = new Start();
        //构建代理对象,生成代理关系
        Agent agent = new Agent(start);
        //用代理对象执行被代理对象的动做
        agent.cooperation();
        
    }

}

输出结果:性能

收出场费,化妆等等
is show time
收拾行李,打道回府

静态代理的特色是:能够在目标对象实现的基础上,加强额外的功能操做,即扩展目标对象的功能。有时候不方便修改别人的代码或者是引入的一个功能,须要进行功能扩展一下才能适用于本身的业务实现,可使用代理模式来进行设计。

可是静态代理的实现基础是一个目标对象对应一个代理对象,而且在编译时就已经维护好了代理关系,若是目标对象是多个那么就会须要多个代理对象,这样在更新目标的对象的时候还须要更新代理对象,当代理对象持续增长时维护成本就变得很是困难。

针对于这种状况,动态代理应运而生。

动态代理

JDK代理

动态代理的代理对象不须要和目标对象共同实现接口,而是利用JDK的API,动态的在内存中构建代理对象。

动态生成代理对象须要调用JDK中的java.lang.reflect.Proxy类的newProxyInstance方法,这个方法须要三个参数:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,]Class<?>[] interfaces,InvocationHandler h)
ClassLoader loader:类加载器,用来加载目标对象类,由于是在运行时得到目标对象,因此确定须要用到反射。
Class<?>[] interfaces:目标对象类实现的接口集合,这些接口中定义目标对象能够执行的方法。
InvocationHandler h:这个参数表明的是动态代理对象在调用方法的时候,会将方法转发到哪个invocationHandler对象身上,InvocationHandler是个接口,
须要本身实现它,而后定义本身的动态代理执行方法。
建立包含动态代理对象具体执行方法的实现类。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Description: 包含动态代理对象具体执行方法的实现类
 */
public class MyInvocationHandler implements InvocationHandler {

    private Company company;

    public MyInvocationHandler(Company company)
    {
        this.company = company;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {

        System.out.println("收出厂费,化妆等");
        //具体执行方法
        Object result = method.invoke(company,args);

        System.out.println("收拾现场,卸妆,打道回府");

        return result;
    }
}

测试类

import org.junit.Test;
import java.lang.reflect.Proxy;

/**
 * @Description: 测试类
 */
public class DynamicProxyTest {

    @Test
    public void AnnualMeeting()
    {
        //建立目标对象
        Company start = new Start();
        //建立代理对象须要执行的方法处理对象
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(start);
        //得到目标对象的类加载器
        ClassLoader classLoader = start.getClass().getClassLoader();
        //建立动态代理对象
        Company proxy = (Company) Proxy.newProxyInstance(classLoader,start.getClass().getInterfaces(),myInvocationHandler);
        //用动态代理对象执行目标对象的方法
        proxy.cooperation();
    }
}

输出结果:

收出厂费,化妆等
is show time
收拾现场,卸妆,打道回府

JDK动态代理的特色:代理对象不须要实现接口,可是目标对象必须实现接口。

那么若是在实际的业务中目标对象确实没有实现接口,怎么办呢?

遇到这种状况的时候就须要时cglib动态代理了。

Cglib代理

Cglib代理,也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,若是想代理没有实现接口的类,就可使用Cglib实现。
  • Cglib是一个强大的高性能的代码生成包,它能够在运行期扩展java类与实现java接口.它普遍的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)。
  • Cglib包的底层是经过使用一个小块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,由于它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

在实现cglib代理时须要引入cglib的jar包,可是spring核心功能已经包含了cglib的功能,因此引入spring-core的jar包就能够了。

须要注意的是:代理的类不能为final,不然报错,目标对象的方法若是为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

没有实现接口的目标对象类

/**
 * @Description: 没有经纪公司的明星,就行像最近以我的练习生出道的蔡徐坤
 */
public class AloneStart {
    /** 合做 */
    public void cooperation() {
        System.out.println("is show time");
    }

}

生成Cglib代理对象的类

import org.mockito.cglib.proxy.Enhancer;
import org.mockito.cglib.proxy.MethodInterceptor;
import org.mockito.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

/**
 * @Description: 生成代理对象的类
 */
public class CglibProxy implements MethodInterceptor {


    private AloneStart aloneStart;

    public CglibProxy(AloneStart aloneStart)
    {
        this.aloneStart = aloneStart;
    }

    /**
     * 建立代理对象
     * @return
     */
    public Object getProxyInstance()
    {
        //动态代理工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(aloneStart.getClass());
        //设置回调函数调用对象
        enhancer.setCallback(this);
        //返回代理对象
        return enhancer.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable
    {
        System.out.println("收出厂费,化妆等");
        //执行代理方法
        methodProxy.invokeSuper(obj,objects);
        System.out.println("卸妆,回家");
        return null;
    }
}

测试类

import org.junit.Test;

/**
 * @Description: 测试类
 */
public class CglibProxyTest {

    @Test
    public void cglibTest()
    {
        //建立目标对象
        AloneStart aloneStart = new AloneStart();
        //建立代理对象
        AloneStart startProxy = (AloneStart) new CglibProxy(aloneStart).getProxyInstance();
        //用代理对象执行目标对象的方法
        startProxy.cooperation();
    }
}

输出结果:

收出厂费,化妆等
is show time
卸妆,回家

jdk采用反射机制调用委托类的方法,而cglib采用相似索引的方式直接调用委托类方法;

还有须要注意的是:

在Spring的AOP中

若是加入容器的目标对象有实现接口,用JDK代理
若是目标对象没有实现接口,用Cglib代理

 参考:

Java的三种代理模式: https://www.cnblogs.com/cenyu/p/6289209.html

说说代理模式:http://www.importnew.com/26116.html

相关文章
相关标签/搜索