静态代理设计与动态代理设计

静态代理设计模式

代理设计模式最本质的特质:一个真实业务主题只完成核心操作,而所有与之辅助的功能都由代理类来完成。

 

例如,在进行数据库更新的过程之中,事务处理必须起作用,所以此时就可以编写代理设计模式来完成。

 

范例:结合传统的代理设计模式以及以购物车CartDao为例来编写代理设计模式

package so.strong.mall.proxy;
import java.util.List;
public interface CartDao {boolean insert(Cart cart) throws Exception;List<Cart> findAll() throws Exception;
}

以上CartDao接口定义的方法,更行插入一定需要事务控制,对于查询操作,不需要事务控制。

 

定义CartDao真实实现

package so.strong.mall.proxy;
import java.util.List;
public class CartDAOImpl implements CartDao{@Overridepublic boolean insert(Cart cart) throws Exception {System.out.println("=====执行数据增加操作=====");return false;}@Overridepublic List<Cart> findAll() throws Exception {System.out.println("=====执行数据列表操作=====");return null;}
}

 

定义代理主题类

package so.strong.mall.proxy;
import java.util.List;
public class CartDAOProxy implements CartDao {private CartDao cartDao;public CartDAOProxy() {}public void setCartDao(CartDao cartDao) {this.cartDao = cartDao;}public void prepare() {System.out.println("=====取消掉jdbc的自动提交");}public void commit() {System.out.println("=====手工提交事务");}public void rollback() {System.out.println("=====出现错误,事务回滚");}@Overridepublic boolean insert(Cart cart) throws Exception {try {this.prepare();boolean flag = this.cartDao.insert(cart);this.commit();return flag;} catch (Exception e) {this.rollback();throw e;}}@Overridepublic List<Cart> findAll() throws Exception {return this.cartDao.findAll();}
}

 

业务层现在并不关心到底是代理类还是真实主题类,它只关心一点,只要取得了CartDao接口对象就可以,那么这一操作可以通过工厂类来隐藏。

package so.strong.mall.proxy;
public class DAOFactory {public static CartDao getCartDaoInstance() {CartDAOProxy proxy = new CartDAOProxy();proxy.setCartDao(new CartDAOImpl());return proxy;}
}

此时业务层暂时不需要继续进行,只需要通过客户端模拟业务层调用即可。

public class TestDemo {
public static void main(String[] args) throws Exception{CartDao dao = DAOFactory.getCartDaoInstance();dao.insert(new Cart());}
}
//=====取消掉jdbc的自动提交
//=====执行数据增加操作=====
//=====手工提交事务

因为事务和处理本身与核心业务有关的功能,但是它不是核心,那么用代理解决是最合适的方式。

 

动态代理设计模式

上面给出的代理设计模式的确可以完成代理要求,但是有一个问题:如果说现在项目里面有200张数据表,那么至少也需要200个左右的DAO接口,如果用上面的代理设计模式,那么意味着除了编写200个的DAO接口实现,还要编写200个代理类,并且有意思的是,这些代理类实现几乎相同。

以上的代理设计模式属于静态代理设计模式,只能够作为代理模式的雏形出现,并不能购作为代码使用的设计模式,为此专门引入了动态代理设计模式的概念。

即:利用一个代理类可以实现所有被代理的操作。

 

如果要想实现动态设计模式,那么必须首先观察一个接口:java.lang.reflect.InvocatonHandler.   它里面有一个方法

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

这个方法就属于代理中调用真实主题类的操作方法,这个方法里面的参数意义如下:

  • Object proxy:表示代理类的对象;
  • Method method:表示现在正在调用的方法;
  • Object[] args:表示方法里面的参数。

但是这个方法没有所对应的真实对象,所以需要在创建这个类对象的时候设置好真实代理对象。

 

如果要想找到代理对象则要使用java.lang.reflect.Proxy类来动态创建,此类主要使用以下方法:

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

此方法参数定义如下:

  • ClassLoader loader :指的是取得对象的加载器;
  • Class<?>[] interfaces: 代理设计模式的核心是围绕接口进行的,所以此处必须取出全部的接口;
  • InvocationHandler h:代理的实现类。

 

范例:使用动态代理实现上面的代理

CartDao不变,修改CartDAOProxy代理类

