这里主要是分析如下3种:静态代理、JDK动态代理、CGLIB动态代理
代理是设计模式的一种,代理类为委托类提供消息预处理,消息转发,事后消息处理等功能。Java中的代理分为三种角色:代理类(ProxySubject)、委托类(RealSubject)、接口(Subject)
三者关系可以表示如下图:
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。
1.why proxy
- 开闭原则:尽量做到对修改关闭,对扩展开放,比如现在有一个委托类,如果想要使用委托类还需要一些其他条件(比如权限),自然最好的做法是使用代理类,这样就不用修改原委托类,而是增加了一个代理类,符合开闭原则。
- 单一职责原则:就是每个类的功能尽可能单一。为什么要单一,因为只有功能单一这个类被改动的可能性才会最小,就拿刚才的例子来说,如果你将权限判断放在当前类里面,当前这个类就既要负责自己本身业务逻辑、又要负责权限判断,那么就有两个导致该类变化的原因,现在如果权限规则一旦变化,这个类就必需得改,显然这不是一个好的设计。
2.静态代理
按照代理创建的时期,可以分为动态代理和静态代理:
- 静态代理:由程序员或者自动生成工具生成代理类,然后进行代理类的编译和运行。在代理类、委托类运行之前,代理类已经以.class的格式存在。
- 动态代理:在程序运行时,由反射机制动态创建而成。
下面举个例子:
新建一个接口,UserService.java, 只有一个方法add()。
package com.adam.java.basic;
public interface UserService {
public abstract void add();
}
建一个该接口的实现类UserServiceImpl.java
package com.adam.java.basic;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("----- add -----");
}
}
建一个代理类ProxyUserService.java
package com.adam.java.basic;
public class ProxyUserService implements UserService {
private UserService userService ;
public ProxyUserService (UserService userService ) {
this.userService = userService ;
}
@Override
public void add() {
System.out.println("Before add...");
userService.add();
System.out.println("After add...");
}
}
测试类
package com.adam.java.basic;
public class DynamicProxyTest {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyUserService proxy = new ProxyUserService(userService);
proxy.add();
}
}
从上面的例子可知,静态代理的特点是,每个被代理对象都需要和一个代理类紧密联系在一起,如果有n种代理模式,m种被代理对象,则需要写m*n个代理类。因此有如下缺点:
- 由于代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了
3.动态代理
鉴于上述代理类和目标类的耦合程度太高,所以才提出了动态代理。
下面说一下JDK动态代理的大致思路:从目标出发,要让被代理类和代理模式解耦,于是引入了一个 中间handler(InvocationHandler),代理类由Proxy通过一系列反射技术代为生成,生成的代理类$proxy继承了Proxy,并提供了一个以InvocationHandler为参数的默认构造函数,因此在调用proxy.newProxyInstance()的时候,其内部就将InvocationHandler实现类传递到了代理类中,在执行代理对象的方法的时候,其实际执行的却是InvocationHandler的invoke()方法。
总结一句话,就是把 代理功能逻辑(代理执行方法)同 委托类 分开,用户在编写代理模式时,只需要编写 代理handler(负责代理功能逻辑) 和实现了subject接口的委托类即可,代理类交由Proxy通过反射技术来自动生成其对应的代理类。
【为什么JDK动态代理只支持对接口的代理】
由于java的单继承,动态生成的代理类已经继承了Proxy类,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。
参考链接:https://blog.csdn.net/yhl_jxy/article/details/80586785
4.CGLIB代理
https://blog.csdn.net/flyfeifei66/article/details/81481222#cglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86
与JDK动态代理不同,CGLIB支持对类的代理,它主要是通过字节码生成技术,修改原有的被代理类的字节码来生成其子类(相当于是代理类继承了被代理类,所以被代理类不能用final修饰,否则无法继承)。
5.总结
- cglib是直接继承了原有类,实现了Factory接口,而jdk是实现了自己的顶层接口,继承了Proxy接口
- 两者都使用了运行时动态字节码生成技术,只是cglib用的是直接通过字节码方式,而jdk动态代理用的是反射
- cglib可以针对类代理,jdk只能针对接口代理(单继承Proxy原因)