一、动态代理_代理模式简介
代理模式是23种设计模式之一。设计模式是前人总结的,在软件开发过程遇到常用问题的解决方案,常见的设计模式有单例模式、工厂模式、适配器模式等等。
代理模式的作用是在不修改原对象的基础上增强该对象的方法。比如官方购买苹果手机不赠送充电头,此时京东平台作为苹果的代理商,可以在代理销售苹果手机时赠送充电头。
代理模式分为静态代理、动态代理。静态代理会生成一个代理类,动态代理不会生成代理类,直接生成代理对象。
==动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术==。在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的。也就是说,你想获取哪个对象的代理,动态代理就会为你动态的生成这个对象的代理对象。动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术,那么就可以在不修改方法源码的情况下,增强被代理对象的方法的功能,在方法执行前后做任何你想做的事情。
二、动态代理_JDK动态代理 dynamic
JDK动态代理是针对接口进行代理,所以我们要写被代理的接口和该接口的实现类。
// 被代理接口
代码语言:javascript复制package com.example.dynamic;
public interface Apple {
// 卖产品
String sell(double price);
// 维修
void repair();
}
// 被代理接口的实现类
代码语言:javascript复制package com.example.dynamic;
public class AppleImpl implements Apple{
@Override
public String sell(double price) {
System.out.println("产品卖了" price "元");
return "ipone14";
}
@Override
public void repair() {
System.out.println("苹果售后维修");
}
}
// 代理方式类,定义被代理方法的增强方式
// 该类实现InvocationHandler接口,重写invoke方法,定义方法的增强方式
代码语言:javascript复制package com.example.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 代理方式类,定义被代理方法的增强方式
// 该类实现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(name, args);
}
}
}
接下来测试一下:
代码语言:javascript复制package com.example.dynamic;
import java.lang.reflect.Proxy;
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 // 代理方式对象
);
// 执行未增强的方法
System.out.println("执行未增强的方法");
String sell = apple.sell(6000);
System.out.println(sell);
apple.repair();
System.out.println("--------------------------");
// 执行增强后的方法
System.out.println("执行增强后的方法");
String sellProxy = appleJD.sell(6000);
System.out.println(sellProxy);
appleJD.repair();
}
}
运行结果:
ok 啊,确实实现了动态代理模式,但是这个时基于JDK的动态代理模式,还是需要我们自己去写接口,接下来介绍一种基于CGLib的动态代理模式
三、动态代理_CGLib动态代理
CGLib动态代理简化了JDK动态代理的写法,JDK是针对接口代理,而CGLib是针对类代理。
我们得在pom.xml文件引进cglib的依赖,所以我们现在pom.xml加上以下代码
代码语言:javascript复制<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
// 被代理类
代码语言:javascript复制package com.example.dynamic_cglib;
public class Apple {
// 卖产品
public String sell(double price){
System.out.println("产品卖了" price "元");
return "ipone13";
}
// 维修
public void repair(){
System.out.println("苹果售后维修");
}
}
// 代理方式类,实现MethodInterceptor接口,重写intercept方法
代码语言:javascript复制package com.example.dynamic_cglib;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class ShoppingProxy implements MethodInterceptor{
// 被代理对象
private Apple apple;
public ShoppingProxy(Apple apple){
this.apple = apple;
}
/**
* 定义原方法的增强方式
* @param o 被代理对象
* @param method 被代理对象调用的方法
* @param args 被代理对象调用的方法时,传入的参数
* @param methodProxy 底层生成的代理类的引用
* @return 方法的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String name = method.getName();
if("sell".equals(name)){
double price = (double) args[0]*0.8;
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);
}
}
}
ok ,再让我们写一个测试类,看看能否好使,注意这里生成代理对象就不是用JDK那个方法了
代码语言:javascript复制package com.example.dynamic_cglib;
import org.springframework.cglib.proxy.Enhancer;
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);
// 执行未增强的方法
System.out.println("执行未增强的方法");
String sell = apple.sell(9000);
System.out.println(sell);
apple.repair();
System.out.println("-------------------");
// 执行增强后的方法
String sellProxy = appleTB.sell(9000);
System.out.println(sellProxy);
appleTB.repair();
}
}
ok,看一下运行结果,也确实进行了动态代理。
四、JDK和CGLib动态代理的区别
所以,通过上述两个例子我们可以知道关于JDK动态代理和CGLib动态代理的区别就是JDK是基于接口的,而CGLib是基于类的。
所以,你学hui了吗?
我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!