静态代理与JDK动态代理

时间:2021-01-17 04:24:59

demo地址:

  https://github.com/ZbLeaning/leaning

代理:

   为其他对象提供一种代理以控制对这个对象的访问。分为静态代理和动态代理。代理模式的目的就是为真实业务对象提供一个代理对象以控制对真实业务对象的访问

静态代理:

   程序运行前就已存在的编译好的代理类

动态代理:

  程序运行期间根据需要动态创建代理类及其实例已完成功能

代理对象的作用:

  1、拦截对真实业务对象的访问

  2、代理对象和真实业务对象(目标对象)实现共同的接口或继承于同一个类

  3、代理对象是对目标对象的增强,可对消息进行预处理和后处理

代理的结构:

  角色: 代理类角色(Proxy):含义对真实对象的引用,负责对真实主题角色的调用,并进行预处理和后处理

  委托类角色(被代理类,Proxied):真实主题角色,业务

  抽象主题角色(Subject):接口或抽象类

静态代理:

  程序运行前,开发者创建或特定工具自动生成源代码并对其编译生成.class文件。

  实现步骤:

    1、定义业务接口

    2、实现业务接口

    3、定义代理类并实现业务接口,最后通过客户端调用

demo:实现在代理方法前后进行预处理和后处理

CodingService:抽象主题角色,构造接口

public interface CodingService {
String debug(String name);
String unitTest(String msg);
}
CodingServiceImpl:委托类角色

public class CodingServiceImpl implements CodingService{
@Override
public String debug(String name) {
return name + "发现了bug";
} @Override
public String unitTest(String msg) {
return "unitTest Result:" + msg;
}
}
CodingServiceProxy:代理类角色

public class CodingServiceProxy implements CodingService{
private CodingService codingService; public CodingServiceProxy(CodingService codingService){
this.codingService = codingService;
} @Override
public String debug(String name) {
System.out.println("预处理...");
String result = codingService.debug(name);
System.out.println(result);
System.out.println("后处理...");
return result;
} @Override
public String unitTest(String msg) {
System.out.println("预处理...");
String result = codingService.unitTest(msg);
System.out.println(result);
System.out.println("后处理...");
return result;
}
}
CodingClient:客户端

public class CodingClient {
public static void main(String[] args) {
CodingService codingService = new CodingServiceImpl();
CodingServiceProxy codingServiceProxy = new CodingServiceProxy(codingService);
codingServiceProxy.debug("西瓜");
codingServiceProxy.unitTest("好吃呀");
}
}

  代理类可在不修改原有代码前提下新增功能。开闭原则的典型实践。aop实现原理就是代理模式

JDK动态代理:

  静态代理的特点是委托类与其代理类一一对应。当有N个委托类时,代理类中的预处理和后处理可能是相同的,只是调用主题不同。用静态代理的话就要创建N个代理类。动态代理可以简单地为各个委托类分别生成代理类,共享预处理和后处理功能,大大减小程序规模。

  在动态代理中,代理类是在运行时生成的。动态代理主要分为JDK动态代理和CgLIB动态代理 JDK动态代理相关类/接口:

  java.lang.reflect.proxy:

    该类用于动态生成代理类, 只需传入目标接口,目标接口的类加载器及invocationHandler便可为目标接口生成代理类及代理对象

// 方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:该方法用于判断指定类是否是一个动态代理类
static boolean isProxyClass(Class cl) // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
InvocationHandler h)

java.lang.reflect.InvocationHandler:

  该接口包含一个invoke方法,通过该方法实现对委托类的代理的访问,是代理类完整逻辑的集中体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑。

// 该方法代理类完整逻辑的集中体现。第一个参数既是代理类实例,第二个参数是被调用的方法对象,
// 第三个方法是调用参数。通常通过反射完成对具体角色业务逻辑的调用,并对其进行增强。
Object invoke(Object proxy, Method method, Object[] args)

java.lang.ClassLoader:

  类加载器类,负责将类的字节码装载到Java虚拟机中并为其定义类对象,然后该类才能被使用。Proxy静态方法生成动态代理类同样需要通过类加载器来进行加载才能使用,它与普通类的唯一区别就是其字节码是由JVM在运行时动态生成的而非预存在于任何一个.class 文件中。

JDK动态代理实现步骤:

   1、创建被代理的接口和类

public interface HelloService {
String hello(String name);
String hi(String msg);
}
public class HelloServiceImple implements HelloService{
@Override
public String hello(String name) {
return "Hello " + name;
} @Override
public String hi(String msg) {
return "Hi, " + msg;
}
}

  2、实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理

public class HelloInvocationHandler implements InvocationHandler {
//委托类对象
private Object target; public HelloInvocationHandler(Object target){
this.target = target;
}
//增强方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("PROXY : " + proxy.getClass().getName());
// 反射调用,目标方法
Object result = method.invoke(target, args);
// 增强逻辑
System.out.println(method.getName() + " : " + result);
return result;
}
}

  3、调用Proxy的静态方法,创建代理类并生成相应的代理对象

public class HelloClient {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImple();
//创建代理类并生成相应代理对象
HelloService proxy = (HelloService)Proxy.newProxyInstance(HelloService.class.getClassLoader(),
helloService.getClass().getInterfaces(), new HelloInvocationHandler(helloService));
proxy.hello("rico");
proxy.hi("panda");
}
}

小结:

  1、动态代理的实现关键是反射

  2、代理对象是对目标对象的增强,以便对消息进行预处理和后处理

  3、InvocationHandler中的invoke()方法是代理类完成逻辑的体现,包括要切入的增强逻辑和进行反射执行的真实业务逻辑

  4、使用JDK动态代理,只需要指定目标接口、目标接口的类加载器及具体的InvocationHandler。

  5、JDK动态代理生成的代理类继承了Proxy类,因此JDK动态代理只能实现接口代理而不能实现类代理,Java不允许多继承,其本身生成的代理类已经继承了Proxy类

  6、JDK动态代理生成的代理类也代理了三个Object类的方法:equals()、hashCode()、toString()