类似AG网站建设东莞优化排名公司
文章目录
- 1.代理模式
- 2. 静态代理
- 3. 动态代理
- 3.1 JDK动态代理
- 3.2 CGLIB动态代理
1.代理模式
代理模式主要有两种:
静态代理模式
动态代理模式
2. 静态代理
有这样一个业务:订单的生成,修改,查看详情。实现如下
package com.sdnu.proxy.service;/*** 订单业务接口*/
public interface OrderService {void generate();void modify();void detail();
}
package com.sdnu.proxy.service;public class OrderServiceImpl implements OrderService{@Overridepublic void generate() {//模拟生成订单的耗时try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生产");}@Overridepublic void modify() {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}@Overridepublic void detail() {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("查看订单详情");}
}
测试
package com.sdnu.proxy.client;import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;public class Test {public static void main(String[] args) {OrderService orderService = new OrderServiceImpl();orderService.generate();orderService.modify();orderService.detail();}
}
过了一段时间,有一个新的业务,我们需要统计订单的生成总时间,于是我们有如下的解决方案:
方案一:采用硬编码的方式,在每一个业务接口中的每一个业务方法直接写一个统计时间。
这种方案的缺点:(1)违背OCP原则
(2)代码没有得到复用
方案二:编写业务的子类,让子类继承原来的业务类,对每个业务方法进行重写。
这种方案的缺点:(1)虽然没有违背OCP原则,但是由于采用继承方式会导致代码的耦合度变高。
(2)代码没有得到复用
方案三:静态代理
代理对象
package com.sdnu.proxy.service;//代理对象
public class OrderServiceProxy implements OrderService{//将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系耦合度低private OrderService target;public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic void generate() {//代理方法long begin = System.currentTimeMillis();target.generate();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");}@Overridepublic void modify() {//代理方法long begin = System.currentTimeMillis();target.modify();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");}@Overridepublic void detail() {//代理方法long begin = System.currentTimeMillis();target.detail();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");}
}
测试
package com.sdnu.proxy.client;import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;
import com.sdnu.proxy.service.OrderServiceProxy;public class Test {public static void main(String[] args) {OrderService orderService = new OrderServiceImpl();OrderServiceProxy orderServiceProxy = new OrderServiceProxy(orderService);orderServiceProxy.generate();orderServiceProxy.modify();orderServiceProxy.detail();}
}
这种静态代理符合OCP开闭原则,同时采用的是关联关系,所以程序的耦合度较低。
缺点:类爆炸问题。
3. 动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
常见的动态代理:
- JDK动态代理
- CGLIB动态代理
- javassit动态代理
3.1 JDK动态代理
测试程序Test
package com.sdnu.proxy.client;import com.sdnu.proxy.service.OrderService;
import com.sdnu.proxy.service.OrderServiceImpl;
import com.sdnu.proxy.service.TimerInvocationHandler;import java.lang.reflect.Proxy;public class Client {public static void main(String[] args) {//创建目标对象OrderService target = new OrderServiceImpl();//创建代理对象OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TimerInvocationHandler(target));//调用代理对象的代理方法orderServiceProxy.generate();orderServiceProxy.modify();orderServiceProxy.detail();}
}
代码OrderService orderServiceProxy = Proxy.newProxyInstance(类加载器, 接口类型, 调用处理器);
做了两件事
● 第一件事:在内存中生成了代理类的字节码
● 第二件事:创建代理对象
参数:
类加载器:在内存中生成了字节码,要想执行这个字节码,也是需要先把这个字节码加载到内存当中的。所以要指定使用哪个类加载器加载。
接口类型:代理类和目标类实现相同的接口,所以要通过这个参数告诉JDK动态代理生成的类要实现哪些接口。
调用的处理器:这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。
代理类
package com.sdnu.proxy.service;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class TimerInvocationHandler implements InvocationHandler {private Object target;public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//目标执行之前的增强long begin = System.currentTimeMillis();//反射机制调用目标对象上的目标方法Object retValue = method.invoke(target, args);//目标执行之后的增强long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");return retValue;}
}
invoke方法有三个参数
- Object proxy。代理对象。计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用
- Method method。目标方法。
- Object[] args。目标方法调用时要传的参数。
3.2 CGLIB动态代理
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
引入依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>