Java设计模式--代理模式与JDK动态代理,cglib动态代理

一.代理模式的概念

即Proxy Pattern,23种常用的面向对象软件的设计模式之一。

代理模式的定义:

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,因此创建代理对象,代理对象在客户端和目标对象之间起到中介的作用。

代理模式的组成部分:

代理模式使用三个角色实现其目标功能,分别是:
抽象角色(AbstractRole):
通过接口或抽象类声明真实角色实现的业务方法。
代理角色(ProxyRole):
实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色(RealRole):
实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

代理模式分类:

代理模式根据其实现方法的不同分为动态代理和静态代理,静态代理和动态代理的主要区别在于其生成代理类的方式,静态代理由程序员直接编写代理类,而动态代理的代理类是在程序执行过程中生成的

二.静态代理

静态代理根据代理类的生成方式分为聚合式静态代理和继承式静态代理
聚合式静态代理中代理类和目标类都实现了同一个接口,又称为代理类聚合了目标类,可实现灵活多变
继承式静态代理中代理类继承了目标类,减少了代码量(不必对不进行功能扩展的方法进行覆盖),但是不够灵活

2.1 聚合式静态代理示例:

该程序由4部分组成:

接口:抽象角色(AbstractRole)

public interface IUserDao {
    void save();
    void find();
}

目标类:即真实角色(RealRole)

public class UserDao implements IUserDao{
    @Override
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
    @Override
    public void find() {
        System.out.println("查询");
    }
}

代理类:代理角色(ProxyRole)
由程序员根据需要(扩展功能)编写

public class UserDaoProxy implements IUserDao{
    //代理对象,需要维护一个目标对象
    private IUserDao target = new UserDao();
    //代理对象对目标对象进行功能扩展,使得代理对象能够完成目标对象不能完成的任务
    @Override
    public void save() {
        System.out.println("代理操作: 开启事务...");
        target.save();
        System.out.println("代理操作:提交事务...");
    }
    //不需要进行功能扩展的方法则直接调用目标对象中对应的该方法
    @Override
    public void find() {
        target.find();
    }
}

测试方法:

public class Application {
    public static void main(String[] args) {
        //创建代理对象
        IUserDao proxy = new UserDaoProxy();
        proxy.find(); 
        proxy.save(); 
    }
}

程序执行结果:

Java设计模式--代理模式与JDK动态代理,cglib动态代理_第1张图片

代理类UserDaoProxy扩展了目标类UserDao的功能,增强了save( )方法的功能,原本save( )方法只输出一句话,进行功能增强后多输出了两句话

2.1 继承式静态代理示例:

该程序由3部分组成:不需要接口即抽象角色(AbstractRole)

目标类:即真实角色(RealRole)

public class UserDao {
    @Override
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
    @Override
    public void find() {
        System.out.println("查询");
    }
}

代理类:代理角色(ProxyRole)
由程序员编写

public class UserDaoProxy extends UserDao{
    //代理对象对目标对象进行功能扩展
    @Override
    public void save() {
        System.out.println("代理操作: 开启事务...");
        target.save();
        System.out.println("代理操作:提交事务...");
    }
}

测试方法:

public class Application {
    public static void main(String[] args) {
        //创建代理对象
        IUserDao proxy = new UserDaoProxy();
        proxy.find(); 
        proxy.save(); 
    }
}

继承式静态代理实现的功能和聚合式静态代理相同

三.动态态代理

动态代理中的代理类是在程序执行过程中生成的,而不是由程序员直接编写的
动态代理根据代理类生成方法的不同分为JDK动态代理和cglib动态代理,和静态代理中聚合式与继承式的分类相同,JDK动态代理中代理类和目标类都实现了同一个接口,cglib动态代理中代理类则继承了目标类

3.1 JDK动态代理

JDK动态代理主要使用了反射技术,同时还要用到JavaSE中的Proxy类和InvocationHandler接口

3.1.1 动态代理涉及的类和接口

1)Proxy类

Java设计模式--代理模式与JDK动态代理,cglib动态代理_第2张图片

Proxy类在reflect包下,与反射的关系紧密
Proxy类中的方法:

Java设计模式--代理模式与JDK动态代理,cglib动态代理_第3张图片

其中第4个方法newProxyInstance( )最常用,用来创建代理类,其三个参数分别为(1)一个类加载器,(2)代理类和目标类共同实现的接口,(3)一个实现了InvocationHandler接口的对象

2)InvocationHandler接口

InvocationHandler接口的主要用途就是作为Proxy类的newProxyInstance( )方法的参数,用来创建代理对象。其有4个实现类
InvocationHandler接口只有一个invoke( )方法

