文章目录
一、概念
为目标对象提供一种代理,以控制对其的访问。代理对象相当于中介,可以去掉功能服务或者增加添加额外功能服务。
举例:
今天我想去服装店A买件衣服,一般都是直接去A的实体店进行购买,但是今天下雨了不想出门,于是我就打开淘宝在A的官方旗舰店B把想要的衣服买了,那么B就是相当于A的一个代理。B又提供了其它一些服务,比如:七天无理由退换货、送货上门等(添加额外功能)。B又没有现场试穿等服务(去掉功能服务)。
1.1 作用
代理可以在不改变目标类原有代码的基础上拓展其功能。例如:对一些方法进行性能测试,添加操作日志等。AOP便是使用动态代理来实现拦截切入功能。
二、分类
2.1 静态代理
2.1.1 特点:
需要定义父类或者接口,代理对象和被代理对象需要同时继承父类或者实现该接口。
2.1.2 缺点:
每进行一个代理都要建立一个代理类,随着代理类增多会造成类膨胀。
2.1.3 实现:
有两种实现方式,继承与聚合,由于聚合的方式优于继承,这里只演示聚合的实现。
创建接口:目标类和代理类同时实现该接口。
public interface Moveable {
void move();
}
目标对象:
public class Car implements Moveable {
@Override
public void move() {
//实现开车
try {
System.out.println("汽车行驶中......");
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代理对象:
public class CarProxy implements Moveable {
private Car car;
public CarProxy(Car car) {
this.car = car;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶......");
car.move();
long endTime = System.currentTimeMillis();
System.out.println("汽车结束行驶...... 汽车行驶时间:"+(endTime-startTime)+"毫秒!");
}
}
测试:
public class ProxyTest {
public static void main(String[] args) {
Car car = new Car();
Moveable proxy = new CarProxy(car);
proxy.move();
}
}
测试结果
汽车开始行驶......
汽车行驶中......
汽车结束行驶...... 汽车行驶时间:724毫秒!
2.2 jdk动态代理
2.2.1 特点:
- 代理目标必须实现一个或者多个接口。
- 代理对象的生成不需要实现接口。
- 代理对象通过JAVA的API动态生成。
2.2.2 缺点:
没有实现接口的类不能进行动态代理。
2.2.3 实现:
Java动态代理类位于java.lang.reflect包下,主要涉及以下两个类
(1) public interface InvocationHandler:该接口定义了一个抽象方法:
/**
* @param proxy 代理类
* @param method 被代理的方法
* @param args 被代理方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
该抽象方法在代理类中动态实现。
(2)public class Proxy:该类为动态代理类,该类产生代理类的方法为:
/**
* @param loader 目标对象的类加载器
* @param interfaces 目标对象的接口类型
* @param h 事件处理函数,实现对目标对象的操作。
*/
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
创建接口:
public interface Moveable {
void move();
}
目标对象:
public class Car implements Moveable {
@Override
public void move() {
//实现开车
try {
System.out.println("汽车行驶中......");
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
代理工厂类:
public class TimeProxyFactory {
//代理的目标对象
private Object target;
public TimeProxyFactory(Object target) {
this.target = target;
}
//获取目标对象
public Object getInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶......");
Object obj = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println("汽车结束行驶...... 汽车行驶时间:"+(endTime-startTime)+"毫秒!");
return obj;
}
});
}
}
测试:
public class ProxyTest {
public static void main(String[] args) {
Moveable move = new Car();
TimeProxyFactory factory = new TimeProxyFactory(move);
Moveable proxy =(Moveable) factory.getInstance();
proxy.move();
}
}
测试结果
汽车开始行驶......
汽车行驶中......
汽车结束行驶...... 汽车行驶时间:123毫秒!
2.3 CGLIB动态代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
2.3.1 特点:
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
- 因为它是使用继承方式,所以不能对final修饰的类进行代理。
- 产生的代理类其实就是目标类的子类。
2.3.2 实现:
目标对象:
public class Train {
public void move() {
System.out.println("火车行驶中......");
}
}
代理类:
public class CGLibProxy implements MethodInterceptor {
//创建代理对象
public Object getProxyInstance(Class c) {
//Enhancer可以为接口或者非接口类型创建一个子类作为代理类,可以拦截所有方法
Enhancer enhancer = new Enhancer();
//设置创建子类的类,也就是说为每个代理类创建子类
enhancer.setSuperclass(c);
//设置回掉函数(因为MethodInterceptor继承了Callback类,默认执行intercept方法)
enhancer.setCallback(this);
//创建子类实例,返回去
return enhancer.create();
}
/**
* 拦截所有目标类方法的调用
* @param o 目标类的实例
* @param method 目标方法的反射对象
* @param objects 目标方法的参数
* @param proxy 代理类的实例
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶......");
//代理类调用父类的方法,来实现代理类的方法
Object returnObject = proxy.invokeSuper(o, objects);
long endTime = System.currentTimeMillis();
System.out.println("汽车结束行驶...... 汽车行驶时间:"+(endTime-startTime)+"毫秒!");
return returnObject;
}
}
测试:
public class ProxyTest {
public static void main(String[] args) {
Train train = (Train) new CGLibProxy().getProxyInstance(Train.class);
train.move();
}
}
测试结果
汽车开始行驶......
火车行驶中......
汽车结束行驶...... 汽车行驶时间:297毫秒!
三、小结
代理方式 | 特点 | 缺点 |
---|---|---|
静态代理 | 需要定义父类或者接口,代理对象和被代理对象需要同时继承父类或者实现该接口,一次代理一个类 | 随着代理类增多,出现大量重复代码,难维护,造成类膨胀 |
jdk动态代理 | 目标类需要实现至少一个接口,代理对象通过JAVA的API动态生成,可以代理一个借口的多个实现 | 只能够代理实现了接口的目标类 |
cglib动态代理 | 代理类要实现MethodInterceptor接口,通过Enhancer创建目标类的子类为代理对象,所有也是通过继承关系创建代理类的,然后通过实现intercept(Object o, Method method, Object[] objects, MethodProxy proxy)方法对所有的方法进行拦截,添加增强处理,注意该方法中要通过代理类的invokeSuper调用父类的方法 | 不能代理final修饰的类 |