代理模式(JDK动态代理、Cglib代理,静态代理)

一、JDK动态代理

 JDK动态代理  也成为"接口代理"

1.在不改变目标类中方法的基础上,通过动态代理,实现对原有方法的增强
2.目标类要实现接口,否则不能用动态代理
3.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

 优点:
      1.在做目标类中方法的增强时,可以不去改变目标类原有方法的业务逻辑
      2.在程序的拓展性能上更好了,维护更加方便

 缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的

public class DynamicProxyBankLogService {

    //目标对象
    private IBankService target;

    public DynamicProxyBankLogService(IBankService target){
        this.target = target;
    }

    //获得代理对象的方法
    public IBankService getProxy(){
        //代理对象
        IBankService proxy = null;

        //类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();

        //获取目标对象实现的所有接口
        Class<?>[] interfaces = target.getClass().getInterfaces();

        //执行器
        InvocationHandler handler = new InvocationHandler() {
            /**
             * 将当前编写的增强功能,作用在目标对象的方法之上
             * @param proxy  代理对象,一般不使用
             * @param method 目标类中的所有方法
             * @param args   目标类中的所有方法的形参列表
             * @return 目标类中方法的返回值,若没有返回值则返回null
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //long l1 = System.currentTimeMillis();
                //method 当前正在作用在哪一个方法之上  methodName方法名
                String methodName = method.getName();
                //日志追踪
                System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));

                //利用反射执行目标方法  invoke(执行哪一个对象的方法 , 方法所需要的形参)
                //返回方法的执行结果  若目标方法没有返回值则返回null
                Object result = method.invoke(target, args);

                //计算耗时  +" , 此方法耗时:"+(l2-l1)/1000.0+"s"
                //long l2 = System.currentTimeMillis();
                System.out.println("结束日志追踪:the method "+methodName+" end with "+result );
                return result;
            }
        };

        proxy = (IBankService)Proxy.newProxyInstance(classLoader, interfaces, handler);
        return proxy;
    }
}

二、Cglib代理

Cglib代理模式
在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
优点:
      1.若目标对象不需要实现接口,用Cglib代理。
      2.做业务逻辑方法的增强时,不需要在原有方法上去做改变。
      3.让程序拓展性更好,维护更加方便
缺点:
      实现MethodInterceptor拦截器接口

 步骤:
      1.当前代理类实现MethodInterceptor拦截器接口,重写拦截器方法
      2.提供一个目标对象,提供一个获得代理对象的方法

 注意事项:
1.必须给目标类中提供公开的无参构造方法
IllegalArgumentException: Superclass has no null constructors but no arguments were given
2.目标类不可以使用final修饰,使用后将无法为其创建子类
IllegalArgumentException: Cannot subclass final class
3.目标类中的目标方法不可以使用final修饰,使用后,拦截器中的拦截方法,会拦截不到final的方法
4.目标类中的目标方法不可以使用static修饰,使用后,拦截器中的拦截方法,会拦截不到static的方法

public class CglibProxyBankLogService implements MethodInterceptor {

    //目标对象
    private BankService target;

    public CglibProxyBankLogService(BankService target){
        this.target = target;
    }

    //获取代理对象的方法
    public BankService getProxy(){
        BankService proxy = null;
        //在内存中构建一个子类对象从而实现对目标对象功能扩展
        Enhancer enhancer = new Enhancer();
        //设置其父类
        enhancer.setSuperclass(BankService.class);
        //设置回调函数  this就是CglibProxyBankLogService,CglibProxyBankLogService implements MethodInterceptor extends Callback
        enhancer.setCallback(this);
        /*
         * enhancer.create()  在内存中构建一个子类对象
         * 1.找到其父类类型Superclass,利用其无参构造方法来创建子类对象
         * 2.创建完毕,回调执行Callback,此处this是拦截器 -->  执行回调函数,就是在执行拦截方法intercept()
         */
        proxy = (BankService)enhancer.create();
        return proxy;
    }

    /**
     * 拦截器方法中实现,目标类中希望增强的非业务逻辑
     * @param proxy   代理对象,此处不用
     * @param method  目标类中的方法对象
     * @param args    目标类中的方法的形参列表
     * @param methodProxy 方法的代理对象,此处不用
     * @return  执行目标类中方法的返回值,若没有返回值则返回null
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long l1 = System.currentTimeMillis();
        //method 当前正在作用在哪一个方法之上  methodName方法名
        String methodName = method.getName();
        //日志追踪
        System.out.println("开启日志追踪:the method "+methodName+" begin with "+ Arrays.asList(args));

        //利用反射执行目标方法  invoke(执行哪一个对象的方法 , 方法所需要的形参)
        //返回方法的执行结果  若目标方法没有返回值则返回null
        Object result = method.invoke(target, args);

        //计算耗时
        long l2 = System.currentTimeMillis();
        System.out.println("结束日志追踪:the method "+methodName+" end with "+result +" , 此方法耗时:"+(l2-l1)/1000.0+"s" );
        return result;
    }
}

三、静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。

 使用前提:无论是目标对象还是代理对象,都必须一起实现相同的接口

 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
 缺点:
      1.目标类和代理类都需要实现接口,而且是相同接口
      2.若采用静态代理模式,代理类会急剧膨胀
      3.一旦接口中有新增方法,无论是目标类还是代理类,都需要新增方法,拓展性和维护性很差

public class StaticProxyBankLogService implements IBankService{

    //目标对象
    private IBankService target;

    public StaticProxyBankLogService(IBankService target){
        this.target = target;
    }

    @Override
    public boolean login(Long id, String pwd) {
        System.out.println("开启日志追踪:the method login begin with [ "+id+" , "+pwd+" ]");
        long l1 = System.currentTimeMillis();
        //调用业务逻辑
        boolean flag = target.login(id, pwd);
        long l2 = System.currentTimeMillis();
        System.out.println("结束日志追踪:the method login end with "+flag+" , login方法耗时:"+(l2-l1)/1000.0+"s");
        return flag;
    }

    @Override
    public void register(Long id, String pwd, String name) {
        System.out.println("开启日志追踪:the method register begin with [ "+id+" , "+pwd+" , "+name+" ]");
        long l1 = System.currentTimeMillis();
        //调用业务逻辑
        target.register(id,pwd,name);
        long l2 = System.currentTimeMillis();
        System.out.println("结束日志追踪:the method register end with "+" , register方法耗时:"+(l2-l1)/1000.0+"s");
    }

}

 


在这里贴一个IBankService和BankService类,方便大家用于测试

//IBankService 接口
public interface IBankService {
    //登陆
    public boolean login(Long id, String pwd);

    //注册
    public void register(Long id, String pwd, String name);
}

//BankService 实现类
public class BankService implements IBankService {
    //登陆
    @Override
    public boolean login(Long id, String pwd) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(id.equals(1L) && "123".equals(pwd)){
            return true;
        }
        return false;
    }

    //注册
    @Override
    public void register(Long id, String pwd, String name) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("id = "+id+" , pwd = "+pwd+" , name = "+name);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值