(十二)mybatis之动态代理

mybatis之动态代理的应用

在前文(https://www.cnblogs.com/NYfor2018/p/9093472.html)我们知道了,Mybatis的使用需要用到Mapper映射文件,一个是映射接口,另一个是映射XML文件(此处不详谈映射文件XML),在应用中我们可以感觉到,映射接口似乎对接着XML文件中的实现命令,可是我们在运行程序是时候调用的往往是Mapper接口,而不是一个包含逻辑的实现类。很显然Mapper产生了代理类。

首先,什么是代理模式?代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(取自百度百科:https://baike.baidu.com/item/%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F/8374046?fr=aladdin

举个栗子,如图:

我们租房的时候一般是去找中介,而不是直接去找包租婆。放在代理模式这里来说,就是,我们在访问真实的对象的时候,往往不是直接去访问真实对象,而是通过代理对象,来对真实对象进行访问。

为什么要使用代理模式?通过代理,一方面可以控制如何访问真正的服务对象,提供额外的服务。另外一方面有机会通过重写一些类来满足特定的需要。就像是,有时候租客反映的一些问题,中介可以直接解决而不需要麻烦到包租婆。

一般来说,动态代理分为两种:一种是JDK反射机制提供的代理;另一种是CGLB代理。在JDK提供的代理,我们必须要提供接口;而在CGLIB中则不需要提供接口。

在学习动态代理之前,先了解一下反射的基础。

反射简单应用

import java.lang.reflect.Method;
public class ReflectService {

         public void sayHello(String name) {

                   System.out.println("hello"+name);

         }

         public static void main(String[] args) throws Exception, IllegalAccessException, ClassNotFoundException {

                   //通过反射创建ReflectService对象

                   Object service = Class.forName(ReflectService.class.getName()).newInstance();

                   //获取服务方法

                   Method method = service.getClass().getMethod("sayHello", String.class);

                   //反射调用方法

                   method.invoke(service, "zhangsan");
         }
}

 

①   先根据类名,来创建实例对象,所以就找了ReflectService类的类名。

②   然后再根据实例对象来找回它的方法,参数是方法名及其参数。

③   利用反射机制来调用ReflectService类的sayHello方法。

JDK动态代理

 

JDK的动态代理,是由JDK的java.lang.reflect.*包提供支持的,我们需要完成以下步骤:

① 编写服务类和接口,服务类是真正的服务提供者,而JDK代理中接口是必要的。

② 编写代理类,提供绑定和代理方法。

首先,先编写动态代理的接口

public interface HelloService {

         public void sayHello(String name);

}

接着,写一个这个接口的实现类

public class HelloServiceImpl implements HelloService{

         @Override
         public void sayHello(String name) {
                   System.out.println("hello "+name);
         }
}

然后,写一个代理类,提供真实对象的绑定和代理方法。代理类的要求是实现InvocationHandler接口的代理方法,当一个对象被绑定后,执行其方法的时候就会进入到代理方法里。

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

public class HelloServiceProxy implements InvocationHandler{

         /**

          * 真实的服务对象

          */

         private Object target;

         /**

          *

          * @param target

          * @return 绑定委托对象并返回一个代理类

          */

         public Object bind(Object target) {

                   this.target = target;

                   //取得代理对象

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

                   //loader服务对象的类加载器,interfaces是加载服务对象的接口,h是执行这个代理类的方法的执行者

                   return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

         }

         @Override

         /**

          * 通过代理对象调用方法首先进入这个方法

          * invoke方法的参数分别是:proxy是代理对象,method是被调用的方法,args是方法的参数

          */

         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                   System.err.println("#########我是JDK动态代理##########");

                   Object result = null;

                   //反射方法前调用

                   System.err.println("我准备说hello");

                   result = method.invoke(target, args);

                   //反射方法后调用

                   System.err.println("我说过hello了");

                   return result;

         }

}

最后,编写一个测试类:

public class HelloServiceMain {

         public static void main(String[] args) {

                   HelloServiceProxy HelloHandler = new HelloServiceProxy();

                   HelloService proxy = (HelloService)HelloHandler.bind(new HelloServiceImpl());

                   proxy.sayHello("张三");

         }

}

运行出来的结果是:

整个过程可以这样理解:

先创建一个代理类对象a,然后用代理类对象绑定真正提供服务对象b,返回一个接口b+,再用这个接口b+来进行服务。

如果我们把proxy.sayHello(“张三”);注释掉:

我们会发现控制台什么东西都没有输出。

所以我们可以知道,代理类的invoke方法,只有在代理的真正提供服务的对象被调用的时候,才会起作用。

所以这时候我们可以这样理解:

挑选的人相当于访问者,相亲交流平台相当于代理,被挑选的人相当于真正提供服务的对象。当挑选的人在向相亲交流平台要求得到被挑选的人的信息,实际上,提供信息的人不是平台,是被挑选的人。挑选的人在对被挑选的人打招呼,而不是对相亲交流平台打招呼。而且,相亲交流平台不只是为一个独特的挑选的人提供被挑选的人的信息,只要有忍看上被挑选的人,相亲交流平台就可以提供ta的信息。

 

CGLIB动态代理

此处仅突出CGLIB动态代理跟JDK动态代理在代理类上的区别:

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

public class HelloServiceCgLib implements MethodInterceptor{

         private Object target;

         public Object getInstance(Object target) {

                   this.target = target;

                   Enhancer enhancer = new Enhancer();

                   enhancer.setSuperclass(this.target.getClass());

                   //回调方法

                   enhancer.setCallback(this);

                   return enhancer.create();

         }

         @Override

         public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

                   System.err.println("##########我是CGLIB的动态代理##########");

                   //反射方法前调用

                   System.err.println("我准备说hello");

                   Object returnObj = proxy.invokeSuper(obj, args);

                   //反射方法后调用

                   System.err.println("我说过hello了");

                   return returnObj;

         }

}

