Java代理模式实例详解【静态代理与 * 】
作者:hu_beliefs 发布时间:2023-04-27 08:18:51
本文实例讲述了Java代理模式。分享给大家供大家参考,具体如下:
即Proxy Pattern,23种java常用设计模式之一。代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。
Java的代理模式是Java中比较常用的设计模式,分为2中代理:静态代理与 * (JDK * 和cglib * )
优点:
职责清晰 真实角色只需关注业务逻辑的实现,非业务逻辑部分,后期通过代理类完成即可。
高扩展性 不管真实角色如何变化,由于接口是固定的,代理类无需做任何改动。
缺点:
很明显的一点就是反射机制,没有高安全性,性能也相对来讲低一些。
代理模式这种设计模式是一种使用代理对象来执行目标对象的方法并在代理对象中增强目标对象方法的一种设计模式。代理对象代为执行目标对象的方法,并在此基础上进行相应的扩展。看起来是有点拗口,首先介绍一个原则:开闭原则(对扩展开放,对修改关闭)。一种好的设计模式甚至是架构,都是在不修改原有形态的基础上扩展出新的功能。
事例场景:
小张是一个普普通通的码农,每天勤勤恳恳地码代码。某天中午小张刚要去吃饭,一个电话打到了他的手机上。“是XX公司的小张吗?我是YY公司的王AA”。“哦,是王总啊,有什么事情吗?”。沟通过后,小张弄明白了,原来客户有个需求,刚好负责这方面开发的是小张,客户就直接找到了他。不过小张却没有答应客户的请求,而是让客户找产品经理小李沟通。
是小张着急去吃面而甩锅吗?并不是,只是为了使故事可以套到代理模式上。我们先看一下代理模式的定义: * 为其他对象提供一种代理,以控制对这个对象的访问。(Provide a surrogate or placeholder for another object to control access to it)
对照定义,码农小张可以映射为其他对象,产品经理小李为小张的代理。我们通过JAVA代码,表述上面事例。
一、静态代理
什么是静态代理:静态代理就是在程序运行前就已经确定代理类与代理对象的代理模式
静态代理模式就是如上图所示,构造三个类实现他们的关系。
首先会思考的一点就是为什么需要实现同一个接口,如果不实现同一个接口,一样可以“代理”功能,所以为什么非要实现同一个接口。我个人认为不实现统一接口的话应该叫做聚合而不是代理;然后,实现统一接口能够使代理类与被代理类之间的联系,提高代码的复用性又能解耦。
package staticProxy;
/**
*接口
*/
public interface DAOInterface {
public void add();
public void delete();
public void update();
public void query();
}
package staticProxy;
/**
*被代理类
*/
public class UserDao implements DAOInterface{
@Override
public void add() {
System.out.println("在目标对象中执行add");
}
@Override
public void delete() {
System.out.println("在目标对象中执行delete");
}
@Override
public void update() {
System.out.println("在目标对象中执行update");
}
@Override
public void query() {
System.out.println("在目标对象中执行query");
}
}
package staticProxy;
/**
* 代理类
*
*/
public class UserDaoProxy implements DAOInterface{
UserDao userDao = null;
public UserDaoProxy(UserDao userDao){
this.userDao = userDao;
}
@Override
public void add() {
userDao.add();
System.out.println("记录日志add");
}
@Override
public void delete() {
userDao.delete();
System.out.println("记录日志delete");
}
@Override
public void update() {
userDao.update();
System.out.println("记录日志update");
}
@Override
public void query() {
userDao.query();
System.out.println("记录日志query");
}
}
静态代理就是写死了在代理对象中执行这个方法前后执行添加功能的形式,每次要在接口中添加一个新方法,则需要在目标对象中实现这个方法,并且在代理对象中实现相应的代理方法;利用Java的反射机制,动态的调用生成代理对象,就能完成 * 的需求。
二、 *
1、JDK *
在代理类管理器的新建代理实例方法中,必须要获得类的加载器、类所实现的接口、还有一个拦截方法的句柄。
在句柄的invoke中如果不调用method.invoke则方法不会执行。在invoke前后添加通知,就是对原有类进行功能扩展了。创建好代理对象之后,proxy可以调用接口中定义的所有方法,因为它们实现了同一个接口,并且接口的方法实现类的加载器已经被反射框架获取到了。
package JDKAgency;
/**
* DAO接口
*/
public interface DAO {
void add();
void update();
void delete();
void query();
}
package JDKAgency;
/**
* DAO的实现类
*/
public class DAOImpl implements DAO {
@Override
public void add() {
System.out.println("添加的方法");
}
@Override
public void update() {
System.out.println("更新的方法");
}
@Override
public void delete() {
System.out.println("删除的方法");
}
@Override
public void query() {
System.out.println("查询的方法");
}
}
package JDKAgency;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类管理器
*/
public class ProxyManager implements InvocationHandler {// * 的核心处理器接口
private Object object;
public ProxyManager(Object object) {
this.object = object;
}
public Object createNewInstance() {
Object o = Proxy.newProxyInstance(object.getClass().getClassLoader(),//真实对象的类加载器
object.getClass().getInterfaces(),//真实对象的所有接口
this);//代理对象
return o;
}
@Override //代理对象 执行业务的方法 参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before......权限检测");//前置通知
Object invoke = method.invoke(object, args);//动态调用执行方法
System.out.println("after......日志监控");//后置通知
return invoke;
}
}
package JDKAgency;
/**
* 测试
*/
public class JTest {
public static void main(String[] args) {
//创建真实对象
DAO dao = new DAOImpl();
DAO o =(DAO) manager.createNewInstance();
//JDK * 是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
o.add();
o.query();
}
}
注意:JDK * 是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
补充:
JavaJDK * 报错java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to *。
javaJDK * 是Java原生代理模式。
注意:JDK * 是代理的接口,因此强制转换应该转换为接口,而不是实现类,若强制转换实现类就会抛出ClassCastException,好比ArrayList与LinkedList实现统一接口List,两者也不能相互转换,但都可以向上转型。
正确的转型方案:
//创建处理器对象
ProxyManager manager = new ProxyManager(dao);
//生成 * 对象
// DAO o = (DAO) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), manager);
2、cglib *
cglib * 是web应用框架常用的一种 * 方式。cglib是动态生成被代理类的子类,注意:是子类。
他需要先引入asm与cglib的jar包。如下图:
接着废话不多说,看代码分析:
package CGlibAgency;
/**
* 被代理类
*/
public class User {
public void saveUser(){
System.out.println("保存对象。");
}
public void updateUser(){
System.out.println("修改对象。");
}
}
package CGlibAgency;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 代理类管理器
*/
public class Interceptor implements MethodInterceptor {//cgLib需要实现MethodInterceptor接口,cgLib是基于类的,动态的生成代理类(被代理类的子类)
private Enhancer enhancer = new Enhancer();//Enhancer是Cglib代理的重要对象
public Object createProxy(Class superClass){
enhancer.setSuperclass(superClass);//获取父类的Class
enhancer.setCallback(this);//设置方法的回调,类似于JDK * 中的Proxy与InvocationHandler实现类的绑定回调
return enhancer.create();//返回代理类的对象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("'权限验证'");
Object aSuper = methodProxy.invokeSuper(o, objects);//通过虚拟的代理对象的代理方法调用父类的方法,参数中一个是父类,一个是所有的子类对象数组(??)
System.out.println("'日志收集'");
return aSuper;
}
}
package CGlibAgency;
/**
* 测试类
*/
public class JTest {
public static void main(String[] args) {
Interceptor interceptor = new Interceptor();//创建代理管理对象
Object proxy = interceptor.createProxy(User.class);//创建一个代理类
System.out.println(User.class.getTypeName());//查看代理类的类型
User user = (User) proxy;//转型,子类自动向上转型
user.saveUser();
user.updateUser();
}
}
总结:
代理模式不仅可以降低模块儿之间的耦合,还能做到高复用,简化代码等。spring的AOP模块就是最直观的代理模式,使用了 * 来实现,在spring中的许多模块中都具有 * 的影子。
更多java相关内容感兴趣的读者可查看本站专题:《Java面向对象程序设计入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。
来源:https://blog.csdn.net/hu_belif/article/details/83996913


