内容学习于:edu.aliyun.com


1. 静态代理设计模式弊端

  代理设计模式最为核心的意义在于,所有的操作业务接口都设置两个子类,一个子类负责真实的业务实现,另外一个子类负责代理业务操作,如果没有这个代理业务,真实业务也无法进行处理。
  现在假设说希望可以实现一个数据的处理操作,在进行数据处理的时候,要求进行合理的事务控制,在数据库里面永远都会存在有一个事务的概念,利用事务可以保证数据操作的完整性。

  如下图所示:

编写传统代理模式:

interface IMemberService{//实现用户数据操作
    public void add();//实现用户数据追加
}

class MemberServiceImpl implements IMemberService{

    @Override
    public void add() {
        System.out.println("【真实业务主题】向数据库添加数据");
    }
}

class MemberServiceProxy implements IMemberService{
    private IMemberService realSubject;//真实操作业务

    public MemberServiceProxy(IMemberService realSubject){
        this.realSubject = realSubject;
    }

    public boolean connect(){
        System.out.println("【代理主题】进行数据库的访问连接...");
        return true;
    }
    public void close(){
        System.out.println("【代理主题】关闭数据库连接...");
    }

    public void transaction(){
        System.out.println("【代理主题】事务提交,进行数据更新处理");
    }
    @Override
    public void add() {
        if (this.connect()){
            this.realSubject.add();
            this.transaction();
            this.close();
        }
    }
}

class Factor {
    private Factor(){}
    public static IMemberService getInstance(){
        return new MemberServiceProxy(new MemberServiceImpl());
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        IMemberService memberService = Factor.getInstance();
        memberService.add();
    }
}

结果:

【代理主题】进行数据库的访问连接…
【真实业务主题】向数据库添加数据
【代理主题】事务提交,进行数据更新处理
【代理主题】关闭数据库连接…

  但是现在需要进一步思考一个问题,在一个数据库里面,有可能会存在有上百张表,如果每一张数据表都存在有一个XxxService接口的时候,这个时候代理就会泛滥,而且泛滥的结果会发现所有的代理步骤几乎雷同。

  所以之前的静态代理设计模式设计上只能够满足于一个操作接口的需求,而无法满足于所有操作接口的需求(前提:操作的形式类似)。

2. 动态代理设计模式

  如果要想解决好静态代理设计模式所存在的代码重复的操作问题,就只能够利用动态代理设计模式来解决,而所谓的动态代理设计模式指的是一组相关的操作接口的实现,可以设置统一的代理类。

  如下图所示:
  动态代理类是在JDK 1.3 的时候添加到项目之中的,如果要想实现动态代理类需要“Invocation"接口和“Proxy类"的支持,首先来观察java.lang.reflect.InvocationHandle接口的定义:

  • public interface InvocationHandler {public 0bject invoke (Object proxy, Method method, Object[] args) throws Throwable ;

  此时对于代理设计需要解决的一个核心问题在于,如何可以让InvocationHandler子类和要代理操作的业务接口产生关联,所以此时就需要通过java.lang.eflect.Proxy类来进行关联的创建,创建关联的方法:

  • public static Object newProxyInstance(ClassL oader loader, Class<?>[] interfaces, InvocationHandler h)

  这种代理对象的创建是依据真实主题类的对象的加载器,和其实现的父接口动态创建的一个新的子类,该子类由JVM在运行的时候自行负责创建。

  如下图所示:

动态代理模式:

interface IMemberService {//实现用户数据操作

    public void add();//实现用户数据追加
}

class MemberServiceImpl implements IMemberService {

    @Override
    public void add() {
        System.out.println("【真实业务主题】向数据库添加数据");
    }
}

class ServiceProxy implements InvocationHandler {
    private Object target;//真实操作业务

    /** * 绑定真实主题对象,同时返回代理实例 * @param target 真正的接口操作对象,利用反射可以追踪其来源 * @return 代理对象 */
    public Object bind(Object target){
        this.target =  target;//保存真实业务对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),this);
    }

    public boolean connect() {
        System.out.println("【代理主题】进行数据库的访问连接...");
        return true;
    }

    public void close() {
        System.out.println("【代理主题】关闭数据库连接...");
    }

    public void transaction() {
        System.out.println("【代理主题】事务提交,进行数据更新处理");
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object returnValue = null;
        if (this.connect()){
            returnValue = method.invoke(this.target,args);//调用真实主题
            this.transaction();
            this.close();
        }
        return returnValue;
    }
}

class Factor {
    private Factor() {
    }

    public static IMemberService getInstance() {
        return (IMemberService) new ServiceProxy().bind(new MemberServiceImpl());
    }
}

public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        IMemberService memberService = Factor.getInstance();//得到一个jvm动态实例化子类对象
        memberService.add();
    }
}

结果:

【代理主题】进行数据库的访问连接…
【真实业务主题】向数据库添加数据
【代理主题】事务提交,进行数据更新处理
【代理主题】关闭数据库连接…

  此时的代码利用动态代理设计类动态的构建了接口的实现子类实例,并且利用InvocationHandler.invoke(实现标准的代码执行调用,在里面进行代理控制。

3. CGLIB实现动态代理

  现在已经实现了一个代理设计,而且这种代理设计是Java官方推荐的唯一实现机制,但是这种实现机制有部分开发人员认为有问题,在面向对象的角度来讲,不一定非要使用接口来进行标准的定义,有些人认为类应该可以作为标准型的定义形式,所以这类人觉得如下的代码设计不好:

  • return Proxy. newProxyInstance(target. getClass (). getClassLoader(),
    target. getClass (). getInterfaces(), this) ;

  在使用Proxy类创建动态代理类实例的时候必须要求有接口“target.getClass().getInterfaces()",所以有一部分不认可此设计的人员就依据自己的习惯设计了一个基于类的代理操作,可以抛开接口存在的意义,实现代理模式,于是这样的背景下就产生了一个第三方应用开发包“cglib" (cglib-nodep-3.2. 10.jar)。

通过cglib实现代理:

class Message {
    public void send(String msg) {
        System.out.println("【真实业务】" + msg);
    }
}

class MessageProxy implements MethodInterceptor {//cglib是基于拦截器实现的
    private Object target;//真实操作对象

    public MessageProxy(Object target) {
        this.target = target;
    }


    public boolean connect() {
        System.out.println("【代理主题】进行访问连接...");
        return true;
    }

    public void close() {
        System.out.println("【代理主题】关闭连接...");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object returnValue = null;
        if (this.connect()) {
            returnValue = method.invoke(this.target, objects);
            this.close();
        }
        return returnValue;
    }
}


public class JavaAPIDemo {
    public static void main(String[] args) throws Exception {
        Message realObject = new Message();//真实业务主题
        Enhancer enhancer = new Enhancer();//负责代理控制的类
        enhancer.setSuperclass(realObject.getClass());//模拟一个操作父类
        enhancer.setCallback(new MessageProxy(realObject));//设置代理对象
        Message proxyObject = (Message) enhancer.create();//创建代理对象,类似于proxy
        proxyObject.send("www.mldn.cn");
    }
}

  此时的操作代码在没有实现父接口的情况下实现类的代理设计,但是这样的设计只是一种传统JDK中的代理操作实现的补充,可以利用其打破原始的设计要求。

  <mark>一定要掌握Proxy + InvocationHandle接口的联合使用。</mark>