java面试常见模式问题---代理模式
作者:兴趣使然の草帽路飞 发布时间:2022-02-14 13:07:31
本篇总结的是 代理设计模式,后续会经常更新~
代理模式最直观的解释就是,通过代理,将被代理对象 “增强”!(即,扩展被代理对象的功能)
代理模式分为静态代理,和 * : * 的代理类是动态生成的 , 静态代理的代理类是我们提前写好的逻辑。
Java 中实现 * 的方式有 2 种:
JDK *
CGLIB *
1、静态代理
静态代理角色分析:
抽象角色 :一般使用接口或者抽象类来实现。
真实角色 :被代理的角色。
代理角色: 代理真实角色 , 代理真实角色后 ,一般会做一些附属的操作。
调用方:使用代理角色来进行一些操作。
我们以租客租客租房子为例,涉及到的对象有:租客、中介、房东。(房东即为被代理对象,中介即为代理对象)
租客通过中介之手租住房东的房子,代理对象中介需要寻找租客租房,并从中获取中介费用。
代码实现:
Rent.java
即抽象角色
// 抽象角色:租房
public interface Rent {
public void rent();
}
Host.java
即真实角色
// 真实角色: 房东,房东要出租房子
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
Proxy.java
即代理角色
//代理角色:中介
public class Proxy implements Rent {
private Host host;
public Proxy() { }
public Proxy(Host host) {
this.host = host;
}
// 租房
public void rent(){
seeHouse();
host.rent();
fare();
}
// 看房
public void seeHouse(){
System.out.println("带房客看房");
}
// 收中介费
public void fare(){
System.out.println("收中介费");
}
}
Client.java
调用方,即客户
// 客户类,一般客户都会去找代理!
public class Client {
public static void main(String[] args) {
// 房东要租房
Host host = new Host();
// 中介帮助房东
Proxy proxy = new Proxy(host);
// 你去找中介!
proxy.rent();
}
}
静态代理的缺点:
需要手动创建代理类,如果需要代理的对象多了,那么代理类也越来越多。
为了解决,这个问题,就有了 * !
2、 *
说到 * ,面试的时候肯定会问 * 的两种实现方式:
先来看公共的 UserService
接口,和 UserServiceImpl
实现类:
/**
* @author csp
* @date 2021-06-03
*/
public interface UserService {
/**
* 登录
*/
void login();
/**
* 登出
*/
void logout();
}
/**
* @author csp
* @date 2021-06-03
*/
public class UserServiceImpl implements UserService{
@Override
public void login() {
System.out.println("用户登录...");
}
@Override
public void logout() {
System.out.println("用户推出登录...");
}
}
JDK *
代码如下:
/**
* @author csp
* @date 2021-06-03
*/
public class JDKProxyFactory implements InvocationHandler {
// 目标对象(被代理对象)
private Object target;
public JDKProxyFactory(Object target) {
super();
this.target = target;
}
/**
* 创建代理对象
*
* @return
*/
public Object createProxy() {
// 1.得到目标对象的类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 2.得到目标对象的实现接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 3.第三个参数需要一个实现invocationHandler接口的对象
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaces, this);
return newProxyInstance;
}
/**
* 真正执行代理增强的方法
*
* @param proxy 代理对象.一般不使用
* @param method 需要增强的方法
* @param args 方法中的参数
* @return
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK * :登录/登出前逻辑校验......");
Object invoke = method.invoke(target, args);
System.out.println("JDK * :登录/登出后日志打印......");
return invoke;
}
public static void main(String[] args) {
// 1.创建对象
UserServiceImpl userService = new UserServiceImpl();
// 2.创建代理对象
JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(userService);
// 3.调用代理对象的增强方法,得到增强后的对象
UserService userServiceProxy = (UserService) jdkProxyFactory.createProxy();
userServiceProxy.login();
System.out.println("==================================");
userServiceProxy.logout();
}
}
输出结果如下:
JDK * :登录/登出前逻辑校验......
用户登录...
JDK * :登录/登出后日志打印......
==================================
JDK * :登录/登出前逻辑校验......
用户推出登录...
JDK * :登录/登出后日志打印......
CGLIB *
代码如下:
/**
* @author csp
* @date 2021-06-03
*/
public class CglibProxyFactory implements MethodInterceptor {
// 目标对象(被代理对象)
private Object target;
// 使用构造方法传递目标对象
public CglibProxyFactory(Object target) {
super();
this.target = target;
}
/**
* 创建代理对象
*
* @return
*/
public Object createProxy() {
// 1.创建Enhancer
Enhancer enhancer = new Enhancer();
// 2.传递目标对象的class
enhancer.setSuperclass(target.getClass());
// 3.设置回调操作
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 真正执行代理增强的方法
* @param o 代理对象
* @param method 要增强的方法
* @param objects 要增强方法的参数
* @param methodProxy 要增强的方法的代理
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib * :登录/登出前逻辑校验......");
Object invoke = method.invoke(target, objects);
System.out.println("cglib * :登录/登出后日志打印......");
return invoke;
}
public static void main(String[] args) {
// 1.创建对象
UserServiceImpl userService = new UserServiceImpl();
// 2.创建代理对象
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(userService);
// 3.调用代理对象的增强方法,得到增强后的对象
UserService userServiceProxy = (UserService) cglibProxyFactory.createProxy();
userServiceProxy.login();
System.out.println("==================================");
userServiceProxy.logout();
}
}
测试结果如下:
cglib * :登录/登出前逻辑校验......
用户登录...
cglib * :登录/登出后日志打印......
==================================
cglib * :登录/登出前逻辑校验......
用户推出登录...
cglib * :登录/登出后日志打印......
面试题一:JDK * 和CGLIB * 区别?
① JDK * 本质上是实现了被代理对象的接口,而 CGLib 本质上是继承了被代理对象,覆盖其中的方法。
② JDK * 只能对实现了接口的类生成代理,CGLib 则没有这个限制。但是 CGLib 因为使用继承实现,所以 CGLib 所以无法对 final
类、private
方法和 static
方法进行代理。
③ JDK * 是 JDK 里自带的,CGLib * 需要引入第三方的 jar
包。
④ 在调用代理方法上,JDK * 是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用。(看过一篇文章,介绍说 FastClass 简单的理解,就是使用一个 index 下标作为入参,可以直接定位到要调用的方法直接,并进行调用)
在性能上,JDK1.7 之前,由于使用了 FastClass 机制,CGLib 在执行效率上比 JDK 快,但是随着 JDK * 的不断优化,从 JDK 1.7 开始,JDK * 已经明显比 CGLib 更快了。
面试题二:JDK * 为什么只能对实现了接口的类生成代理?
根本原因是通过 JDK * 生成的类已经继承了 Proxy 类,所以无法再使用继承的方式去对类实现代理。
来源:https://csp1999.blog.csdn.net/article/details/117672843#comments_16904981
猜你喜欢
- 本文研究的主要是Collections.shuffle()方法的相关内容,下面看看具体内容。Java.util.Collections类下有
- 一、效果展示初级难度中级难度高级难度测试界面二、项目介绍项目背景扫雷是一款大众类的益智小游戏。根据点击格子出现的数字找出所有非雷格子,同时避
- 一个是新浪微博,腾讯微博的分享按钮,一个是他们的绑定情况(其实就是是否授权)。点击微博分享中新浪或腾讯按钮,就进行相应的授权(若没授权),显
- 1、下载源代码点击这个链接:http://www.scala-lang.org/download/all.html选择需要的版本点击打开;在
- 栅栏类似闭锁,但是它们是有区别的.1.闭锁用来等待事件,而栅栏用于等待其他线程.什么意思呢?就是说闭锁用来等待的事件就是countDown事
- 深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象
- 1.类成员与方法的可见性最小化举例:如果是一个private的方法,想删除就删除如果一个public的service方法,或者一个publi
- 安装容易出现的问题以及解决方法:1、更新sdk时可能无法连接服务器,可在C:\WINDOWS\system32\drivers\etc下的h
- 一.瀑布模型瀑布模型严格遵循软件生命周期各阶段的固定顺序:计划、分析、设计、编程、训试和维护,上一阶段完成后才能进入到下一阶段, 整个模型就
- 面试题:1.如何保证多线程下 i++ 结果正确?2.一个线程如果出现了运行时异常会怎么样?3.一个线程运行时发生异常会怎样?为了避免临界区的
- 目录wait-notifyjoin方式ReentrantLockReentrantLock+ConditionSemaphore三个线程T1
- 背景WebView是在APP中,可以很方便的展示web页面,并且与web交互APP的数据。方便,并且更新内容无需APP发布新版本,只需要将最
- 一、前言java是一门跨硬件平台的面向对象高级编程语言,java程序运行在java虚拟机上(JVM),由JVM管理内存,这点是和C++最大区
- Android N 可以同时显示多个应用窗口。 在手机上,两个应用可以在“分屏”模式中左右并排或上下并排显示。例如,用户可以 在上面窗口聊Q
- Swing 的组件与AWT 组件相似,但又为每一个组件增添了新的方法,并提供了更多的高级组件.Swing 的基本组件:1.按钮(JButto
- 本文实例为大家分享了C#实现简单串口通讯的具体代码,供大家参考,具体内容如下参数设置界面代码:using System;using Syst
- 废话不多说了,直接给大家贴代码了,具体代码如下所述:package com.example.esp8266;import java.io.I
- 本篇介绍我们如何利用selenium 来操作各种页面元素阅读目录链接(link)输入框 textbox按钮(Button)下拉选择框(Sel
- 每一个基于java的应用程序都有一个共同工作来展示给用户看到的内容作为工作的应用几个对象。当编写一个复杂的Java应用程序,应用程序类应该尽
- 一.Mybatis-Plus——sum聚合函数//总收益 Order order =new Orde