猜你喜欢
- 银行卡大家都使用,在密码输错超过限制次数之后,就容易被锁死,智能到银行柜台才能解锁,那么这一功能如果实现的呢,今天小编通过实例代码给大家详细
- 使用方法 首先在Github或者Gitee上面新建一个仓库复制仓库的链接用idea在本地新建一个demo项目点击菜单栏的VCS,按
- Java实现驼峰、下划线互转1.使用 Guava 实现先引入相关依赖<dependency> <
- trim中prefix与suffix等标签用法1.prefix 前缀增加的内容2.suffix 后缀增加的内容3.prefixOverrid
- 1 异常异常的体系• ThrowableError通常出现重大问题如:运行的类不存在或者内存溢出等。不编写针对代码对其处理Exception
- 前言继承是面向对象语法的三大特征之一。继承可以降低代码编写的冗余度,提高编程的效率。通过继承,子类获得了父类的成员变量和方法。一个子类如何继
- Ribbon 介绍Ribbon 是 Netflix 提供的一个基于 Http 和 TCP 的客户端负载均衡工具,且已集成在 Eureka 依
- mybatis的映射文件写法多种多样,不同的写法和用法,在实际开发过程中所消耗的开发时间、维护时间有很大差别,今天我就把我认为比较简单的一种
- springboot jackson配置项目中使用的json是jackson。这个呢是spring boot自带的,一开始是用阿里的fast
- 介绍TextView 是 Android 开发中最常用的小部件之一。它用于在屏幕上显示文本。但是,TextView 有几个较少为人知的功能,
- 项目中看到了有埋点监控、报表、日志分析,有点兴趣想慢慢捣鼓一下1. 数据埋点监控机器环境的性能和业务流程或逻辑等各项数据,并根据这些数据生成
- 目录Maven依赖配置示例Maven依赖要开始使用咖啡因Caffeine和Spring Boot,我们首先添加spring-boot-sta
- jstat命令简介jstat(Java Virtual Machine Statistics Monitoring Tool)是JDK提供的
- DataTableDataTable 是 C# 中常用的一种数据表格类型,它类似于数据库中的表格,可以用来存储和处理数据。DataTable
- 假设你已经装了texlive打开cmd输入latex --version应该能输出打开vscode,安装这几个插件设置->Settin
- 前言在实际项目开发中,会碰到这样的问题,数据库表结构设计好了,可实体类还没相应地弄出来。实体类的属性命名方法一般是驼峰法,而数据库中的表字段
- 本文会介绍从一个最基本的java工程,到Web工程,到集成Spring、SpringMVC、Spring
- 使用ehcache-spring-annotations使得在工程中简单配置即可使用缓存下载地址:http://code.google.co
- 本文实例总结了Java编程实现生成给定范围内不重复随机数的方法。分享给大家供大家参考,具体如下:在Java中的Math类中存在一个rando
- 对于生成的sql语句 自动加上单引号的情况mybatis是这样的,如果表的字段跟系统字段冲突,写sql语句的时候必须得加上单引号,这样才会区