美文网首页嘟嘟程序猿
201Spring--基础--动态代理(七)

201Spring--基础--动态代理(七)

作者: 无剑_君 | 来源:发表于2019-07-25 10:10 被阅读550次

    一、动态代理

      Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,代理对象存在的价值: 主要用于拦截对真实业务对象的访问。代理对象有什么方法: 一般来说,真实业务对象具有什么方法,那么代理对象就会具备相应的方法。

    二、JDK 动态代理

    1. Java 虚拟机运行图


      Java 虚拟机运行图
      Java 动态代理运行机制

      JDK:
      动态代理接口:InvocationHandler
      动态代理类:Proxy
      JDK动态代理4个条件:
      目标接口、目标类、代理类、代理类里的拦截器

    2. 业务接口与业务类
    //  接口
    public interface UserService {
        Boolean add(User user);
    }
    // 实现类
    @Service
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserMapper userMapper;
        @Override
        public Boolean add(User user) {
            if(userMapper.insertUser(user)>1){
                return true;
            }
            return  false;
        }
    }
    
    
    1. 代理类代码
    public class LogHandler implements InvocationHandler {
        // 我们要代理的目标对象
        private Object targetObject;
    
        public Object newProxyInstance(Object targetObject) {
            this.targetObject = targetObject;
            /*
            * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看
            其三个参数 第一个参数
            * handler.getClass().getClassLoader()
            * ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
            * 第二个参数realSubject.getClass().getInterfaces(),我们这里为
            代理对象提供的接口是真实对象所实行的接口
            * ,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 第三
            个参数handler, 我们这里将这个代理对象关联到了上方的
            * InvocationHandler 这个对象上
            */
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                    targetObject.getClass().getInterfaces(),
                    this);
        }
    
        /**
         * 实现接口的方法
         */
        public Object invoke(Object proxy, Method method, Object[]
                args) throws Throwable {
            Object ret = null;
            try {
                System.out.println("正在进行操作前的准备工作……");
                // 调用目标方法,代理对象与参数
                ret = method.invoke(targetObject, args);
                System.out.println("操作成功,正在进行确认处理……");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("error-->>" + method.getName());
                throw e;
            }
            return ret;
        }
    }
    
    1. 代理测试
      @Test
        public void testAdd() {
            User user = new User();
            user.setParentId(2L);
            LogHandler logHandler = new LogHandler();
            UserService userServiceProxy = (UserService) logHandler.newProxyInstance(userService);
            userServiceProxy.add(user);
        }
    

    三、CGLIB 动态代理

    CGlib 是一个强大的,高性能,高质量的 Code 生成类库。它可以在运行期扩展 Java 类与实现 Java 接口。
    用 CGlib 生成代理类是目标类的子类。
    用 CGlib 生成 代理类不需要接口。
    用 CGLib 生成的代理类重写了父类的各个方法。
    拦截器中的 intercept 方法内容正好就是代理类中的方法体。

    1. Maven依赖
    <!--添加cglib包动态代理 -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
    
    1. 业务类
      不需实现接口。
    public class UserServiceImpl{
    public void saveUser() {
    System.out.println("保存用户信息");
    }
    public void updateUser(){
    System.out.println("更新用户信息");
    }
    }
    
    1. 动态代理类
    public class UserDaoInterceptor implements MethodInterceptor{
    //目标类
    private Object target;
    /**
    * 产生代理类
    * @return
    */
    public Object createProxy(){
    //字节码增强技术
    Enhancer enhancer = new Enhancer();
    //设置代理类的父类是目标类
    enhancer.setSuperclass(target.getClass());
    //设置回调
    enhancer.setCallback(this);
    return enhancer.create();
    }
    public UserDaoInterceptor(Object target) {
    this.target = target;
    }
    /**
    * 等同于invoke方法
    */
    public Object intercept(Object arg0, Method method, Object[]
    arg2,
    MethodProxy arg3) throws Throwable {
    //前置通知
    if(method.getName().equals("saveUser")
    ||method.getName().equals("updateUser")){
    System.out.println("开启事务");
    method.invoke(target, arg2);
    System.out.println("提交事务");
    }else{
    method.invoke(target, arg2);
    }
    return null;
    }
    }
    
    
    1. 代理测试
    public class UserDaoTest {
    @Test
    public void testCGLibProxy(){
    Object target = new UserDaoImpl();
    /**
    * 创建一个拦截器
    */
    UserDaoInterceptor interceptor = new
    UserDaoInterceptor(target);
    UserDaoImpl proxy = (UserDaoImpl)interceptor.createProxy();
    proxy.saveUser();
    }
    }
    

    spring 有两种代理方式:
    1)若目标对象实现了若干接口,spring 使用 JDK 的 java.lang.reflect.Proxy 类代理。
    优点:因为有接口,所以使系统更加松耦合
    缺点:为每一个目标类创建接口
    2)若目标对象没有实现任何接口,spring 使用 CGLIB 库生成目标对象的子类。
    优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
    缺点:因为没有使用接口,所以系统的耦合性没有使用 JDK 的动态代理好。

    相关文章

      网友评论

        本文标题:201Spring--基础--动态代理(七)

        本文链接:https://www.haomeiwen.com/subject/bmrilctx.html