搜YSL也是看见微商在朋友圈发的广告,不知道大家有没有发现,微商简直就是代理模式的完美例子,画个问号?,向下看👇 是什么 看不太懂?,没有关系,下面讲例子 举上面的例子——微商 如: 测试类ProxyTest 看下面的结果是不是很暖心 用类图做个总结: 静态代理缺点: Proxy类的newProxyInstance方法 InvocationHandler接口的invoke方法 微商代理类已经不需要了,可以动态生成 完整代码如下 看下测试类 看测试结果 至此动态代理就实现了 带着这两个疑问,咱们反编译下生成动态代理类 反编译生成动态代理类 生成的文件如下 如此就可以解释上面的两个问题了 接下来看jdk动态代理在Mybatis中的应用,终于到了 在短短的测试类中就使用了三个设计模式,确实对初学者不太友好,所以一点一点拆开来看未免不是一个好的学习习惯,所以今天主要看两行代码 看完上面的动态代理,再看这两行代码就能解开初学Mybatis时候的疑惑, 下面从源码分析一下,Mybatis底层是怎么创建Dao层接口的代理对象的 也就是研究下面的代码 当调用几个类的getMapper方法后,会调用下面类第1个newInstance方法 上面的代码,写注释的地方是重点 看到代码不得不提出两个问题 invoke方法作用:生成findAll方法对应的MapperMethod类实例,MapperMethod类是最重要的,在下面 该类的两个作用 先看是如何解析的 那么问题来了,该类是如何找到findAll方法对应的sql语句呢? 再看execute方法为findAll方法找到的sql语句类型匹配方法 根据sql语句类型匹配对应的方法后,其实是调用SqlSession接口的实现类执行sql语句 SqlSession接口 最后交给SqlSession实现类DefaultSqlSession去执行findAll方法对应sql语句,并返回结果 当代理对象userDao调用findAll()执行的代码流程文章目录
一、引入今天的主题
今天准备写代理模式的时候,苦思要找什么例子,就搜了下世界名牌口红的企业——YSL(圣罗兰),就问下了女朋友,知道这个嘛。上图的回答,简直让我怀疑找了个假女朋友(😂)。二、正文开始——代理模式
三、代理模式分类
动态代理分为面向接口的jdk动态代理和Cglib动态代理(暂不做讨论,Mybatis中使用的是jdk动态代理)。3.1静态代理
3.1.1实现静态代理两个要求
3.1.2代码实现
/** * @Author Think-Coder * @Data 2020/5/14 10:55 * @Version 1.0 */ //定义一个卖化妆品的接口 public interface MakeUpSeller { //销售的方法 //name为化妆品名字,price是价格 void sell(String name,double price); } //原对象—————YSL官方商店 public class YSLSeller implements MakeUpSeller { @Override public void sell(String name, double price) { System.out.println("感谢购买"+name+",一共是"+price+"元"); } } //代理对象————微商代理YSL官方商店 public class WeiShangProxy implements MakeUpSeller { //持有YSL官方商店的引用 private YSLSeller yslSeller; public WeiShangProxy(YSLSeller yslSeller) { this.yslSeller = yslSeller; } //实现接口的sell方法,并增强原对象YSL官方商店的方法 //增强原对象的方法:两个输出方法 @Override public void sell(String name, double price) { System.out.println("我要发朋友圈,介绍商品优势"); //YSL官方商店对象调用卖产品的接口 yslSeller.sell(name,price); System.out.println("并送您一瓶卸妆水,欢迎下次再来"); } }
public class ProxyTest { public static void main(String[] args) { //将new的YSLSeller官方商店原对象传入微商代理对象 //微商代理对象实现了客户对YSL官方商店的访问控制 WeiShangProxy weiShangProxy = new WeiShangProxy(new YSLSeller()); //微商代理对象调用卖产品方法 weiShangProxy.sell("YSL口红",1000); } }
我要发朋友圈,介绍商品优势 感谢购买YSL口红,一共是1000.0元 并送您一瓶卸妆水,欢迎下次再来 Process finished with exit code 0
在测试类中最重要的就是将new YSLSeller()对象放入WeiShangProxy构造函数中
也就是说客户直接访问了微商代理类,从而微商代理控制了客户对YSL官方商店的访问
静态代理是面向实现编程(YSLSeller实现了MakeUpSeller接口)而不是面向接口编程,就把程序写死了,不利于程序的扩展,即如果原对象增加或删除方法,代理对象也会跟着改变,极大提高代码维护成本
于是就有了JDK动态代理3.2jdk动态代理
3.2.1定义
3.2.2jdk动态代理的两个核心方法
生成代理对象 /** * 参数1:ClassLoader loader,原对象的类加载器 * 参数2:Class<?>[] interfaces,原对象继承(实现)的类和接口Class类数组 * 参数3:InvocationHandler h,用户自定义增强原对象的方法接口 **/ public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) //上面省略 /* * Look up or generate the designated proxy class. * 查找或生成指定的代理类 */ Class<?> cl = getProxyClass0(loader, intfs); //下面省略 }
用户自定义的规则接口需要实现此接口,invoke方法用于增加原代理对象方法public interface InvocationHandler { /** * 参数1:Object proxy,代理对象 * 参数2:Method method,原对象方法对应的反射类,method.invoke反射调用原对象方法 * 参数3:Object[] args,传入方法参数 **/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
3.2.3拿上面的例子举例
MakeUpSeller接口及YSLSeller官方商店类不发生变化
加入MakeUpSellerHandler类实现InvocationHandler接口,用于增强原对象方法 /** * @Author Think-Coder * @Data 2020/5/14 10:55 * @Version 1.0 */ //定义一个卖化妆品的接口 public interface MakeUpSeller { //销售的方法 //name为化妆品名字,price是价格 void sell(String name,double price); } //原对象—————YSL官方商店 public class YSLSeller implements MakeUpSeller { @Override public void sell(String name, double price) { System.out.println("感谢购买"+name+",一共是"+price+"元"); } } //实现InvocationHandler接口 public class MakeUpSellerHandler implements InvocationHandler { //持有原对象的父类的引用,父类引用指向子类对象,多态的体现 private MakeUpSeller makeUpSeller; public MakeUpSellerHandler(MakeUpSeller makeUpSeller) { this.makeUpSeller = makeUpSeller; } @Override //增强原对象的方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我要发朋友圈,介绍商品优势"); //反射调用原对象的方法 method.invoke(makeUpSeller,args); System.out.println("并送您一瓶卸妆水,欢迎下次再来"); return null; } }
public class ProxyTest { public static void main(String[] args) { /** * 参数1:MakeUpSeller.class.getClassLoader(),MakeUpSeller的类加载器 * 参数2:new Class[]{MakeUpSeller.class},MakeUpSeller继承(实现)的类和接口Class数组 * 参数3:new MakeUpSellerHandler(new YSLSeller()),用户自定义增强原对象的方法接口 **/ MakeUpSeller yslProxy = (MakeUpSeller) Proxy.newProxyInstance(MakeUpSeller.class.getClassLoader(), new Class[]{MakeUpSeller.class}, new MakeUpSellerHandler(new YSLSeller())); yslProxy.sell("YSL口红",1000); } }
我要发朋友圈,介绍商品优势 感谢购买YSL口红,一共是1000.0元 并送您一瓶卸妆水,欢迎下次再来 Process finished with exit code 0
不过,还有两个疑问没有解决
编译是.java文件编译为.class文件,反编译为.class文件变为.java文件的过程
改下测试类代码 public static void main(String[] args) throws IOException { MakeUpSeller yslProxy = (MakeUpSeller) Proxy.newProxyInstance(MakeUpSeller.class.getClassLoader(),new Class[]{MakeUpSeller.class}, new MakeUpSellerHandler(new YSLSeller())); yslProxy.sell("YSL口红",1000); createProxyClass(); } public static void createProxyClass() throws IOException { byte[] bytes = ProxyGenerator.generateProxyClass("MakeUpSeller$proxy", new Class[]{MakeUpSeller.class}); Files.write(new File("D:\ITProject\javaproj\selfproj\ProxyTest\out\production\ProxyTest\MakeUpSeller$proxy.class").toPath(),bytes); }
代码如下,做了部分省略, //继承Proxy代理类,实现了MakeUpSeller接口 //这个就可以回答第一个问题,可以转成MakeUpSeller类型 public final class MakeUpSeller$proxy extends Proxy implements MakeUpSeller { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public MakeUpSeller$proxy(InvocationHandler var1) throws { super(var1); } //实现MakeUpSeller接口sell类 public final void sell(String var1, double var2) throws { try { //这行代码很重要,回答了第二个问题 //该类继承proxy类,h便为InvocationHandler接口,因此可以调用invoke方法 //而MakeUpSellerHandler实现了InvocationHandler接口,因此直接调用了 //MakeUpSellerHandler类中invoke方法 super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var5) { throw var5; } catch (Throwable var6) { throw new UndeclaredThrowableException(var6); } } }
最后也用类图总结一下
main方法用代理对象调用sell方法时,其实是动态生成的MakeUpSeller$proxy类实例调用的sell方法
根据上面反编译类中sell方法中,调用的是MakeUpSellerHandler接口中invoke方法,invoke方法中包装了原对象YSLSeller的sell方法,最后实现了动态代理。四、动态代理在MyBatis中的应用
4.1手写的MyBtatis框架的测试类
public static void main(String[] args) throws IOException { //1.读取配置文件,连接数据库 InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.创建SqlSessionFactory工厂 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(in); //3.使用工厂生产SqlSession对象,用于操作数据库 SqlSession session = factory.openSession(); //4.使用SqlSession创建Dao接口的代理对象,因为IUserDao接口没有实现类 IUserDao userDao = session.getMapper(IUserDao.class); //5.使用代理对象执行方法 List<User> users = userDao.findAll(); for (User user:users){ System.out.println(user); } //6.释放资源 session.close(); in.close(); }
//4.使用SqlSession创建Dao接口的代理对象,因为IUserDao接口没有实现类 IUserDao userDao = session.getMapper(IUserDao.class); //5.使用代理对象执行方法 List<User> users = userDao.findAll();
为什么只有Dao层接口,没有Dao层的接口实现类就可以操作数据库?
就是用到了jdk的动态代理生成了Dao层接口的代理对象userDao4.2MapperProxyFactory类创建Dao层接口代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap(); //通过构造函数传入IUerDao接口Class对象 //学过反射的童鞋应该知道,拿到Class对象,相当于拿到IUserDao类 public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return this.mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return this.methodCache; } //先调用此方法 public T newInstance(SqlSession sqlSession) { MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); //调用下面newInstance方法 return this.newInstance(mapperProxy); } protected T newInstance(MapperProxy<T> mapperProxy) { /** * 有没有很熟悉! * mapperInterface就是Dao层接口 IUserDao * 参数1:this.mapperInterface.getClassLoader(),IUserDao的类加载器 * 参数2:new Class[]{this.mapperInterface},IUserDao继承(实现)的类和接口Class数组 * 参数3:mapperProxy,上边的newInstace方法返回的,实现了InvocationHandler接口,用于方法增强 **/ return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy); } }
MapperProxyFactory类就是创建代理对象的工厂类,自定义Dao层接口传入构造函数,通过newInstance方法返回自定义Dao层接口的代理对象4.3使用代理对象执行findAll方法
List<User> users = userDao.findAll();
4.3.1首先看MapperProxy类
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final Method privateLookupInMethod; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //上面省略 //下面两行代码很重要 //method为Dao层自定义接口方法 //调用下面的cachedMapperMethod找到与要执行的Dao层接口方法对应的MapperMethod MapperMethod mapperMethod = this.cachedMapperMethod(method); //调用execute方法来执行findAll方法 //先把sqlSession传入到MapperMethod内部 //在MapperMethod内部将要执行的方法名和参数再传入sqlSession对应方法中去执行 return mapperMethod.execute(this.sqlSession, args); } //根据的传入IUserDao接口自定义方法findAll,生成对应的MapperMethod类实例 private MapperMethod cachedMapperMethod(Method method) { return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> { return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()); }); } }
4.3.2再看MapperMethod类
public class MapperMethod { //SqlCommand内部类解析自定义接口方法的方法名称和SQL语句类型, private final MapperMethod.SqlCommand command; //MethodSignature内部类解析接口方法的签名,即接口方法和参数名称和参数值映射关系,如String a="0" private final MapperMethod.MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new MapperMethod.SqlCommand(config, mapperInterface, method); this.method = new MapperMethod.MethodSignature(config, mapperInterface, method); } }
答案就是Configuration对象,通过MapperMethod构造函数传进来的
如图所示Configuration中的mapperedStatements字段中的MapperedStatement对象是一个Map类型
key为findAll方法,value中包含sql语句,可以通过方法名findAll找到对应的sql语句(这个就是上面第二个问题的答案)
execute方法源码 public Object execute(SqlSession sqlSession, Object[] args) { Object result; Object param; //根据SqlCommand解析出来的sql语句类型,为增删改查类型匹配方法 switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) { this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) { result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) { result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) { result = this.executeForCursor(sqlSession, args); } else { param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }
如根据查找到executeForMany方法 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { Object param = this.method.convertArgsToSqlCommandParam(args); List result; if (this.method.hasRowBounds()) { RowBounds rowBounds = this.method.extractRowBounds(args); //最后执行sqlSession接口中的selectList方法 result = sqlSession.selectList(this.command.getName(), param, rowBounds); } else { result = sqlSession.selectList(this.command.getName(), param); } if (!this.method.getReturnType().isAssignableFrom(result.getClass())) { return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result); } else { return result; } }
public interface SqlSession extends Closeable { /** * var1:Dao层自定义接口的方法名称,即findAll() * var2:方法的参数 * var3:用于分页查询 **/ <T> T selectOne(String var1); <T> T selectOne(String var1, Object var2); <E> List<E> selectList(String var1, Object var2, RowBounds var3); .... }
这个和我们直接用SqlSession对象调用DefaultSqlSession的实现类的方法是一样的,转了一圈回来,就完成了动态代理五、图总结
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算