可以看到CGLIB的代理类是实现MethodInterceptor的代理方法。在mybatis中通常在延迟加载的时候才会用到CGLIB的动态代理。

原文地址:https://www.cnblogs.com/NYfor2018/p/9114113.html

时间: 2024-10-09 10:15:37

(十二)mybatis之动态代理的相关文章

设计模式(二)学习----动态代理

动态代理:动态代理是指在实现阶段不需要关心代理谁,而在运行阶段才指定代理哪一个对象.Spring AOP采用的核心思想就是动态代理设计模式.  下面看动态代理的UML类图: 下面思考问题:invocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的? 动态代理类: package com.lp.ecjtu.DynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect

Mybatis的动态代理模式

mybatis的动态代理需要遵循4个规则: 1.xml文件中的namespace的值为接口类的全限命名 2.statement的id要跟接口的方法名相同. 3.statement的parameterType要跟接口的方法的参数的类型相同. 4.statement的resultType要跟接口方法的返回值类型相同. 接口: public Student selectStuById(int id); xml文件: <select id="selectStuById" resultTy

c++第十二章 -(动态管理内存、动态数组和函数返回动态内存)

1.静态内存,编译时申请,存储在栈,如基本数据类型. 2.动态内存由一些没有名字,只有地址的内存块构成.那些内存块是在程序运行期间动态分配的.它们来自一个标准c++库替你管理的“大池子”(内存池),从内存池申请一些内存需要用new语句,他将根据你提供的数据类型分配一块大小适当的内存.你不必担心内存块的尺寸问题. 3.注意在用完内存块之后,应该用delete语句把它返还内存池.另外作为一种附加的保险措施,在释放内存之后还要把关联的指针置NULL,对空指针进行“解引用”会报错. class Comp

由浅入深分析mybatis通过动态代理实现拦截器(插件)的原理

最近在用mybatis做项目,需要用到mybatis的拦截器功能,就顺便把mybatis的拦截器源码大致的看了一遍,为了温故而知新,在此就按照自己的理解由浅入深的理解一下它的设计. 和大家分享一下,不足和谬误之处欢迎交流.直接入正题. 首先,先不管mybatis的源码是怎么设计的,先假设一下自己要做一个拦截器应该怎么做.拦截器的实现都是基于代理的设计模式设计的,简单的说就是要创造一个目标类的代理类,在代理类中执行目标类的方法并拦截执行拦截器代码. 那么我们就用JDK的动态代理设计一个简单的拦截器

系统架构设计——设计模式之代理模式(二)CGLIB动态代理实现

像上一篇所说的代理模式其实是静态代理,在实际开发中其实应用不大,因为他需要事先知道被代理对象是谁,而且被代理对象和代理对象实现了公共的接口.实际情况往往并不能满足这些条件,我们往往在写代理模式的时候并不知道到时候被代理的对象是谁.解决办法就是--动态代理.以下我们将使用CGLIB实现动态代理. 一.动态代理概述 程序在运行期而不是编译器,生成被代理对象的代理对象,并且被代理对象并不需要和代理对象实现共同的接口.基于此,我们可以利用代理对象,提供一种以控制对被代理对象的访问. 1.1 动态代理的原

spring如何管理mybatis(一) ----- 动态代理接口

问题来源 最近在集成spring和mybatis时遇到了很多问题,从网上查了也解决了,但是就是心里有点别扭,想看看到底怎么回事,所以跟了下源码,终于发现了其中的奥妙. 问题分析 首先我们来看看基本的配置. spring的配置: <!-- 数据库配置 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="

Mybatis Mapper动态代理方式

目录结构及配置文件与原始dao方法相比更简便 只需一个UserMapper的接口,放在一起的配置文件,配置文件中namespace的地址确定jdk动态代理的对象 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybati

Mybatis 之动态代理

使用Mybatis 开发Web 工程时,通过Mapper 动态代理机制,可以只编写接口以及方法的定义. 如下: 定义db.properties driver=oracle.jdbc.OracleDriver url=jdbc:oracle:thin:@localhost:1521:orcl username=scott password=tiger 定义SqlMapConfig.xml <?xml version="1.0" encoding="UTF-8"?

MyBatis - Mapper动态代理开发

采用Mapper动态代理方法只需要编写相应的Mapper接口(相当于Dao接口),那么Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同Dao接口实现类方法. - Mapper接口开发需要遵循以下规范: ① Mapper.xml文件中的namespace与mapper接口的全类名相同. ② Mapper接口方法名和Mapper.xml中定义的statement的id相同. ③ Mapper接口方法的输入参数类型和mapper.xml中定义的statement的paramet