package so.strong.mall.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CartDAOProxy implements InvocationHandler {private Object obj; //这个是真实对象主题/*** 将要操作的真实主题对象绑定到代理之中,而后返回一个代理类对象* @param obj 真实对象主题* @return 代理类对象*/public Object bind(Object obj) {this.obj = obj;return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(), this);}public void prepare() {System.out.println("=====取消掉jdbc的自动提交");}public void commit() {System.out.println("=====手工提交事务");}public void rollback() {System.out.println("=====出现错误,事务回滚");}//只要执行了操作方法,那么就一定会触发invoke
    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object object = null;//接收返回值if (method.getName().contains("insert")) { //更新插入类操作this.prepare();try {object = method.invoke(this.obj, args); //反射调用方法this.commit();} catch (Exception e) {this.rollback();}} else {object = method.invoke(this.obj, args);//查询操作不需要事务支持
        }return object;}
}
//修改工厂
package so.strong.mall.proxy;
public class DAOFactory {public static Object getCartDaoInstance(Object realObject) {return new CartDAOProxy().bind(realObject);}
}
//修改调用
package so.strong.mall.proxy;
public class TestDemo {public static void main(String[] args) throws Exception{CartDao dao =(CartDao) DAOFactory.getCartDaoInstance(new CartDAOImpl());dao.insert(new Cart());}
}

 

CGLIB实现动态代理设计模式

动态代理模式的确好用,而且也解决了代理类重复的问题,但是不管是传统静态代理或动态代理都有个设计缺陷,以动态代理为例:

return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); //传入真实主题类,返回代理主题类

代理设计模式有一个硬性要求,就是类必须要有接口,所以业界很多人认为应该在没有接口的环境下也能使用代理设计模式。

所以在此时在开源社区里面提供了一个组件包——CGLIB,利用此包可以在没有接口的情况下也能够使用动态代理设计模式,它是模拟的类。

如果要想使用CGLIB,那么必须首先搞清楚对应关系:

  • Proxy:net.sf.cglib.proxy.Enhancer
  • InvocationHandler:net.sf.cglib.proxy.MethodInterceptor
  • 真实主题调用:net.sf.cglib.proxy.MethodProxy

老师课上使用的是引入CGLIB的jar包,我去mvn仓库找了一下,找到了一个cglib,放到pom里面发现也可以。

 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>2.2.2</version>
</dependency>

 

范例:使用CGLIB实现动态代理设计模式

