代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:比如说我们要吃午餐,在没有点餐平台前,只能去饭店吃;后来有了美团外卖,我们还是吃同一家饭店的午餐,却不用直接去饭店才能吃午餐了,直接在美团上点餐,就可以吃到香喷喷的午餐了,美团就相当于代理了饭店,提供送餐功能。
我们javaWEB开发常用的spring、mybatis等框架底层都是用了动态代理,接下来将用三个demo来分析三种代理模式的优缺点。
一、静态代理。
接口:hello.java
public interface Hello {
void sayHello(String string);
void sayHi(String string);
}
实现类:ImpHello
public class ImpHello implements Hello{
@Override
public void sayHello(String string) {
System.out.println("我说:"+string);
}
@Override
public void sayHi(String string){
System.out.println("sayHi:"+string);
}
}
我们需要在执行实现类sayHello前打印一下say的内容,就有下下代理对象:
public class StaticProxiedHello implements Hello{
private ImpHello impHello=new ImpHello();
@Override
public void sayHello(String string) {
System.out.println("你说:"+string);
impHello.sayHello(string);
}
@Override
public void sayHi(String string) {
System.out.println(string);
}
@Test
public void testProxi() {
//======================静态代理
StaticProxiedHello staticProxiedHello=new StaticProxiedHello();
staticProxiedHello.sayHello("很爱很爱你");
}
控制台打印:
你说:很爱很爱你
我说:很爱很爱你
可见静态代理
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
二、JDK接口动态代理
代理对象代码如下,JDK动态代理对象必须实现InvocationHandler接口
public class ProxyHello implements InvocationHandler {
private Hello hello;
public ProxyHello(Hello hello){
this.hello=hello;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理方法=========================");
if("sayHello".equals(method.getName())){
System.out.println("你说:"+Arrays.toString(args));
}
if("sayHi".equals(method.getName())){
System.out.println("你说:"+Arrays.toString(args));
}
// System.out.println("你说:"+Arrays.toString(args));
return method.invoke(hello,args);
}
}
2.测试JDK代理代码如下
@Test
public void testJDKProxie() {
// 0. 在需要使用Hello的时候,通过JDK动态代理获取Hello的代理对象。
Hello hello = (Hello)Proxy.newProxyInstance(
getClass().getClassLoader(), // 1. 类加载器
new Class<?>[] {Hello.class}, // 2. 代理需要实现的接口,可以有多个
new ProxyHello(new ImpHello()));// 3. 方法调用的实际处理者
hello.sayHello("你好啊");
System.out.println(hello.getClass().getName());
执行结果:
你说:[你好啊]
我说:你好啊
com.sun.proxy.$Proxy4
注意打印JDK动态代理生成的类名为$Proxy4
可见JDK动态代理:
1.代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理;
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
三、cglib动态代理
1.cglib代理对象
public class CglibInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB动态代理"+ Arrays.toString(objects));
return methodProxy.invokeSuper(o,objects);
}
}
2.测试类
@Test
public void testCGLIB() {
//================cglib动态代理
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(ImpHello.class);
enhancer.setCallback(new CglibInterceptor());
Hello hello=(Hello) enhancer.create();
hello1.sayHello("I love you!!!!!!");
System.out.println(hello.getClass().getName());
}
打印结果
CGLIB动态代理[I love you!!!!!!]
我说:I love you!!!!!!
yuan.base.yuanjpa.ImpHello$$EnhancerByCGLIB$$857ffca1
可见CGLIB代理:
1.对比其他代理不需要实现接口。
spring aop就用到了JDK、和cglib代理。