视频资料:动态代理视频(黑马程序员)
源码在我的资源里
代理就是代替处理,就是另一个对象代理原来的对象处理某些逻辑
代理可以给我们带来什么?
- 在不改变原对象代码的基础上,对原对象的功能进行修改或者增强
- 解耦
java中的代理模式
java动态代理生成方式分三种:静态代理,基于 jdk (接口) 动态代理、CGLIB(父类)的动态代理
一个静态,两个动态
目标类:原对象,(演员本人) 代理类:代理模式产生的对象,在原有的基础上修改逻辑,并不改变目标类本身(替身)
替身比演员本人厉害,可以做出演员做不出的动作
静态代理
静态代理:我们自己手写的代理类,工程中有代理类的源码
继承某种程度上来说就是静态代理(子类的方法中调用了父类的方法)
继承:
父:
public class Customer {
public String order(String foodName){
return "已经下单点了"+foodName;
}
}
子:
public class DeliveryClerk extends Customer{
@Override
public String order(String foodName) {
String result = super.order(foodName);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result+",已经搅拌均匀";
}
}
test:
public static void main(String[] args) {
Customer customer = new DeliveryClerk();
String result = customer.order("666");
System.out.println(result);
}
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了666,已经搅拌均匀
接口实现
接口:
public interface OrderInterface {
String order(String foodName);
}
”父类(目标类)“实现接口
public class Customer implements OrderInterface{
@Override
public String order(String foodName){
return "已经下单点了"+foodName;
}
}
”子类(代理类)“实现接口
public class DeliveryClerk2 implements OrderInterface{
//把原来的对象传入,并保存带成员变量,也就是目标类对象
private OrderInterface source;
public DeliveryClerk2(OrderInterface orderInterface) {
this.source = orderInterface;
}
@Override
public String order(String foodName) {
String result = source.order(foodName);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result+",已经搅拌均匀";
}
}
测试:
public static void main(String[] args) {
Customer customer = new Customer();
//创建代理对象,也就是外卖小哥2
DeliveryClerk2 deliveryClerk2 = new DeliveryClerk2(customer);
String result = deliveryClerk2.order("红烧肉");
System.out.println(result);
}
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了红烧肉,已经搅拌均匀
动态代理
动态代理,是在内存中生成代理对象的一种技术,也就是整个代理过程在内存中进行,我们不需要手写代理类的代码,也不会存在代理雷编译的过程,而是直接在运行期,在jvm中”凭空“造出一个代理类对象供我们使用。一般使用动态代理技术有以下两种:
基于jdk(接口)的动态代理
传入目标类,重写InvocationHandler接口中的invoke()方法,通过Proxy . newProxyInstance(ClassLoader loader , Class<?>[] interfaces , InvocationHandler h)获得我们需要的代理类
jdk自带的动态代理技术,需要使用一个静态方法来创建代理对象。它要求被代理对象,也就是目标类,必须实现接口。生成的代理对象和原对象都实现相同的接口,是兄弟关系
public static void main(String[] args) {
//准备一个目标类对象,也就是顾客对象
Customer customer = new Customer();
//使用JDK的API,动态的生成一个代理对象
OrderInterface deliveryClerk = (OrderInterface) Proxy.newProxyInstance(
customer.getClass().getClassLoader(),
customer.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+" 我执行了..."+args[0]);
return null;
}
}
);
//调用代理对象,执行对应方法
String result = deliveryClerk.order("麻辣香锅");
System.out.println(result);
}
输出:
order 我执行了...麻辣香锅
null
Proxy . newProxyInstance(ClassLoader loader , Class<?>[] interfaces , InvocationHandler h)
- loader :固定写法,指定目标类对象的类加载器即可,用于加载目标类及其接口的字节码文件
- interfaces :固定写法,指定目标类的实现的所有接口的字节码对象的数组,通常,使用目标类的字节码对象调用getInterfaces()方法即可得到
- InvocationHandler 是一个接口,有唯一一个方法invoke()方法,它会在代理类对象调用方法时执行,
-
- Object invoke(Object proxy, Method method, Object[] args),我们在这个方法中完成对增强或者扩展代码逻辑的编写
- proxy:代理类对象的一个引用,也就是Proxy . newProxyInstance的返回(我指我自己:😅),此引用几乎不会用到
- method:对应的是出发invoke执行的方法的Method对象。假如我们调用xxx方法,xxx方法就会触发invoke的执行,那么method就是xxx方法对应的反射对象(Method对象)
- args:代理对象调用方法时,传递的实际参数
现在我们只需要修改invoke中的代码就可以改变输出
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射
Object result = method.invoke(customer, args);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result+",已经搅拌均匀";
}
输出:
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了麻辣香锅,已经搅拌均匀
我们来模拟一下jdk中的动态代理
我们把DeliveryClerk 方法中加入handler,在目标类中实现代理类
/**
* Proxy。newProxyInstance(
* ClassLoader loader ,
* Class<?>[] interfaces ,
* InvocationHandler h)
* 方法中做的事情,从底层看一看到底jdk如何完成的动态代理
*/
public class DeliveryClerk implements OrderInterface {
//接收外部传进来的handler
private InvocationHandler handler;
//InvocationHandler以有参构造的方式传进来
public DeliveryClerk(InvocationHandler handler) {
this.handler=handler;
}
@Override
public String order(String foodName) {
//每个方法的实现,实际上并没有做其他的事情,
// 而是直接调用传进来的handler的invoke()方法
//该invoke()方法就是我们在外面重写的invoke()方法
try {
//OrderInterface是传进来的参数
Method method=OrderInterface.class.getMethod("order", String.class);
String result =(String) handler.invoke(this, method, new Object[]{foodName});
return result;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@Override
public void test() {
}
}
main:
public class DynamicTest {
public static void main(String[] args) {
//准备一个目标类对象,也就是顾客对象
Customer customer = new Customer();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//methon反射调用
Object result = method.invoke(customer, args);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result+",已经搅拌均匀";
}
};
OrderInterface deliveryClerk = new DeliveryClerk(handler);
//调用代理对象,执行对应方法
String result = deliveryClerk.order("麻辣香锅");
System.out.println(result);
}
代理类和目标类是兄弟关系:并列关系,不能互相转换
基于CGLIB(父类)的动态代理
引入jar包
asm-7.1.jar
cglib-3.3.0.jar
第三方CGLIB的动态代理技术,也是可以使用一个静态方法来创建代理对象。
它不要求目标类实现接口,但要求目标类不能是最终类,也就是不能被final修饰()final修饰的类不能被其他类继承
Enhancer . create(Class type,Callback callback)
参数作用:
- type:指定我们要代理的目标类的字节码对象,也就是指定目标类的类型
- callback:此单词的意思是回调,意思就是我们提供一个方法,它会在合适的时候帮我们调用它
callback就是一个接口,并不包含方法的声明。所以我们使用时通常使用它的一个子接口MethodInterceptor,此单词的意思叫做方法拦截器。
MethodInterceptor里面有一个方法intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy)
和InvocationHandler类的invoke方法差不多
参数:
- proxy:代理类对象的一个引用,也就是Proxy . newProxyInstance的返回(我指我自己:😅),此引用几乎不会用到
- method:对应的是出发invoke执行的方法的Method对象。假如我们调用xxx方法,xxx方法就会触发invoke的执行,那么method就是xxx方法对应的反射对象(Method对象)
- objects:代理对象调用方法时,传递的实际参数
- methodProxy:方法的代理对象,一般也不作处理,这样暂时忽略
父子关系:代理对象可以用父类的引用接受的
public class DynamicTest {
public static void main(String[] args) {
//创建一个目标类
Customer customer = new Customer();
//使用CGLIB创建代理对象
Customer deliverClerk = (Customer) Enhancer.create(customer.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("order".equals(method.getName())) {
Object result = method.invoke(customer, args);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result + ",额外添加佐料";
} else {
return method.invoke(customer, args);
}
}
});
String result = deliverClerk.order("鱼香肉丝");
System.out.printf(result);
}
}
我们来模拟一下CGLIB中的动态代理
/**
* 模拟在内存中Enhancer.create(Class type, Callback callback)方法
*/
public class DeliveryClerk extends Customer {
private MethodInterceptor interceptor;
public DeliveryClerk(MethodInterceptor interceptor) {
this.interceptor = interceptor;
}
@Override
public String order(String foodName) {
try {
Method method=Customer.class.getMethod("order", String.class);
String result = (String)interceptor.intercept(this, method, new Object[]{foodName}, null);
return result;
} catch (Throwable e) {
e.printStackTrace();
}
return super.order(foodName);
}
@Override
public void test() {
super.test();
}
}
注意Customer没有实现接口(因为CGLIB不要求目标类实现接口)
public class Customer {
public String order(String foodName){
return "已经下单点了"+foodName;
}
public void test() {
}
}
public class DynamicTest {
public static void main(String[] args) {
//创建一个目标类
Customer customer = new Customer();
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if ("order".equals(method.getName())) {
Object result = method.invoke(customer, args);
System.out.println("已经接受订单,正前往取餐途中...");
System.out.println("已经取餐,正在配送...");
return result + ",额外添加佐料";
} else {
return method.invoke(customer, args);
}
}
};
Customer deliveryClerk = new DeliveryClerk(interceptor);
String result = deliveryClerk.order("111");
System.out.printf(result);
}
}
输出:
已经接受订单,正前往取餐途中...
已经取餐,正在配送...
已经下单点了111,额外添加佐料