1、什么是代理
生活中的代理
1、房产中介,租户不能直接对接房主,要通过中介。中介就是代理,同时中介提供价格调整、房屋维修等增强功能。
2、商家店铺,用户不能直接去厂家买东西,要通过商家。商家就是代理,同时商家提供商品价格调整、举办优惠活动等增强功能。
由上面的例子可以看出,代理和目标都实现了同一种功能,只不过代理在原功能的基础上进行了功能增强。(中介和房主都提供房屋出租的功能,中介只不过是在原功能的基础上进行了价格调整、房屋装修等额外功能)
2、代理的特点
- 功能增强(在原功能的基础上封装其他功能)
- 访问控制(某些情况下,不允许用户直接访问目标地址,必须通过中间代理)
3、静态代理
一个代理类就专门处理一个目标类,代码简单,但是不具备通用性。
package proxy.staticproxy;
/**
* 售卖主方法
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class ShopMain {
public static void main(String[] args) {
KingStaticProxyImpl proxy = new KingStaticProxyImpl();
System.out.println("买10个");
System.out.println(proxy.sell(10));
System.out.println();
System.out.println("买110个");
System.out.println(proxy.sell(110));
}
}
package proxy.staticproxy;
/**
* U盘售卖接口
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public interface UsbSellService {
/**
* 销售
*
* @param amount 数量
* @return 金额
*/
double sell(int amount);
}
package proxy.staticproxy;
/**
* 金士顿厂家
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class KingFactoryImpl implements UsbSellService {
@Override
public double sell(int amount) {
//厂家,一个U盘卖50
return 50 * amount;
}
}
package proxy.staticproxy;
/**
* 金士顿静态代理
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class KingStaticProxyImpl implements UsbSellService {
/** 厂家的实例 */
UsbSellService service = new KingFactoryImpl();
@Override
public double sell(int amount) {
//若一次购买超过100个,赠送10元优惠券一张
if (amount > 100) {
System.out.println("赠送10元优惠券一张");
}
//在原有价格基础上,加价10%
return service.sell(amount) * 1.1;
}
}
4、动态代理
1、动态代理是指代理类对象在程序运行时由JVM根据反射机制动态生成的。动态代理不需要定义代理类的java源文件。
2、动态代理其实就是:在jdk运行期间动态创建class字节码并加载到JVM。
3、动态代理的实现方式常用的有两种:jdk动态代理、cglib动态代理
jdk动态代理和cglib动态代理(保证代理类和目标类有同名方法)
1、jdk动态代理是基于接口的方式,即代理类和目标类都实现同一个接口。
2、cglib动态代理是代理类继承目标类,然后重写其中目标类的方法。
4.1 jdk动态代理
1、使用java反射包中的类和接口实现动态代理的功能,会使用到反射包java.lang.reflect里面的3个类:InvocationHandler、Method、Proxy
2、jdk动态代理需要实现类通过接口定义业务方法,所以必须有接口。
- InvocationHandler接口
1、需要创建一个类,实现InvocationHandler接口,再重写invoke方法。
2、InvocationHandler接口中只有一个方法invoke(),代理对象的增强代码需要写在这个方法中。(实现原来静态代理类中的功能)
- Method类
method.invoke()就是执行目标类中的方法的。method.invoke(目标对象, 方法参数)
- Proxy类
创建代理对象,不再使用new方法创建代理对象,而是使用Proxy类的方法。
还是使用上面的例子展示一下jdk动态代理:
package proxy.dynamicproxy;
import java.lang.reflect.Proxy;
/**
* 售卖主方法
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class ShopMain {
public static void main(String[] args) {
//创建目标对象
KingFactoryImpl target = new KingFactoryImpl();
//创建InvocationHandler对象
MyInvocationHandler handler = new MyInvocationHandler(target);
//使用Proxy创建代理
UsbSellService sellService = (UsbSellService)Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler);
//通过代理执行方法,会调用handler中的invoke方法
System.out.println("买10个");
System.out.println(sellService.sell(10));
System.out.println();
System.out.println("买110个");
System.out.println(sellService.sell(110));
}
}
package proxy.dynamicproxy;
/**
* U盘售卖接口
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public interface UsbSellService {
/**
* 销售
*
* @param amount 数量
* @return 金额
*/
double sell(int amount);
}
package proxy.dynamicproxy;
/**
* 金士顿厂家
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class KingFactoryImpl implements UsbSellService {
@Override
public double sell(int amount) {
//厂家,一个U盘卖50
return 50 * amount;
}
}
package proxy.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 实现InvocationHandler接口,实现代理功能
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class MyInvocationHandler implements InvocationHandler {
/** 目标对象 */
private Object object;
public MyInvocationHandler(Object object) {
this.object = object;
}
/**
* 通过代理对象执行方法时,会调用执行这个invoke方法
*
* @param proxy 代理类实例
* @param method 被代理的方法
* @param args 方法参数
* @return 方法执行结果
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入了MyInvocationHandler的invoke方法");
System.out.println("method.getName():" + method.getName());
Object res = method.invoke(object, args);
if ((int)args[0] > 100) {
System.out.println("赠送10元优惠券一张");
}
return res;
}
}
4.2 cglib动态代理
1、cglib是第三方的工具库,创建代理对象
2、cglib的原理是继承,重写方法,所以要求目标类不能是final的、方法也不能是final的。
3、cglib的要求目标类比较宽松,只要能继承就可以。
4、cglib非jdk功能,需要额外导入java包。
cglib动态代理需要先引入cglib的包
implementation("cglib:cglib:3.3.0")
下面依然使用之前的U盘举例:
package proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
/**
* 售卖主方法
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class ShopMain {
public static void main(String[] args) {
//创建Enhancer对象,类似于JDK动态代理中的Proxy
Enhancer enhancer = new Enhancer();
//设置目标类
enhancer.setSuperclass(KingFactoryImpl.class);
//设置回调函数
enhancer.setCallback(new MyMethodInterceptor());
//正式创建代理类
KingFactoryImpl proxy = (KingFactoryImpl)enhancer.create();
//通过代理执行方法,会调用proxy中的intercept方法
System.out.println("买10个");
System.out.println(proxy.sell(10));
System.out.println();
System.out.println("买110个");
System.out.println(proxy.sell(110));
}
}
package proxy.cglib;
/**
* 金士顿厂家
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class KingFactoryImpl {
public double sell(int amount) {
//厂家,一个U盘卖50
return 50 * amount;
}
}
package proxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 实现MethodInterceptor接口,实现代理功能
*
* @author zhao.hualuo
* Create at 2022/4/28
*/
public class MyMethodInterceptor implements MethodInterceptor {
/**
* cglib拦截方法
*
* @param obj cglib生产的代理对象
* @param method 被代理对象的方法
* @param args 方法参数
* @param proxy 代理方法
* @return 返回值
* @throws Throwable 异常
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if ((int)args[0] > 100) {
System.out.println("赠送10元优惠券一张");
}
Object object = proxy.invokeSuper(obj, args);
return (double)object * 1.1;
}
}