设计模式(一)三种代理模式以及什么时候使用

1.为什么要使用动态代理?

每个解决方案都是为了去解决一个问题,那么为什么使用动态代理呢,那就要看他解决了什么问题
日志:在程序执行期间追踪正在发生的活动
什么是代理模式
先说动态代理之jdk代理

1.1 实现

Spring AOP实现方式有两种

1:使用JDK动态代理,如果被代理的目标实现了至少一个接口,则会使用JDK动态代理,所有该目标类型实现的接口都将被代理。

2:通过CGLIB来为目标对象创建代理,若该目标对象没有实现任何接口,则创建一个CGLIB代理,创建的代理类是目标类的子类。

链接:https://www.jianshu.com/p/a0c349e813eb

2.动态代理使用前提:

标记为final的方法,不能被代理,因为无法进行覆盖
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理。
实现与解释:

 代理类所在包:java.lang.reflect.Proxy
  JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:

ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

在这里插入图片描述

2.1动态代理实现(代码)

JrmkApplyService jrmkApplyproxy = (JrmkApplyService) Proxy.newProxyInstance(JrmkApplyService.class.getClassLoader(), new
        Class[]{JrmkApplyService.class}, new DynamicProxyHandler(JrmkApplyServiceImpl));
package com.sdecloud.efpx.manager.handler;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

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

/**
 *
 *    * @className: DynamicProxyHandler
 *   * @description:TODO
 *   * @param:
 *   * @return:
 *   * @throws:
 *   * @author: lizz
 *   * @date: 2020/06/05 11:07
 *
 */
@Transactional
public class DynamicProxyHandler  implements InvocationHandler {
            private Object object;
            public DynamicProxyHandler(final Object object) {
                 this.object = object;
            }
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                 System.out.println("before");

                 Object result = method.invoke(object, args);
                 if("1".equals("1")){
                     throw new Throwable();
                 }
                 System.out.println("after");
                 return result;
             }
}
public interface ArithmeticCalculator {

int add(int i, int j);
int sub(int i, int j);
int mul(int i, int j);
int div(int i, int j);
}
**然后写接口的实现类**

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }

}

**然后写实现动态代理的类**
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ArithmeticCalculatorLoggingProxy {

    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCalculatorLoggingProxy(ArithmeticCalculator target) {
        super();
        this.target = target;
    }

    //返回代理对象
    public ArithmeticCalculator getLoggingProxy(){
        ArithmeticCalculator proxy = null;

        ClassLoader loader = target.getClass().getClassLoader();
        Class [] interfaces = new Class[]{ArithmeticCalculator.class};//这个class是代理对象实现的接口
        InvocationHandler h = new InvocationHandler() {
            /**
             * proxy: 代理对象。 一般不使用该对象
             * method: 正在被调用的方法
             * args: 调用方法传入的参数
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                String methodName = method.getName();
                //打印日志
                System.out.println("[before] The method " + methodName + " begins with " + Arrays.asList(args));

                //调用目标方法
                Object result = null;

                try {
                    //前置通知
                    result = method.invoke(target, args);
                    //返回通知, 可以访问到方法的返回值
                } catch (NullPointerException e) {
                    e.printStackTrace();
                    //异常通知, 可以访问到方法出现的异常
                }

                //后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值

                //打印日志
                System.out.println("[after] The method ends with " + result);

                return result;
            }
        };

        /**
         * loader: 代理对象使用的类加载器。 
         * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. 
         * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法
         */
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(loader, interfaces, h);

        return proxy;
    }
}

public static void main(String[] args) {
    ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
                  arithmeticCalculator = ArithmeticCalculatorLoggingProxy(arithmeticCalculator).getLoggingProxy();
    int result = arithmeticCalculator.add(11, 12);
    System.out.println("result:" + result);
    result = arithmeticCalculator.div(21, 3);
    System.out.println("result:" + result);

在这里插入图片描述

3.Cglib代理(基于继承的方式实现)

静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
ps:静态代理 代理类 与目标对象实现同样接口

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.

2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
5.如果方法为static,private则无法进行代理。
CGlib采用非常底层的字节码技术ASM,可以为一个类创建子类,并在子类中采用方法拦截技术拦截父类方法的调用,并顺势进行增强,即是织入横切逻辑

3.1 cglib代理实现(代码)

/**
 * 目标对象,没有实现任何接口
 */
public class UserDao {

    public void save() {
        System.out.println("----已经保存数据!----");
    }
}

Cglib代理工厂:ProxyFactory.java

/**
 * 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;
    }
}

或者::

/**
 * Cglib子类代理工厂
 * 对UserDao在内存中动态构建一个子类对象
 */
public class ProxyFactory {
    //维护目标对象
    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(new MethodInterceptor(){
        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;
        }
        });
        //4.创建子类(代理对象)
        return en.create();

    }
}
/**
 * 测试类
 */
public class App {

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

        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法
        proxy.save();
    }
}

4.总结

如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理   
如果目标对象实现了接口,且强制使用cglib代理,则会使用cglib代理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值