代理模式?就这?

(一)什么是代理模式?
我们在编程中有一个思想:不要随便改动源代码 !那么我们要给一个对象提供更多的方法功能,我们第一时间想到的是修改源码,实际上这种方法并不可取,我们提供了一个更为科学的方法,大体逻辑如下图:

avatar

代理(proxy)模式 是一种设计模式,提供了对目标对象的另一种访问形式,也就是通过代理对象来访问目标对象,通过代理对象间接的为目标对象进行功能的增强,扩展目标对象的功能,用户调用代理对象实现我们的需求。
(二)静态代理
静态代理 必须定义接口或者父类,实现目标对象与代理对象时必须实现同一个接口或者父类。

举个栗子:
  • 接口:UserDao
/** 
* 接口 
*/
  public interface UserDao {
  void save(); 
}
  • 目标对象:UserDaoImp
/** 
* 接口实现 
* 目标对象 
*/
 public class UserDaoImp implements UserDao {
 public void save() { 
 System.out.println("----已经保存数据!----");
 } 
}
  • 代理对象:UserDapImpProxy
/**
 * 代理对象,静态代理 
*/ 
   public class UserDaoImpProxy implements UserDao{
   //接收保存目标对象  
   private UserDao target;
   public UserDaoImpProxy(UserDao target){
    this.target=target; 
   } 
  public void save() { 
   System.out.println("开始事务...");
   target.save();
  //执行目标对象的方法 
   System.out.println("提交事务..."); 
   } 
}
  • 测试类:Test
/** 
* 测试类
 */ 
   public class Test{
   public static void main(String[] args) { 
  //目标对象
   UserDao target = new UserDaoImp();
   //代理对象,把目标对象传给代理对象,建立代理关系
   UserDaoImpProxy proxy = new UserDaoImpProxy(target);
   proxy.save();//执行的是代理的方法
   } 
}
  • 测试类输出数据

开始事务…
----已经保存数据!----
提交事务…

我们发现并没有改动源码,但是我们不单单执行了我们需要的功能,还对功能进行了扩展,这是一种非常优秀的设计思想,对我来说算是涨知识啦~但是如此优秀的静态代理仍然有着其致命的缺陷:代理对象和目标对象都要实现相同的接口或父类,因此当需要扩展的方法或多,会出现许多代理对象类,当接口的方法发生变化,代理对象类也要随着修改。这无疑是一件非常头疼的事情,于是我们可以引出我们的 动态代理,完美解决这个问题。
(三)动态代理
动态代理 也叫JDK原生代理,代理对象不需要实现接口,根据JDK的API动态的在内存中创建代理对象。

我们查一查JDK中创建代理对象的API:
1.代理类所在的包:java.lang.reflect.Proxy
2.我们实现代理只需要使用Proxy中一个又长又臭的静态方法:

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

我们需要关注方法的三个参数:
  • ClassLoader loade:目标对象的类加载器
  • Class<?>[] interfaces:目标对象实现的接口
  • InvocationHandler h:事件处理,调用目标对象的方法时被触发,拦截被执行的方法进行功能扩展后放行
举个栗子理解一下:
  • 代理工厂类:ProxyFactory
/**
 * 代理工厂
 * 负责动态创建代理对象
 */
public class ProxyFactory {

    //目标对象
    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }
    //给目标对象生成代理对象
    public Object getProxyInstance() {
    //代理类的静态方法
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始事务...");
                        //执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务...");
                        return returnValue;
                    }
                }
         );
       }
     }
  • 测试类:Test
/**
 * 测试类
 */
public class App {
    public static void main(String[] args) {
        // 目标对象
        UserDaoImp target = new UserDaoImp ();
        // 在代理工厂创建代理对象
        UserDaoImp proxy = (UserDaoImp ) new ProxyFactory(target).getProxyInstance();
        // 执行代理对象被增强后的方法
        proxy.save();
    }
}
  • 测试类输出数据

开始事务…
----已经保存数据!----
提交事务…

动态代理中代理对象无需实现接口,因此不会存在静态代理中存在的致命问题,但是目标对象仍然需要实现接口,否则无法使用动态代理,因此动态代理的缺陷就产生了:当目标对象是一个单独的对象,没有实现接口,我们要如何对其进行代理?聪明的程序猿们想出了一种解决方式:Cglib代理
(四)Cglib代理
Cglib代理,也叫作子类代理,它是在内存中动态构建一个目标对象的子类对象,对子类进行功能的扩展与增强,从而达到子类对象即代理对象的目的。

avatar

我们先百度一下什么是Cglib:

1.Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的interception(拦截).
2.Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

然后我们百度一下怎么使用Cglib:

1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

多说无益举个栗子:
  • 目标对象:UserDao
/**
 * 目标对象,没有实现任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已经保存数据!----");
    }
}
  • Cglib代理工厂:ProxyFactory
/**
 * Cglib子类代理工厂
 * 对UserDao在内存中动态构建一个子类对象
 */
public class ProxyFactory implements MethodInterceptor {
    //目标对象
    private Object target;

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

    //给目标对象创建一个子类对象
    public Object getProxyInstance() {
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类
        return en.create();

    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");

        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);

        System.out.println("提交事务...");

        return returnValue;
    }
}
  • 测试类:Test
/**
 * 测试类
 */
public class App {

    @Test
    public void test() {
        //目标对象
        UserDao target = new UserDao();

        //目标对象的子类对象
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();

        //执行子类对象的方法
        proxy.save();
    }
}
  • 测试类输出数据

开始事务…
----已经保存数据!----
提交事务…

在Spring框架的AOP编程中,如果加入容器的目标对象有实现接口就用JDK动态代理,如果目标对象没有实现接口则用Cglib代理,AOP是Spring里的灵魂,在我看来AOP(面向切面编程)是一种非常优秀的设计思想,非常值得进行研究,所以我下一篇要写写对Spring AOP的总结,加油!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mushroom-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值