【java设计模式】——代理设计模式,两种举例说明
代理设计模式
1.介绍
Spring 框架中AOP底层使用动态代理设计模式。通过学习动态代理设计模式可以很好的理解Spring框架AOP底层
代理模式(Proxy)是GoF23种设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
代理设计模式包括:静态代理和动态代理。
静态代理:代理对象由程序员自己编写,里面提供硬编码方式来访问调用者
动态代理:
- JDK动态代理:一种基于接口的代理实现
- Cglib动态代理:一种基于继承的代理实现
代理设计模式优点:
- 保护代理对象。客户不能直接访问真实对象。只能通过代理对象进行访问
- 可扩展性。复合开闭原则,可以实现在不修改真实代理对象情况下,进行扩展。
- 对于一些访问困难的真实对象,可以使用代理对象,更轻松的访问。
代理设计模式缺点:
- 系统复杂性增加。类的数目增加,代码相对更复杂一点。
- 性能降低。每次访问真实对象,只能通过代理对象。
2.静态代理示例
以房屋买卖为例子(简易流程),客户想要买房,找中介,由中介带看房东的房子。
客户看好了,想要买,房东也同意买了,中介收取客户的中介费。
创建房东(真实对象)
新建com.spring.proxy.staticproxy.Fangdong
public class Fangdong {
public void bigHouse(){
System.out.println("房东的大房子");
}
}
创建中介类(代理对象)
新建com.spring.proxy.staticproxy.Zhongjie
public class Zhongjie {
public void zhongjie(){
System.out.println("约谈客户,进行带看");// 扩展功能
Fangdong fangdong = new Fangdong();
fangdong.bigHouse();
System.out.println("交易成功,收取中介费"); // 扩展功能
}
}
创建客户类(调用者)
com.spring.proxy.staticproxy.Kehu
public class Kehu {
public static void main(String[] args) {
Zhongjie zhongjie = new Zhongjie();
zhongjie.zhongjie();
}
}
3.动态代理示例
3.1JDK动态代理
JDK动态代理是基于接口来实现的,底层是基于Java 反射技术实现的。
创建接口
创建接口com.spring.proxy.jdkproxy.House
因为JDK动态代理要求调用者必须实现接口。所以先建立接口
public interface bigHouse {
void bighose();
}
创建真实对象类
创建类com.spring.proxy.jdkproxy.Fangdong
/**
* JDK动态代理强制要求:真实的被调用的类必须实现接口
*/
public class FangDong implements bigHouse{
@Override
public void bighose() {
System.out.println("大房子");
}
}
创建代理类
创建com.spring.proxy.jdkproxy.Zhongjie
/**
* 在JDK动态代理底层是基于java的反射机制实现的。本质上就是方法的调用
*
* 负责调用真实对象方法的类,必须实现InvocationHandler接口
*/
public class ZhongJie implements InvocationHandler {
private FangDong fangDong;
public ZhongJie(FangDong fangDong) {
this.fangDong = fangDong;
}
/**
*
* @param proxy 产生的代理对象
* @param method 真实对象中被调用的方法对象
* @param args 被调用方法的参数
* @return 被调用方法的返回值,都当做invoke方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("带看");
//坑:千万不要用invoke方法的第一个参数proxy,他是代理对象,而不是真实对象
Object result = method.invoke(fangDong, args);
System.out.println("中介费");
return result;
}
}
创建客户类
创建com.spring.proxy.jdkproxy.Kehu
public class KeHu {
public static void main(String[] args) {
/**
* 参数1:ClassLoader 在当前程序中,所有类的类加载器都是同一个对象
* 作用:让生成的代理对象立即生效,(invoke中的proxy对象)
* 参数2:Class[],放接口,告诉编译器,调用的是哪个方法
* 作用:数组中放哪个接口,接口中的方法对象就会传递给invoke中的method方法对象
* 参数3:InvocationHandler对象,负责调用真实对象的处理类,就是ZhongJie
*/
//动态代理:动态:不是就调用某个对象的方法,而是想调哪个对象都可以
InvocationHandler zhongJie = new ZhongJie(new FangDong());
bigHouse bigHouse = (bigHouse) Proxy.newProxyInstance(Kehu.class.getClassLoader(),
new Class[]{bigHouse.class}, zhongJie);
bigHouse.bighose();
}
}
第二例子
// 被代理接口
public interface Apple {
String sell(double price);//卖产品
void repair();// 维修
}
// 被代理接口的实现类
public class AppleImpl implements Apple{
@Override
public String sell(double price) {
System.out.println("产品卖了"+price+"元");
return "iphone13";
}
@Override
public void repair() {
System.out.println("苹果售后维修");
}
}
// 代理方式类,定义被代理方法的增强方式
// 该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式
public class ShoppingProxy implements InvocationHandler {
private Apple apple;// 被代理对象
public ShoppingProxy(Apple apple) {
this.apple = apple;
}
/**
* 定义原方法的增强方式
* @param proxy 被代理对象
* @param method 被代理对象调用的方法
* @param args 被代理对象调用的方法时,传入的参数
* @return 方法的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName(); //被代理对象执行的方法名
if("sell".equals(name)){
double price = (double)args[0]*0.9; //增强参数
Object result = method.invoke(apple, price); // 执行方法
return result + "和充电头"; // 增强返回值
}else if("repair".equals(name)){
System.out.println("专属客服为您服务!"); // 增强方法流程
return method.invoke(apple,args);
}else{
return method.invoke(apple,args); // 什么都不增强
}
}
}
public class Test {
public static void main(String[] args) {
// 被代理对象
Apple apple = new AppleImpl();
// 代理方式对象
ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
// 生成代理对象
Apple appleJD = (Apple) Proxy.newProxyInstance(
apple.getClass().getClassLoader(), // 类加载器
apple.getClass().getInterfaces(),//被代理接口
shoppingProxy //代理方式对象
);
// 执行增强后的方法
String sell = appleJD.sell(6000);
System.out.println(sell);
appleJD.repair();
}
}
3.2Cglib动态代理
Cglig是基于继承的,是第三方提供的技术,需要导入jar包,并且。
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
创建房东类
/**
* 真实被调用对象
*/
public class FangDong {
public void bigHouse(){
System.out.println("大房子");
}
}
创建中介类
/**
* 作用:调用真实对象的方法
*/
public class ZhongJie implements MethodInterceptor {
/**
*
* @param o 目标对象:即FangDong对象
* @param method 目标方法
* @param objects 目标方法参数
* @param methodProxy 生成的代理对象(即FangDong的子类)
* @return 目标方法返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("带看");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("中介费");
return result;
}
}
创建客户类
public class KeHu {
public static void main(String[] args) {
//所有方法都在Enhancer中
Enhancer enhancer = new Enhancer();
//1.setSuperClass(真实是哪个类) 真实是哪个类,根据这个类创建代理对象,即FangDong的子类
enhancer.setSuperclass(FangDong.class);
//2.setCallBack(对象) 设置由哪个类作为增强实现类
enhancer.setCallback(new ZhongJie());
//3.create() 创建代理对象,返回值为object类型,本质为代理对象
FangDong fangDong = (FangDong) enhancer.create();
fangDong.bigHouse();
}
}
测试结果,发现出现异常,这是因为Java 17版本中的Java Platform Module System(java 9就开始有了)引起的,特别是强封装的实现。
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @1376c05c
把代码原封不动的放在Java 11的环境中就可以使用。
如果必须要在Java17中使用添加JVM参数,表示允许使用未命名模块。
--add-opens java.base/java.lang=ALL-UNNAMED
第二例子
<!-- 引入cglib依赖 -->
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
// 被代理类
public class Apple{
public String sell(double price) {
System.out.println("产品卖了"+price+"元");
return "iphone13";
}
public void repair() {
System.out.println("苹果售后维修");
}
}
// 代理方式类,实现MethodInterceptor接口,重写intercept方法
public class ShoppingProxy implements MethodInterceptor {
private Apple apple; // 被代理对象
public ShoppingProxy(Apple apple) {
this.apple = apple;
}
/**
* 定义原方法的增强方式
* @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 {
String name = method.getName();
if("sell".equals(name)){
double price = (double)objects[0]*0.8;
Object result = method.invoke(apple, price);
return result+"和数据线";
}else if("repair".equals(name)){
System.out.println("专属客服为您服务!");
return method.invoke(apple,objects);
}else{
return method.invoke(apple,objects);
}
}
}
public class Test {
public static void main(String[] args) {
// 被代理对象
Apple apple = new Apple();
// 代理方式
ShoppingProxy shoppingProxy = new ShoppingProxy(apple);
// 生成代理对象
Apple appleTB = (Apple) Enhancer.create(Apple.class, shoppingProxy);
// 执行增强后的方法
String sell = appleTB.sell(9000);
System.out.println(sell);
appleTB.repair();
}
}