01,什么是代理模式?
- 代理(Proxy)是设计模式的一种,提供了间接对被代理象进行访问的方式,当一个类没有被代理时,我们访问的方式是直接访问,当被代理之后,我们可以通过代理对象间接访问被代理对象,这样做的好处是,可以在被代理对象实现的功能上通过代理对象
增加额外的功能
。 - 举个现实生活中的例子:假设我们买一台电脑,我们肯定不会直接去厂家买,而是去旗舰店或者体验店去卖,这里的工厂就相当于一个被代理的类,旗舰店或者体验店就相当于代理类
- 代理模式分为:静态代理模式和动态代理模式
- 动态代理模式又可分为两种:基于接口的动态代理,基于子类代理的动态代理(Cglib代理)
02 ,静态代理
- 在使用静态代理时,被代理对象与代理对象需要一起实现
相同的接口
或者是继承相同父类
,因此要定义一个接口或抽象类 - 定义一个生产者的接口
package com.aismall.staticProxy;
/*模拟生产的接口*/
public interface IProducer {
/*销售*/
public void saleProduct(float money);
}
- 定义生产者的实现类
package com.aismall.staticProxy;
/*模拟生产厂家类,实现生产接口*/
public class Pruducer implements IProducer {
/*销售*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱"+money);
}
}
- 为了代理这个生产者的实现类,我们需要创建一个代理类,并且还要实现被代理类实现的接口
package com.aismall.staticProxy;
/*模拟代理类,实现生产接口*/
public class ProxyShop implements IProducer {
//创建一个接口形式的变量,用来接收实现类的实例(多态的思想)
private IProducer producer;
public ProxyShop(){
super();
}
//有参构造,要注意这个形参,也是接口类型的变量
public ProxyShop(IProducer producer) {
super();
//变量赋值
this.producer=producer;
}
/*销售*/
public void saleProduct(float money){
//方法增强的部分
enhanceMethod();
//被代理的方法
producer.saleProduct(1000*0.8f);
}
public void enhanceMethod(){
System.out.println("产品被代理商售出,代理商扣除了20%的费用");
}
}
- 编写测试类
package com.aismall.staticProxy;
public class ststicProxyTest {
public static void main(String[] args) {
//创建一个生产厂家的实例
IProducer producer=new Pruducer();
//创建一个代理商的实例,生厂家的实例作为参数传递
ProxyShop proxyShop=new ProxyShop(producer);
//调用销售方法(此方法内部调用了生产厂家的销售方法并对其进行了增强)
proxyShop.saleProduct(1000);
}
}
- 运行结果
产品被代理商售出,代理商扣除了20%的费用
厂家拿到钱800.0
- 分析
本来厂家一台电脑可以买1000元,由代理商代买之后,代理商扣除了20%
的费用,最后厂家得到了800,这个扣除费用的功能,就是对saleProduct
方法的增强
- 静态代理总结:
优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,同时,一旦接口增加方法,目标对象与代理对象都要维护。 - 而动态代理方式可以解决上面的问题
03,动态代理
- 动态代理:
特点:字节码随用随创建,随用随加载
作用:不修改源码的基础上对方法进行增强 - 分类:
基于接口的动态代理:JDK动态代理
基于子类的动态代理:CGLIB动态代理
3.1,基于接口的动态代理
- 基于接口的动态代理
涉及的类:Proxy
提供者:JDK官方
- 如何创建代理对象:
使用Proxy类中的newProxyInstance方法 - 创建代理对象的要求:
被代理类至少实现一个接口,如果没有则不能使用,为什么有这条规则,我们可以通过查看newProxyInstance方法
的源码得知:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{.....}
-
newProxyInstance方法的参数介绍:
ClassLoader
:用于加载被代理对象的字节码,写的是被代理对象的类加载器或者和被代理对象使用相同的类加载器,固定写法Class[]
:字节码数组,用于加载被代理对象所实现的接口的字节码
,目的是让代理对象和被代理对象拥有相同的方法(因为java类可以实现多个接口,所以这里使用字节码数组,固定写法,就是因为这个参数
,被代理对象不实现接口就无法使用这种动态代理方式
。InvocationHandler接口
:可以在此接口的实现类
中书写用于增强的代码
,它是写如何代理,这里通常情况下都是使用匿名内部类的方式InvocationHandler接口
中只有一个方法:invoke
作用:执行被代理对象的任何接口方法都会经过该方法
public Object invoke(Object proxy, Method method, Object[] args)
invoke
方法参数介绍:proxy
:代理对象的引用method
:当前执行的方法的对象的引用args
:当前执行的方法所需要的参数return
:和被代理对象相同的返回值
-
定义一个生产者接口
package com.aismall.dynamicProxy;
/*模拟生产的接口*/
public interface IProducer {
/*销售*/
public void saleProduct(float money);
}
- 定义一个生产者接口的实现类
package com.aismall.dynamicProxy;
/*模拟生产厂家*/
public class Pruducer implements IProducer {
/*销售*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱"+money);
}
}
- 定义代理类并实现销售功能
package com.aismall.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class proxyShop {
public static void main(String[] args) {
//创建一个生产工厂的实例
final Pruducer pruducer=new Pruducer();
// 调用Proxy类中的静态方法:newProxyInstance
IProducer proxyProducer=(IProducer) Proxy.newProxyInstance(pruducer.getClass().getClassLoader(),
pruducer.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*在此提供增强的代码*/
//创建一个用于返回的对象变量
Object returnValue=null;
//1.获取方法执行的参数
Float money=(Float)args[0];
//2.判断方法是不是销售方法
if("saleProduct".equals(method.getName())){
returnValue=method.invoke(pruducer,0.8f*money);
}
return returnValue;
}
});
proxyProducer.saleProduct(1000);
}
}
- 运行结果
销售产品,并拿到钱800.0
- 总结:
代理对象不需要实现接口
,但是被代理对象一定要实现接口
,否则不能使用这种动态代理方式,因此这也算是这种方式的缺陷。
3.2,基于子类的动态代理(cglib代理)
- 基于子类的动态代理:
涉及的类:Enhancer
提供者:第三方cglib库
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_3</version>
</dependency>
</dependencies>
-
如何创建代理对象:
- 使用Enhancer类中的
create方法
- 源码如下:
public static Object create(Class type, Callback callback) { Enhancer e = new Enhancer(); e.setSuperclass(type); e.setCallback(callback); return e.create(); }
- 使用Enhancer类中的
-
创建代理对象的要求:
- 被代理类不能是最终类(也就是没有被final修饰的类)
-
create方法的参数介绍:
Class:字节码
,用于指定被代理对象的字节码,固定写法Callback接口
:在此接口的实现类中书写用于增强的代码
,我们一般使用它的子接口MethodInterceptor接口
MethodInterceptor接口
中只有一个方法intercept
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
- intercept方法:
作用:执行被代理对象的任何接口方法都会经过该方法
- intercept方法参数介绍:
o
:代理对象的引用method
:当前执行的方法的对象的引用objects
:当前执行的方法所需要的参数methodProxy
:当前执行方法的代理对象return
:和被代理对象相同的返回值
-
定义一个生产者类
package com.aismall.dynamicProxy;
/*模拟生产厂家*/
public class Pruducer {
/*销售*/
public void saleProduct(float money){
System.out.println("销售产品,并拿到钱"+money);
}
}
- 定义代理类并实现销售功能
package com.aismall.cglibProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class proxyShop {
public static void main(String[] args) {
final Pruducer pruducer=new Pruducer();
Pruducer cglibProducer= (Pruducer) Enhancer.create(pruducer.getClass(), new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//在此书提供增强的代码
Object returnValue=null;
//1.获取方法执行的参数
Float money=(Float)objects[0];
//2.判断方法是不是销售方法
if("saleProduct".equals(method.getName())){
returnValue=method.invoke(pruducer,0.8f*money);
}
return returnValue;
}
});
cglibProducer.saleProduct(1000);
}
}
- 运行结果
销售产品,并拿到钱800.0