设计模式总结(Java)—— 代理模式(静态代理和动态代理)
一、静态代理1.1 什么事静态代理由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。1.2 案列接口类:public interface Subject {public void play();}实现类:public class RealSubject implements Subject{p...
一、静态代理
1.1 什么事静态代理
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
1.2 案列
接口类:
public interface Subject {
public void play();
}
实现类:
public class RealSubject implements Subject{
public void play() {
System.out.println("逗你玩!");
}
}
代理类:
public class ProxySubject implements Subject{
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
public void play() {
subject.play();
}
}
测试类:
public class Test {
public static void main(String[] args) {
ProxySubject subject = new ProxySubject(new RealSubject());
subject.play();
}
}
静态代理优缺点:
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
另外,如果要按照上述的方法使用代理模式,那么真实角色(委托类)必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色(委托类),该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。
二、动态代理
2.1 什么是动态代理
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。
2.2 JDK动态代理
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
案列
接口类和实现类同上静态代理一样
代理类:
public class ProxySubject {
private static void before() {
System.out.println("方法执行前 !");
}
private static void after() {
System.out.println("方法执行后");
}
public static void main(String[] args) {
final Subject subject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object object = method.invoke(subject, args);
after();
return object;
}
});
proxy.play();
}
}
JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。
2.3 cglib动态代理
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)
public class BookFacadeImpl {
public void addBook() {
System.out.println("新增图书...");
}
}
代理类:
public class BookFacadeCglib implements MethodInterceptor {
//相当于JDK动态代理中的绑定
public Object getInstance(Class clazz) {
Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
enhancer.setSuperclass(clazz); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
//设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("预处理——————");
Object obj = proxy.invokeSuper(object, args); // 调用业务类(父类中)的方法
System.out.println("调用后操作——————");
return obj;
}
public static void main(String[] args) {
BookFacadeCglib cglib = new BookFacadeCglib();
BookFacadeImpl proxy = (BookFacadeImpl) cglib.getInstance(BookFacadeImpl.class);
proxy.addBook();
}
}
2.4.二者优缺点分析
使用JDK动态代理,目标类必须实现的某个接口,如果某个类没有实现接口则不能生成代理对象。
Cglib原理是针对目标类生成一个子类,覆盖其中的所有方法,所以目标类和方法不能声明为final类型。
从执行效率上看,Cglib动态代理效率较高。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)