家喻户晓,Spring AOP 中波及到了动静代理模式,那么有动静代理相应的就会有动态代理。那么动静代理分为哪几种,绝对应的区别又是什么呢?
首先什么是代理?
找一个货色或者一个人去帮你做事,比方常说的中介就是一个代理,各大经销商的代理商等等。JAVA 中的代理即是指将本人的事件委派给他人帮忙去实现。
动态代理:代理的是程序员曾经创立好的类,也就是说以后仅有一个对象能被胜利代理。上代码看下
首先是一个须要代理的接口类
该类形容了两个办法,一个是 eat(),一个是 run();
public interface UserAction {void eat();
void run();}
接下来是该类的实现类,较为简单的实现形式,仅仅打印内容而已。
public class UserActionImpl implements UserAction {
@Override
public void eat() {System.out.println("吃饭");
}
@Override
public void run() {System.out.println("跑步");
}
}
接口曾经实现实现,剩下的即是代理对象了。上述的过程中动态代理和 JDK 的动静代理还没有区别。区别在于上面
public class UserActionStaticProxy implements UserAction{
private UserAction userAction;
public UserActionStaticProxy(UserAction userAction){this.userAction = userAction;}
@Override
public void eat() {System.out.println("动态代理 eat 办法开始");
userAction.eat();
System.out.println("动态代理 eat 办法完结");
}
@Override
public void run() {System.out.println("动态代理 run 办法开始");
userAction.run();
System.out.println("动态代理 run 办法完结");
}
}
从上述代码能够看到,咱们实现了接口类并定义了一个新的类 UserActionStaticProxy。而后定义了他的有参构造方法,将接口类传入即可。办法重写的同时退出了办法的监控。
public static void main(String[] args) {UserAction userAction = new UserActionStaticProxy(new UserActionImpl());
userAction.eat();}
在调用的时候,咱们能够看到传入了 UserActionImpl 类去转换为 UserAction 类(向下转型),而后能够间接调用他的办法即可。运行上述办法当前即执行了 Proxy 类下的 eat 办法。甚至于咱们能够在 Proxy 类下的 eat 办法调用一次 this.run() 办法,能够本人拼接为本人所想要的形式,有点和策略模式凑近了。(下图是在 eat 办法中退出了 this.run()办法)
从动态代理模式中能够看进去,如果咱们须要代理多个类的话,那么就须要新建对应的接口实现类 (Imp.class) 和对应的代理类(Proxy,class)。所以实现起来会比拟繁琐,因而就应运而生出了动静代理。
动静代理和动态代理的本质区别其实不是很大,都须要接口以及接口的实现类,动静代理解决了须要反复新增大量的单体代理文件,而是把所有的对象都在一个中央进行了代理,也就是波及到了 JAVA 中的反射机制。
能够看下动静代理的代码:
public class LogHandler implements InvocationHandler {
private Object object;
public Object newProxyInstance(Object object){
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {System.out.println("动静代理" + method.getName() + "开始");
Object ret = method.invoke(object,args);
System.out.println("动静代理" + method.getName() + "完结");
return ret;
}
}
能够看到,动静代理的时候,咱们将对象替换成了所有对象的父类 ——Object 类,在代理类的同时,咱们通过反射 Proxy.newProxyInstance 获取了该类的对象。而后应用了 java 对应的 invoke 办法去执行被代理类的办法。
对应的执行的主办法:
public static void main(String[] args) {UserAction userAction1 = (UserAction)new LogHandler().newProxyInstance(new UserActionImpl());
userAction1.eat();}
执行的后果:
因而能够看到,动态代理和动静代理的区在于代理类的区别,动态代理在代码扩容时,每减少一个接口类须要代理,那么就须要新增一个对应的代理类。而动静代理的益处在于须要新增代理接口时,不须要新增代理类,能够间接通过反射的形式调用被代理类。从上述就能够看出,代理的益处就是对办法的加强,能够在办法的前后进行一系列的操作,比方打印日志,验证权限,办法之后能够对立返回格局,对立异样捕捉等等 ……(其实也就是 AOP 能做的事件)。所以动静代理相比于动态代理最实质的区别就在于咱们须要对一个新的接口类代理时,不须要再去减少繁琐的代理类了。
前文提到,动静代理又分为 JDK 的动静代理以及 CGLIB 的动静代理。JDK 的动静代理是根据的 JAVA 弱小的反射机制。而 CGLIB 动静代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过批改其字节码生成子类来解决,也就是说生成被代理类的一个子类将其办法笼罩,以达到代理的目标。
AOP 是会主动切换这两种动静代理的类型的,具体的区别如下:
1、如果指标对象实现了接口,默认状况下会采纳 JDK 的动静代理实现 AOP
2、如果指标对象实现了接口,能够强制应用 CGLIB 实现 AOP
3、如果指标对象没有实现了接口,必须采纳 CGLIB 库,spring 会主动在 JDK 动静代理和 CGLIB 之间转换
这里借用他人的 CGLIB 代码来看下具体的区别
public class UserAction {public void eat(){System.out.println("CGLIB 动静代理吃饭");
}
}
public class CglibProxy implements MethodInterceptor {
private Object target;// 须要代理的指标对象
// 重写拦挡办法
@Override
public Object intercept(Object obj, Method method, Object[] arr, MethodProxy proxy) throws Throwable {System.out.println("Cglib 动静代理,监听开始!");
Object invoke = method.invoke(target, arr);// 办法执行,参数:target 指标对象 arr 参数数组
System.out.println("Cglib 动静代理,监听完结!");
return invoke;
}
// 定义获取代理对象办法
public Object getCglibProxy(Object objectTarget){
// 为指标对象 target 赋值
this.target = objectTarget;
Enhancer enhancer = new Enhancer();
// 设置父类, 因为 Cglib 是针对指定的类生成一个子类,所以须要指定父类
enhancer.setSuperclass(objectTarget.getClass());
enhancer.setCallback(this);// 设置回调
Object result = enhancer.create();// 创立并返回代理对象
return result;
}
public static void main(String[] args) {CglibProxy cglib = new CglibProxy();// 实例化 CglibProxy 对象
UserAction user = (UserAction) cglib.getCglibProxy(new UserAction());// 获取代理对象
user.eat();// 执行办法}
}
代码实质上其实和 JDK 的动静代理的区别并不是很大,而 JDK 的动静代理是基于接口的,必须要对应接口的实现类才能够实现 JDK 的动静代理,而 CGLIB 补救了这方面的毛病,CGLIB 是基于类的继承关系,因而在没有接口的实现下咱们能够应用 CGLIB 去实现动静代理。
举荐浏览
为什么阿里巴巴的程序员成长速度这么快?
纳尼?SpringCloud 要被淘汰了?
《飞马打算》到底是什么?能够让数万程序员为之着迷
一年半开发教训拿多少钱适合?
看完三件事
如果你感觉这篇内容对你还蛮有帮忙,我想邀请你帮我三个小忙:
点赞,转发,有你们的『点赞和评论』,才是我发明的能源。
关注公众号『Java 斗帝』,不定期分享原创常识。
同时能够期待后续文章 ing????