架构师之路之设计模式三:代理模式

又到了周二,这代表快周末了,哈哈!但是本帅公司周六补班,你气不气!补就补吧,周日回家哈哈!

快过年了,在这里我先给大家拜个早年,祝大家身体健康、万事如意、来年发大财!

废话不多说,今天咱们来讨论一下设计模式中的代理模式

首先:什么叫做代理模式我的理解就是,通过其他人,给自己服务,自己只专注与自己本身的工作就行了!举个例子来说,链家(也就是租房子的),假如你是房东,你就是被代理类,后面称目标类,那么链家就是为你服务的,称之为代理类,你使用代理类的好处就是,你自己只需要专注买房子,收钱就行了,不用再去满大街宣传,每天带人看房子这些额外的工作,说白了,代理类就是给目标类增加一些额外的功能,同时,不改变自己本身的代码! 这么说的已经够明白了吧!

在研究代理类之前,我们先去模拟一下业务代码中的保存!

package com.proxy;

/**
 * 模拟业务类接口
 * 模拟保存功能
 * @author 皇甫
 */
public interface IUserService {
    /**
     * 保存方法
     * @param name
     */
    public void save(String name);
}
package com.proxy;

/**
 * 业务类的实现类
 * @author 皇甫
 */
public class UserServiceImpl implements IUserService {
    @Override
    public void save(String name) {
        System.out.println("-----------"+name+"保存成功------------");
    }
}

 以上我们模拟了一个保存的功能,现在你的老大有一个需求,需要你去给这个类增加事务属性,此时,最菜的做法是 

package com.proxy;

/**
 * 业务类的实现类
 * @author 皇甫
 */
public class UserServiceImpl implements IUserService {
    @Override
    public void save(String name) {
        System.out.println("开启事务");
        System.out.println("-----------"+name+"保存成功------------");
        System.out.println("关闭事务");
    }
}

 这样做你是完成了任务,但是你改变了业务的原有代码,使它不再专注于自己本身的业务,与java提倡的功能单一性理论相悖,而且他也会使业务逻辑变得更加复杂!此时咱们就可以使用设计模式中的代理模式。

代理类再我们日常的使用中被分为  静态代理类、基于jdk的动态代理类、基于cglib的动态代理类;咱们下面先讨论静态代理类,由浅入深,慢慢琢磨!

首先咱们先来看一下静态代理类,它的主要特点是代理类和目标类必须实现同一个接口,在代理类内部加上额外功能即可!至于缺点,咱们先看一下他的实现方式再来谈! 

package com.proxy;

/**
 *静态代理类 增加事务管理
 * @author 皇甫
 */
public class StaticProxy implements IUserService {
    private IUserService iUserService;

    public StaticProxy(IUserService iUserService) {
        this.iUserService = iUserService;
    }

    @Override
    public void save(String name) {
        try{
            System.out.println("事务开启");
            iUserService.save(name);
            System.out.println("事务提交");
        }catch (Exception e){
            System.out.println("事务回滚");
            e.printStackTrace();
        }finally {
            System.out.println("关闭资源");
        }
    }
}

测试 

     /**
     * 测试静态代理
     */
    @Test
    public void testStaticProxy(){
        IUserService iu = new UserServiceImpl();
        StaticProxy staticProxy = new StaticProxy(iu);
        staticProxy.save("大皇甫");
    }

 结果:


事务开启
-----------大皇甫保存成功------------
事务提交
关闭资源

我们重新定义一个业务代理类,让他继承和目标类一样的接口,实现同样的方法,我们就可以这样,既没有改变原有的业务代码,也完成了增加事务的需求;但是不知道大家发现没有这样一个缺点:增加事务的方法不止一个,假如我有200个需要事务的方法,你就需要写200个代理类来完成这个需求,就会产生类爆炸!实际业务开发中,根部不可能!所以我们有没有更好的办法呢?此时就需要引申出另外一个概念:基于jdk的动态代理类

 动态代理类必须实现InvocationHandler,覆盖invoke方法!通过new ProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler invocationHandler )返回一个实例,这个实例就是代理对象,使用ClassLoader来动态的生成代理对象,通过反射拿到目标类的构造函数,从而调用目标类的方法,其唯一参数类型是调用处理器接口类型!代码如下

package com.proxy;

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

/**
 * 动态代理类
 * @author 皇甫
 */
public class DynamicProxy implements InvocationHandler {
    private Object target;

    /**
     * @param target 目标类对象
     * @return 代理对象
     *
     * newProxyInstance
     * 参数1 拿到当前类的类加载器
     * 参数2 拿到目标对象所实现的接口
     * 参数3 InvocationHandler 当用户使用此代理类拦截时,待用本类的invoke方法
     */
    public Object newProxyInstance(Object target){
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * 执行拦截  增添额外功能
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke = null;
        try{
            System.out.println("开启事务");
            invoke = method.invoke(target, args);
            System.out.println("提交事务");
        }catch (Exception e){
            System.out.println("事务回滚");
            e.printStackTrace();
        }finally {
            System.out.println("关闭资源");
        }
        return invoke;
    }
}

测试:

    /**
     * 动态代理
     */
    @Test
    public void testDynamicProxy(){
        DynamicProxy d= new DynamicProxy();
        IUserService iu = (IUserService)d.newProxyInstance(new UserServiceImpl());
        iu.save("大皇甫");
    }

 结果:

开启事务
-----------大皇甫保存成功------------
提交事务
关闭资源

我们可以很清楚的看到,额外功能依旧被很轻松的追加上去了,日后如果你想给其他类增加额外的功能,只需要更换newProxyInstance(new UserServiceImpl());里面的实体类属性就ok了,但是基于jdk的动态代理只能对实现了接口的类进行代理,那么如何对没有接口的类进行代理呢!请看终极大boss,许多优秀的框架都集成了这个模式-----cglib代理!比如现在如日中天的SpringAOP 他可以代理没有接口的类,但是,他不能代理find修饰的类或者方法,他的本质其实是生成了一个代理类,来继承目标类;而find不能被继承,所以你懂的。。。。

使用这个代理,你需要先导入个jar,这里我给出maven的坐标,有兴趣的同学自行下载 

         <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.10</version>
        </dependency>

 cglib代理代码

package com.proxy;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Cglib代理
 * @author 皇甫
 */
public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object invoke = null;

        try{
            System.out.println("事务开启");
            System.out.println("执行之前方法名字"+method.getName());
            invoke = methodProxy.invokeSuper(o, objects);
            System.out.println("执行之后的方法名"+method.getName());
            System.out.println("事务提交");
        }catch (Exception e){
            System.out.println("事务回滚");
            e.printStackTrace();
        }finally {
            System.out.println("资源关闭");
        }
        return invoke;
    }
}

 测试:

 

    /**
     * 测试cglib
     */
    @Test
    public void testCglibProxy(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(new CglibProxy());
        UserServiceImpl iu = (UserServiceImpl)enhancer.create();
        iu.save("大皇甫");
    }

结果:

事务开启
执行之前方法名字save
-----------大皇甫保存成功------------
执行之后的方法名save
事务提交
资源关闭

 可以看到,我们依旧完成了对功能的添加,他可以可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口,但是不能代理find方法。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值