什么是代理模式
代理模式:简单理解就是主要对我们方法执行之前与之后实现增强
代理模式应用场景
- 日志的采集
- 权限控制
- 实现aop
- Mybatis mapper
- Spring的事务
- 全局捕获异常
- Rpc远程调用接口
- 代理数据源
代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色: 真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
代理模式创建方式
静态代理
静态代理需要自己人工编写代理类代码
基于接口实现方式如下:
public class OrderServiceProxy implements OrderService{
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
public String addOrder(String userName, String userPwd) {
System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
String result = orderService.addOrder(userName, userPwd);
System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
return result;
}
}
public interface OrderService {
/**
* 需要被代理的方法
* @return
*/
String addOrder(String userName,String userPwd);
}
public class Test001 {
public static void main(String[] args) {
OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
orderService.addOrder("静态代理测试","123456");
}
}
基于继承的实现方式
public class OrderServiceProxy extends OrderServiceImpl {
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
public String addOrder(String userName, String userPwd) {
System.out.println("使用静态代理类打印日志开始:userName:" + userName + "," + userPwd);
String result = super.addOrder(userName, userPwd);
System.out.println("使用静态代理类打印日志结束:userName:" + userName + "," + userPwd);
return result;
}
}
动态代理与静态代理的区别
动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。
动态代理
- 动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
- 动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。
Jdk动态代理
JDK动态代理的一般步骤如下:
- 创建被代理的接口和类;
- 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
- 调用Proxy的静态方法,创建代理类并生成相应的代理对象;
实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对我们的目标方法增强。
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy 使用jdk程序生成的代理类
* @param method 目标方法
* @param args 方法需要传递的参数
* @return
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("使用Jdk动态代理打印日志开始" + args[0]);
Object result = method.invoke(target, args);
System.out.println("使用Jdk动态代理打印日志结束" + args[1]);
return result;
}
/**
* 生成代理类
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 测试代理类
*
* @param <T>
* @return
*/
public static void main(String[] args) {
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderServiceImpl orderService = jdkInvocationHandler.getProxy();
orderService.addOrder("动态代理", "测试动态代理");
}
}
public interface OrderService {
void addOrder(String arg0, String arg1);
}
public class OrderServiceImpl implements OrderService {
public void addOrder(String arg0, String arg1) {
System.out.println(arg0+"-----"+arg1);
}
}
运行结果
Connected to the target VM, address: '127.0.0.1:54763', transport: 'socket'
使用Jdk动态代理打印日志开始动态代理测试
动态代理测试-----测试动态代理
使用Jdk动态代理打印日志结束测试动态代理
Disconnected from the target VM, address: '127.0.0.1:54763', transport: 'socket'
注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
CGLIB动态代理
利用asm字节码技术,生成子类实现对目标方法实现增强
CGLIB动态代理实现方式
Maven依赖
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
</dependencies>
实现一个业务类,注意,这个业务类并没有实现任何接口:
package com.jpeony.spring.proxy.cglib;
public class HelloService {
public HelloService() {
System.out.println("HelloService构造");
}
/**
* 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
自定义MethodInterceptor
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定义MethodInterceptor
*/
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======代理开始======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======代理结束======");
return object;
}
}
调用代理类测试代码
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\myWork");
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(HelloService.class);
// 设置enhancer的回调对象
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理对象
HelloService proxy= (HelloService)enhancer.create();
// 通过代理对象调用目标方法
proxy.sayHello();
}
Jdk与Cglib动态代理的区别
- Jdk动态代理利用反射技术生成匿名的代理类走InvokeHandler回调方法实现增强,同时也是一种基于接口的方式实现代理。
- Cglib动态代理利用asm字节码技术生成一个子类覆盖其中的方法实现增强,同时采用fastClass机制对整个代理类建立索引比反射效率要高
- 在Spring中如果需要被代理的对象如果实现了接口采用Jdk动态代理,没有实现接口则使用Cglib动态代理。