Java常见23中设计模式之【代理模式】

时间:2023-03-09 17:51:17
Java常见23中设计模式之【代理模式】

一、静态代理模式
静态代理,使用继承的方式实现自己新增的服务
这种模式可以实现帮助被代理者完成一些前期的准备工作和后期的善后工作,但是核心的业务逻辑仍然是由被代理者完成。
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个代理的第三者间接引用,。起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或额外服务。

代理是一个对象,代理对象为其他对象提供一种代理,以控制对这个对象的访问,代理对象起到中介作用,可以去掉或者增加额外的服务。
如:火车票代售点就是火车站售票处的一个代理对象,可通过访问代售点进行业务处理。
二,静态代理的2种实现方式:继承和聚合
静态代理中的代理和被代理对象在代理之前关系是确定的。它们都实现了相同的接口或者继承相同的抽象类。
下面的例子分别讲述了所有的类实现同一个接口,类对象之间是如何代理的。

案例代码实现:
1.定义一个接口类Moveable.java
package com.sjms.test.proxy.interfaces;

public interface Moveable {
void move();
}

2.定义一个实体类Car.java 并实现以上接口。
package com.sjms.test.proxy.impl;

import com.sjms.test.proxy.interfaces.Moveable;

public class Car implements Moveable {

@Override
public void move() {
try {
System.out.println("开始喽");
Thread.sleep(1000L);

} catch (Exception e) {
e.printStackTrace();
}
}

}

3.定义一个代理类Car2.java(此类,即实现了父类方法,又实现了新增的业务)
package com.sjms.test.proxy.impl;

public class Car2 extends Car {
public void move() {//实现自身方法
Long startTime = System.currentTimeMillis();
System.out.println("开始开车。。。。");
super.move();//实现父类方法
Long endTime = System.currentTimeMillis();
System.out.println("已经停止。。。。,用时:"+(endTime-startTime));
}
}
4.Test.java类
package com.sjms.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.sjms.test.proxy.handler.TimerHandler;
import com.sjms.test.proxy.impl.Car;
import com.sjms.test.proxy.impl.Car2;
import com.sjms.test.proxy.impl.Car3;
import com.sjms.test.proxy.interfaces.Moveable;

4.通过聚合构造函数方式 (通过将参数传递进来,然后我们使用传递进来的参数,调用其方法,实现静态代理)
public class Car3 implements Moveable{

private Car car;
public Car3(Car car) {
super();
this.car = car;
}

@Override
public void move() {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶.......");
     //通过构造方法将car传进来,然后通过car调用方法
car.move();
long endtime = System.currentTimeMillis();
System.out.println("汽车结束行驶......");
System.out.println("汽车行驶时间:"+(endtime - starttime)+"毫秒!");
}
}

public class Test {
public static void main(String[] args) {
//1.静态代理,使用继承的方式实现自己新增的服务
Moveable moveable = new Car2();
moveable.move();

//2.聚合静态代理,使用构造方法传入参数。
/*Car car = new Car();
Moveable car3 = new Car3(car);
car3.move();*/

}
}

二、使用静态代理,会让我们的代理类无限膨胀下去,所以出现了动态代理模式(JDK动态代理、CGLIB动态代理)
在代理类和被代理类之间加上一个事务处理器,将我们需要处理的具体功能放在其中进行处理。

1.JDK动态代理(InvocationHandler事务处理器,Proxy:代理类)
InvocationHandler接口中仅定义了一个方法public object invoke(Object obj,Method method,Object[] args)
第一个参数:代理类,第二个参数:被代理的方法,第三个参数:该方法的参数数组
Proxy:动态代理类,通过newProxyInstance方法返回代理类的实例,

实现步骤:
1).创建一个实现InvocationHandler接口的实现类,并实现invoke方法
2).创建被代理的类和接口(Car和Moveable 在上面的案例中已经构建)
3).动态创建一个代理类(Proxy.newProxyInstance)package com.sjms.test.proxy.handler;
TimerHandler.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimerHandler implements InvocationHandler{
private Object target;
public TimerHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] param)
throws Throwable {
Long startTime = System.currentTimeMillis();
System.out.println("汽车开始行使了...");
method.invoke(target);
Long entTime = System.currentTimeMillis();
System.out.println("汽车结束行驶......");
System.out.println("汽车行驶时间:"+(entTime - startTime)+"毫秒!");
return null;
}

}

Test.java 构造代理类,实现具体的代理类

package com.sjms.test.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.sjms.test.proxy.handler.TimerHandler;
import com.sjms.test.proxy.impl.Car;
import com.sjms.test.proxy.impl.Car2;
import com.sjms.test.proxy.impl.Car3;
import com.sjms.test.proxy.interfaces.Moveable;

public class Test {
public static void main(String[] args) {
//1.静态代理,使用继承的方式实现自己新增的服务
Moveable moveable = new Car2();
moveable.move();

//2.聚合静态代理,使用构造方法传入参数。
/*Car car = new Car();
Moveable car3 = new Car3(car);
car3.move();*/

//3.动态代理
Car carObj = new Car();//被代理的类
InvocationHandler handler = new TimerHandler(carObj);//事务处理器
//使用代理器实现
Moveable move = (Moveable)Proxy.newProxyInstance(carObj.getClass().getClassLoader(), carObj.getClass().getInterfaces(), handler);
//move 为生产的代理类
move.move();
}
}

2.cgLib的动态代理实现
由于JDK只能针对实现了接口的类做动态代理,而不能对没有实现接口的类做动态代理,所以cgLib横空出世!
CGlib是一个强大、高性能的Code生成类库,它可以在程序运行期间动态扩展类或接口,它的底层是使用java字节码操作框架ASM实现。它可以动态的操作类部方法

1) 引入cgLib 库
2)定义业务类,被代理的类没有实现任何接口(有实现接口的也可以)
package com.sjms.test.proxy.impl;

public class Car4 {
public void move() {
System.out.println("开始发车");
}
}
3)定义拦截器,在调用目标方法时,CGlib会回调MethodInterceptor 接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。
package com.sjms.test.proxy.handler;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* cglib 动态代理拦截器实现
* */
public class CglibDynProxyInterceptor implements MethodInterceptor{

@Override
public Object intercept(Object arg0, Method arg1, Object[] params,
MethodProxy methodProxy) throws Throwable {
if (arg1.getName().equals("move")){
System.out.println("cgLib动态代理拦截器开始,拦截方法mmove()......");
//代理类调用父类的方法
return methodProxy.invokeSuper(arg0, params);
}
return null;
}

}
4)定义动态代理工厂,生成动态代理
package com.sjms.test.proxy.factory;

import com.sjms.test.proxy.handler.CglibDynProxyInterceptor;

import net.sf.cglib.proxy.Enhancer;
//生产代理工厂

public class CglibProxyFactory {
public static Object getCarProxy(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibDynProxyInterceptor());
return enhancer.create();
}
}
5)测试
package com.sjms.test.proxy;

import com.sjms.test.proxy.factory.CglibProxyFactory;
import com.sjms.test.proxy.impl.Car4;

public class Car4CglibTest {
public static void main(String[] args){
//Cglib动态代理原理,就是通过拦截器来新增自己的增值业务
Car4 car = (Car4)CglibProxyFactory.getCarProxy(new Car4());
car.move();
}
}

ps:切面编码实际底层原理使用动态代理模式
cgLib的动态代理原理
CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理。