代理就是有一些工作你不屑于自己做的,或者不方便自己做的,这个时候你就请别人帮你做,拿我最喜欢的偶像坤坤做例子,他开演唱会肯定不会自己收门票,不会自己清场打扫卫生,所以就需要代理来代替他完成一整场演唱会的工作。
静态代理,这种方式最好理解,但是也是最差的一种代理方式,属于硬编码的一种,写死在代码中,编译期间就产生了的代理关系。
先定义一个坤坤的接口。
public interface CXK {
public void show();
}
再定义一个接口的实现类,这个实现类通过添加方法的方式来达到接口功能增强的目的。
public class CXK_Agent implements CXK {
public void getMoney(){
System.out.println("收钱才开始表演......");
show();
over();
}
@Override
public void show() {
System.out.println("唱,跳,rap,篮球");
}
public void over(){
System.out.println("打扫清场卫生");
}
}
调用的时候肯定是通过创建一个坤坤团队的代理类来开演唱会,这样团队才能完成一整套工作。
静态代理的演唱会圆满结束!
忽然有一天,坤坤没有养一群固定团队了,他在要开演唱会的时候才临时找外包团队。这就是动态代理。
动态代理不单只负责维护坤坤的演唱会,还会维护TFBoy的演唱会,周杰伦的演唱会等等。所以就不能硬编码。
JDK动态代理
JDK动态代理就是针对接口的增强,针对接口的代理,所以还是先创建最爱的坤坤接口。
public interface CXK {
public void show();
}
然后创建一个实现类,实现坤坤的唱跳Rap表演节目。
public class CXK_MAN implements CXK{
@Override
public void show() {
System.out.println("唱,跳,rap,篮球");
}
}
最后我们再给坤坤包装团队。也就是动态增强的代理方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CXK_proxy implements InvocationHandler {
private Object man;
public CXK_proxy(Object man){
this.man = man;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收钱才开始表演......");
Object mans = method.invoke(man,args);
System.out.println("打扫清场卫生");
return mans;
}
}
可以看出是给一个有参构造方法,预留好演员的位置,方便把坤坤融入该外包团队,然后实现InvocationHandler接口,重写里面的invoke方法,invoke方法就是描述团队一整场演唱会下来都做了什么的方法。也就是增强方法。
最后就是到演出的时候,先创建一个坤坤的实例,把坤坤放进增强类的有参构造方法里,然后使用Proxy.newProxyInstance方法,把坤坤类的类加载器和坤坤实现的接口和坤坤的增强型团队都传进去。
public class test {
public static void main(String[] args) {
CXK_MAN man = new CXK_MAN();
CXK_proxy handlers = new CXK_proxy(man);
CXK cxk_man = (CXK) Proxy.newProxyInstance(man.getClass().getClassLoader(), man.getClass().getInterfaces(),handlers);
cxk_man.show();
}
}
记得返回那个必须强转型,类型必须是坤坤的接口,反正就记住一句话,JDK代理就是围绕着接口的代理。
执行一下:
演唱会圆满结束。
坤坤开多少场演唱会,团队就跟他合作多少次。
CGLIB的动态代理
这也是spring框架已经集成的动态代理,AOP的底层其实就有它,如果是你自己单独的Java代码的话需要加个jar包依赖到maven的pom文件。它底层其实是通过拦截器实现的增强。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
由于GCLIB是针对实现类的方法的增强型方法代理,所以我们可以不提供接口,它这个动态代理方法不要求接口。
老规矩,坤坤先表演拿手才艺
public class CXK_MAN {
public void show() {
System.out.println("唱,跳,rap,篮球");
}
}
还是要给一个代理类的
public class CXK_proxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("收钱才开始表演......");
Object cxk = methodProxy.invokeSuper(o,objects);
System.out.println("打扫清场卫生");
return cxk;
}
}
然后是测试类
public class test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CXK_MAN.class);
enhancer.setCallback(new CXK_proxy());
CXK_MAN man = (CXK_MAN) enhancer.create();
man.show();
}
}
执行一康康
可以看到演唱会顺利结束。
所以总结:
(1)JDK是针对接口做的代理。
(2)CGLIB是针对实例对象的方法做的代理。
(3)Spring中的AOP底层是CGLIB动态代理。
(4)静态代理在实际项目中还可能用得上,动态代理不是搞科研不是搞框架的话,业务代码是真的很少接触。
追根溯源:为什么JDK动态代理要基于接口?
因为在JDK自带提供的动态代理中,创建一个代理对象,需要使用Proxy类里面的newProxyInstance方法,所以我们可以看到在动态生成的$Proxy.class之类的文件,都是extends 了 Proxy类,然后因为Java是单继承,要实现多继承的话只能通过接口(不然你就光一个代理吗?目标对象怎么办?)所以只能再implement实现接口。
这就是为什么JDK动态代理要基于接口。