Java中代理模式的一点理解
1、什么是代理模式?
代理模式中可主要分为客户端、代理类、目标类这三种角色。
- 客户端:发起请求方。
- 目标类:特定行为的实现类,也就是真正工作的人
- 代理类:可以调用目标类的所有功能,并可以在此基础上扩展额外的工作。通过在代理类内部持有目标类的对象来实现代理功能。
以租房为例:
- 客户端:租客
- 目标类:房东
- 代理类:中介。中介不仅代理出租目标房东的房子,还会处理其他工作。
2、代理模式有哪些?
代理模式有分为两类。
- 静态代理:顾名思义,关键字静态,代理关系一目了然。在编译之前就了然的代理关系。
- 动态代理:既然了静态代理,为什么还需要动态代理呢?当有多个代理关系时,代码就会凸显代码的冗余。因此使用动态代理,一不变应万变,没错,就是一,只需要一个动态代理类,就可以实现多了个代理关系。
- jdk动态代理:基于接口实现,在创建代理对象时,需要指定生成代理类实现的接口。
- cglib动态代理:基于继承实现,即使目标类没有实现接口也可以正常生成代理对象。
3、代理模式的作用?
代理的作用就是不改变目标类的情况下,对目标类进行增强。细品后发现和Spring的Aop有些相似,是的,Spring利用了动态代理实现了Aop的强大功能。
4、实操体会一下
下面通过三个简单的代理实现代码,进一步理解代理模式在Java中的基本实现。
4.1、静态代理
静态代理直接将目标对象targeti通过构造方法传递进去,构建出来了代理对象staticProxy。
如下代码:
/**
* 目标类接口
*/
public interface HelloService {
public String sayHello(String msg);
}
/**
* 目标类实现类
*/
public class HelloServiceImpl implements HelloService{
public String sayHello(String msg){
System.out.println("你好呀!!");
return "HelloServiceImpl: " + msg;
}
}
/**
* 代理类
*/
public class StaticProxy implements HelloService {
/**
* 目标对象
*/
private HelloService target;
public StaticProxy(HelloService target){
this.target = target;
}
@Override
public String sayHello(String msg){
// 1、增强逻辑,记录入参
System.out.println("方法名称: sayHello, 入参: " + msg);
// 2、使用放射调用目标对象方法
String result = target.sayHello(msg);
// 3、增强逻辑,记录出参
System.out.println("方法名称: sayHello, 返回值: " + result);
return result;
}
}
/**
* 调用代理类实现目标类的功能
*/
public class StaticTest{
public static void main(String args[]){
HelloService helloService = new HelloServiceImpl();
// 1、静态代理
HelloService staticProxy = new StaticProxy(helloService);
String result = staticProxy.sayHello("静态代理");
System.out.println("MainTest==静态代理: " + result);
}
}
/**
* 方法名称: sayHello, 入参: 静态代理
你好呀!!
方法名称: sayHello, 返回值: HelloServiceImpl: 静态代理
MainTest==静态代理: HelloServiceImpl: 静态代理
*/
4.2、jdk动态代理
jdk动态代理是先将目标对象targeti通过构造方法传递进去,然后通过getProxy()方法完成了代理的创建,最后将代理对象强转为了接口类型HelloService,由于jdk动态代理是基于接口实现的,生成的代理类会实现这个HelloService接口,所以是可以这样强转的。
如下代码:
/**
* jdk动态代理
*/
public class JdkDynamicProxy implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public JdkDynamicProxy(Object target){
this.target = target;
}
/**
* 获取目标对象的代理
* @return 返回代理对象
*/
public Object getProxy(){
// 创建代理对象
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 指定当前目标对象使用类加载器
target.getClass().getInterfaces(), // 目标对象实现的接口和类型
this // 设置回调顺序
);
}
/**
* 对目标对象的方法进行增强
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1、增强逻辑,记录入参
System.out.println("方法名称: " + method.getName() + ", 入参: " + Arrays.toString(args));
// 2、使用放射调用目标对象方法
Object result = method.invoke(target, args);
// 3、增强逻辑,记录出参
System.out.println("方法名称: " + method.getName() + ", 返回值: " + result);
return result;
}
}
/**
* 调用代理类实现目标类的功能
*/
public class JdkTest{
public static void main(String args[]){
HelloService helloService = new HelloServiceImpl();
HelloService jdkProxy = (HelloService) new JdkDynamicProxy(helloService).getProxy();
String result = jdkProxy.sayHello("jdk动态代理");
System.out.println("MainTest==jdk动态代理: " + result);
System.out.println("====================================");
}
}
/*
方法名称: sayHello, 入参: [jdk动态代理]
你好呀!!
方法名称: sayHello, 返回值: HelloServiceImpl: jdk动态代理
MainTest==jdk动态代理: HelloServiceImpl: jdk动态代理
*/
4.3、cglib动态代理
cglib动态代理是先将目标对象targeti通过构造方法传递进去,然后通过getProxy0方法完成了代理的创建,只不过这里是将代理对象强转为了HelloServicelmpl类型,因为cglib:是基于继承来的,生成的代理类本质是HelloServicelmpl类的子类,所以这里是可以强转为HelloServicelmpl类型的。
如下代码:
/**
* Cglib动态代理
*/
public class CglibDynamicProxy implements MethodInterceptor {
/**
* 目标对象
*/
private Object target;
public CglibDynamicProxy(Object target){
this.target = target;
}
/**
* 获取目标对象的代理
* @return 返回代理对象
*/
public Object getProxy(){
// 字节码增强器, 可以为没有实现接口的类创建代理
Enhancer enhancer = new Enhancer();
// 因为Cglib的原理是动态生成要代理类的子类,然后子类重写父类方法,因此设置代理类的父类类型
enhancer.setSuperclass(target.getClass());
// 设置回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
/**
* 对目标对象的方法进行增强
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 1、增强逻辑,记录入参
System.out.println("方法名称: " + method.getName() + ", 入参: " + Arrays.toString(args));
// 2、使用放射调用目标对象方法
Object result = methodProxy.invoke(target, args);
// 3、增强逻辑,记录出参
System.out.println("方法名称: " + method.getName() + ", 返回值: " + result);
return result;
}
}
/**
* 调用代理类实现目标类的功能
*/
public class JdkTest{
public static void main(String args[]){
HelloServiceImpl cglibProxy = (HelloServiceImpl) new CglibDynamicProxy(new HelloServiceImpl()).getProxy();
String result = cglibProxy.sayHello("cglib动态代理");
System.out.println("MainTest==cglib动态代理: " + result);
System.out.println("====================================");
}
}
/*
方法名称: sayHello, 入参: [cglib动态代理]
你好呀!!
方法名称: sayHello, 返回值: HelloServiceImpl: cglib动态代理
MainTest==cglib动态代理: HelloServiceImpl: cglib动态代理
*/
5、总结
1、代理模式分为两大类,分别是静态代理和动态代理,而动态代理又分为jdk动态代理和cglib动态代理。
2、单一实现可以使用静态代理,多个目标类需要被代理则需要使用动态代理模式,实现接口使用jdk动态代理,否则使用cglib动态代理。
3、动态代理是Spring中AOP实现的基础。