package so.strong.mall.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;class ItemDAOImpl {public void insert(Item item) {System.out.println("=====增加操作=====");}
}class MyProxy implements MethodInterceptor {private Object target; //真实操作主题public MyProxy(Object target) {this.target = target;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {Object object = null;this.prepare();object = method.invoke(this.target, args);this.commit();return object;}public void prepare() {System.out.println("=====取消掉jdbc的自动提交=====");}public void commit() {System.out.println("=====手工提交事务=====");}
}public class TestCGLIB {public static void main(String[] args) {ItemDAOImpl itemDAO = new ItemDAOImpl(); //真实主题对象//代理设计模式之中必须要有公共的集合点,例如:接口,而CGLIB没有接口Enhancer enhancer = new Enhancer(); //创建一个代理工具类enhancer.setSuperclass(ItemDAOImpl.class); //设置一个虚拟的父类enhancer.setCallback(new MyProxy(itemDAO)); //设置代理的回调操作ItemDAOImpl proxyDao = (ItemDAOImpl) enhancer.create();proxyDao.insert(new Item());}
}

可以发现此时没有了对接口的依赖,也可以实现动态代理设计,但是需要模拟代理的父类对象。

转载于:https://www.cnblogs.com/itermis/p/8940582.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/390591.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

6.3 遍历字典

遍历所有的键—值对 遍历字典时&#xff0c;键—值对的返回顺序也与存储顺序不同。 6.3.2 遍历字典中的所有键 在不需要使用字典中的值时&#xff0c;方法keys() 很有用。 6.3.3 按顺序遍历字典中的所有键 要以特定的顺序返回元素&#xff0c;一种办法是在for 循环中对返回的键…

Google Guava新手教程

以下资料整理自网络 一、Google Guava入门介绍 引言 Guavaproject包括了若干被Google的 Java项目广泛依赖 的核心库&#xff0c;比如&#xff1a;集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [comm…

数据科学领域有哪些技术_领域知识在数据科学中到底有多重要?

数据科学领域有哪些技术Jeremie Harris: “In a way, it’s almost like a data scientist or a data analyst has to be like a private investigator more than just a technical person.”杰里米哈里斯(Jeremie Harris) &#xff1a;“ 从某种意义上说&#xff0c;这就像是数…

初创公司怎么做销售数据分析_为什么您的初创企业需要数据科学来解决这一危机...

初创公司怎么做销售数据分析The spread of coronavirus is delivering a massive blow to the global economy. The lockdown and work from home restrictions have forced thousands of startups to halt expansion plans, cancel services, and announce layoffs.冠状病毒的…

leetcode 909. 蛇梯棋

题目 N x N 的棋盘 board 上&#xff0c;按从 1 到 N*N 的数字给方格编号&#xff0c;编号 从左下角开始&#xff0c;每一行交替方向。 例如&#xff0c;一块 6 x 6 大小的棋盘&#xff0c;编号如下&#xff1a; r 行 c 列的棋盘&#xff0c;按前述方法编号&#xff0c;棋盘格…

Python基础之window常见操作

一、window的常见操作&#xff1a; cd c:\ #进入C盘d: #从C盘切换到D盘 cd python #进入目录cd .. #往上走一层目录dir #查看目录文件列表cd ../.. #往上上走一层目录 二、常见的文件后缀名&#xff1a; .txt 记事本文本文件.doc word文件.xls excel文件.ppt PPT文件.exe 可执行…

WPF效果(GIS三维篇)

二维的GIS已经被我玩烂了&#xff0c;紧接着就是三维了&#xff0c;哈哈&#xff01;先来看看最简单的效果&#xff1a; 转载于:https://www.cnblogs.com/OhMonkey/p/8954626.html

r软件时间序列分析论文_高度比较的时间序列分析-一篇论文评论

r软件时间序列分析论文数据科学 &#xff0c; 机器学习 (Data Science, Machine Learning) In machine learning with time series, using features extracted from series is more powerful than simply treating a time series in a tabular form, with each date/timestamp …

leetcode 168. Excel表列名称

题目 给你一个整数 columnNumber &#xff0c;返回它在 Excel 表中相对应的列名称。 例如&#xff1a; A -> 1 B -> 2 C -> 3 … Z -> 26 AA -> 27 AB -> 28 … 示例 1&#xff1a; 输入&#xff1a;columnNumber 1 输出&#xff1a;“A” 示例 2&…

selenium抓取_使用Selenium的网络抓取电子商务网站

selenium抓取In this article we will go through a web scraping process of an E-Commerce website. I have designed this particular post to be beginner friendly. So, if you have no prior knowledge about web scraping or Selenium you can still follow along.在本文…

剑指 Offer 37. 序列化二叉树

题目 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采取相反方式重构得到原数据。 请设计一个算法来实现二叉树的序列化与反序列化…

一个简单的 js 时间对象创建

JS中获取时间很常见&#xff0c;凑凑热闹&#xff0c;也获取一个时间对象试试 首先&#xff0c;先了解js的获取时间函数如下&#xff1a; var myDate new Date(); //创建一个时间对象 myDate.getYear(); // 获取当前年份&#xff08;2位&#x…

裁判打分_内在的裁判偏见

裁判打分News flash: being an umpire is hard. Their job is to judge whether a ball that’s capable of moving upwards of 100 MPH or breaking 25 inches crossed through an imaginary zone before being caught. I don’t think many would argue that they have it ea…

LCP 07. 传递信息

小朋友 A 在和 ta 的小伙伴们玩传信息游戏&#xff0c;游戏规则如下&#xff1a; 有 n 名玩家&#xff0c;所有玩家编号分别为 0 &#xff5e; n-1&#xff0c;其中小朋友 A 的编号为 0 每个玩家都有固定的若干个可传信息的其他玩家&#xff08;也可能没有&#xff09;。传信息…

微信公众号自动回复加超链接最新可用实现方案

你在管理微信号时是否会有自动回复或者在关键字触发自动回复加一个超链接的需求呢&#xff1f;例如下图像王者荣耀这样&#xff1a; 很多有开发经验的朋友都知道微信管理平台会类似富文本编辑器&#xff0c;第一想到的解决方案会是在编辑框中加<a href网址 >显示文字<…

从Jupyter Notebook切换到脚本的5个理由

意见 (Opinion) 动机 (Motivation) Like most people, the first tool I used when started learning data science is Jupyter Notebook. Most of the online data science courses use Jupyter Notebook as a medium to teach. This makes sense because it is easier for be…

win10子系统linux编译ffmpeg

android-ndk-r14b(linux版) ffmpeg-4.0 开启win10子系统&#xff08;控制面板-》程序和功能-》启用或关闭Windows功能 然后在 适用与 Linux 的 Windows 子系统前面打勾&#xff09; 然后点击确定&#xff0c;等待安装&#xff0c;电脑会重启 然后在win10应用商店 搜索ubuntu安装…

leetcode 451. 根据字符出现频率排序

给定一个字符串&#xff0c;请将字符串里的字符按照出现的频率降序排列。 示例 1:输入: "tree"输出: "eert"解释: e出现两次&#xff0c;r和t都只出现一次。 因此e必须出现在r和t之前。此外&#xff0c;"eetr"也是一个有效的答案。 示例 2:输入…

Spring-Security 自定义Filter完成验证码校验

Spring-Security的功能主要是由一堆Filter构成过滤器链来实现&#xff0c;每个Filter都会完成自己的一部分工作。我今天要做的是对UsernamePasswordAuthenticationFilter进行扩展&#xff0c;新增一个Filter&#xff0c;完成对登录页面的校验码的验证。下面先给一张过滤器的说明…

如何使用Ionic和Firebase在短短三天内创建冠状病毒跟踪器应用程序

I am really fond of Hybrid App technologies – they help us achieve so much in a single codebase. Using the Ionic Framework, I developed a cross-platform mobile solution for tracking Coronavirus cases in just 3 days. 我真的很喜欢Hybrid App技术-它们可以帮助…