Java设计模式--代理模式与JDK动态代理,cglib动态代理_第4张图片

3.1.2 动态代理示例:

下面展示一个静态代理程序,和静态代理一样,由4部分组成:

接口:抽象角色(AbstractRole)

//和静态代理一样,没有更改
public interface IUserDao {
    void save();
    void find();
}

目标类:即真实角色(RealRole)

//和静态代理一样,没有更改
public class UserDao implements IUserDao{
    @Override
    public void save() {
        System.out.println("模拟: 保存用户!");
    }
    @Override
    public void find() {
        System.out.println("查询");
    }
}

代理类:代理角色(ProxyRole)
动态代理中,代理类是由程序生成的,这时不直接编写代理类,而是编写一个专门生成代理类的工厂类ProxyFactory,由ProxyFactory根据传入的参数生成代理类
ProxyFactory

public class ProxyFactory {
    //构造方法,接收一个目标对象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //代理对象的创建方法
    public Object getProxyInstance() {
        //调用Proxy类的newProxyInstance()方法
        Object proxy = Proxy.newProxyInstance(
                //参数一:目标对象使用的类加载器
                target.getClass().getClassLoader(),  
                //参数二:目标对象实现的所有接口
                target.getClass().getInterfaces(),   
                //参数三:一个InvocationHandler对象的实现类,这里使用适配器模式创建
                //执行代理对象方法时才执行此方法,创建代理对象是不执行此方法
                new InvocationHandler() {           
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //获取当前执行的方法的方法名
                        String methodName = method.getName();
                        //方法返回值
                        Object result = null;
                        //判断程序调用的代理类的方法
                        if ("find".equals(methodName)) {
                            //不需要进行功能扩展的方法,反射创建目标类的方法并调用
                            result = method.invoke(target, args);
                        } 
                        //需要进行功能扩展的方法
                        else {
                            //进行功能扩展
                            System.out.println("开启事务...");
                            //反射创建目标类的方法并调用
                            result = method.invoke(target, args);
                            System.out.println("提交事务...");  
                        }
                        //返回结果
                        return result;
                    }
                }
        );
        return proxy;
    }
}

测试方法:

public class Application {
    public static void main(String[] args) {    
        //创建目标对象
        IUserDao target = new UserDao();
        System.out.println("目标对象:" + target.getClass());  // class cn.itcast.b_dynamic.UserDao
        //创建代理对象,使用代理工厂
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println("代理对象: " + proxy.getClass());  //  class $Proxy0
        //执行代理对象的方法
        proxy.find();
        proxy.save();
    }
}

程序执行结果:

Java设计模式--代理模式与JDK动态代理,cglib动态代理_第5张图片

3.2 cglib动态代理

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就需要使用cglib实现
cglib是一个强大的,高性能,高质量的code生成类库,它可以在运行期通过生成代理对象的方式扩展目标类的功能,它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)
cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉

目标类:即真实角色(RealRole)

public class UserDao {
    public static void save() {
        System.out.println("模拟: 保存用户!");
    }
    public void find() {
        System.out.println("查询");
    }
}

代理类:代理角色(ProxyRole)

public class ProxyFactory implements MethodInterceptor{
    //接收一个目标对象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //返回代理对象——一个目标对象的子类对象
    public Object getProxyInstance() {
        //字节码生成工具类
        Enhancer en = new Enhancer();
        //设置父类
        en.setSuperclass(target.getClass());
        //设置回调函数
        en.setCallback(this);
        //创建子类对象
        return en.create();
    }
    // 事件处理器,执行目标方法时候执行
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务...");
        Object result = method.invoke(target, args);
        System.out.println("提交事务...");
        return result;
    }
}

测试方法:

public class App {
    public static void main(String[] args) {
        //创建目标对象
        UserDao target = new UserDao();
        System.out.println("目标对象:" + target.getClass());
        //创建代理对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println("代理对象: " + proxy.getClass());
        //执行代理对象的方法
        proxy.find();
        proxy.save();
    }
}

总结:

经过静态代理和动态代理实现方法的分析,可以看出动态代理的优势:如果对多个目标类进行同样的功能扩展,使用静态代理需要为每个目标类创建一个代理对象,耦合性非常高,可复用性非常低,而使用动态代理,则只需要创建一个代理工厂,代理对象可以用代理工厂创建,这样扩展功能只需编写一遍就可以对所有目标类进行功能扩展

你可能感兴趣的:(JavaSE基础,JavaSE基础)