前言
本章讲解静态代理和动态代理
方法
一、静态代理
在前面的章节中,我已经介绍了静态代理设计模式,接下来我们继续研究。
优点:
- 保护真实对象
- 让真实对象指责更加明确
- 扩展功能
举例:有一个老板(Boos)和一个秘书(Secretary),老板拥有开会、吃饭、游玩等功能。我们直接就可以产生老板对象并调用相应方法进行实现。但是,例如开会还要有会前的布置工作、会后的具体任务,其不是老板的功能,这个时候我们需要对老板的功能做扩展。使用秘书类代理老板处理其他业务,让老板专心注意于自己的业务即可。
1)新建Boos.java、Secretary.java、功能接口Features.java内容如下
package cn.edu.ccut;
public class Boos implements Features{
private String name;
public Boos() {}
public Boos(String name) {
this.name = name;
}
@Override
public void meeting() {
System.out.println("领导开会");
}
@Override
public void eat() {
System.out.println("领导吃饭");
}
@Override
public void play() {
System.out.println("领导游玩");
}
}
package cn.edu.ccut;
public class Secretary implements Features{
private Boos boos = new Boos("李总");
@Override
public void meeting() {
System.out.println("开会前准备");
this.boos.meeting();
System.out.println("散会");
}
@Override
public void eat() {
System.out.println("吃饭前准备");
this.boos.eat();
System.out.println("送客");
}
@Override
public void play() {
System.out.println("游玩前准备");
this.boos.play();
System.out.println("回城");
}
}
package cn.edu.ccut;
public interface Features {
public void meeting();
public void eat();
public void play();
}
2)新建测试类Test.java进行测试
package cn.edu.ccut;
public class Test {
public static void main(String[] args) {
//新建秘书对象
Secretary secretary = new Secretary();
//开会
secretary.meeting();
}
}
测试结果如下:
完美的实现了静态代理。但是也有一些缺点:
- 当真实对象的功能很多时,代理类也必须编写同样的方法,比较麻烦
接下来的动态代理解决了这一问题,代理类可以写一个方法即可!
二、动态代理(JDK)
为了优化静态代理带来的缺点,动态代理应运而生,其中就包括我们的JDK动态代理!
既然是JDK动态代理,那么也就拥有JDK就足够了。
本次依然拿老板和秘书为例作为讲解。
优点:JDK自带,无需引入外部的jar
缺点:真实对象必须实现接口;利用反射机制,效率不高
1)编写代理类如下
package cn.edu.ccut;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Secretary implements InvocationHandler{
private Boos boos = new Boos("李总");
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("准备工作");
Object obj = method.invoke(this.boos, args);
System.out.println("结束工作");
return obj;
}
}
2)编写测试类进行测试
package cn.edu.ccut;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Features boosProxy = (Features) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {Features.class}, new Secretary());
boosProxy.meeting();
}
}
测试结果如下:
完美的实现了JDK动态代理。
三、动态代理(CGLIB)
为了优化JDK动态代理带来的缺点,CGLIB动态代理应运而生!
cglib基于字节码机制,可生成真实对象的子类而非JDK代理生成的代理对象
优点:真实对象无需实现接口;效率高
缺点:需引入外部的jar
1)准备工作
需要引入cglib的相关jar包
2)编写代理类如下
package cn.edu.ccut;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class Secretary implements MethodInterceptor{
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("准备工作");
Object obj = arg3.invokeSuper(arg0, arg2);
System.out.println("结束工作");
return obj;
}
}
3)编写测试类
package cn.edu.ccut;
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Boos.class);
enhancer.setCallback(new Secretary());
Boos boos = (Boos) enhancer.create();
boos.meeting();
}
}
测试结果如下:
完美的实现了CGLIB动态代理。
总结:
当我们使用SpringAOP的时候,其底层原理就是动态代理机制!
Spring默认采用JDK动态代理,我们需要在Spring配置文件加入如下配置强制其使用CGLIB动态代理,以便于操作:
<!--
true表示Cglib动态代理
false表示JDK动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>