1. 静态代理和动态代理
本章节参考了https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/
1.1. 定义
静态代理和动态代理指的是实现代理模式的方式。静态模式意思是所有的代码是静态写好的。而动态代理则相对,部分代码是动态生成的。在动态代理中还分为JDK动态代理和CGLib动态代理。
1.2. 关键实现
1.2.1. 静态代理
静态代理是基于接口实现的,他要求真实类和代理类实现同样的接口。
public interface IDBQuery {
String request();
}
public class DBQuery implements IDBQuery{
public DBQuery(){
try{
Thread.sleep(1000);//假设数据库连接等耗时操作
}catch(InterruptedException ex){
ex.printStackTrace();
}
}
@Override
public String request() {
// TODO Auto-generated method stub
return "request string";
}
}
public class DBQueryProxy implements IDBQuery{
private DBQuery real = null;
@Override
public String request() {
// TODO Auto-generated method stub
//在真正需要的时候才能创建真实对象,创建过程可能很慢
if(real==null){
real = new DBQuery();
}//在多线程环境下,这里返回一个虚假类,类似于 Future 模式
return real.request();
}
}
public class Main {
public static void main(String[] args){
IDBQuery q = new DBQueryProxy(); //使用代里
q.request(); //在真正使用时才创建真实对象
}
}
1.2.2. JDK代理
当使用JDK代理时,一个最直观的变化就是代理类不需要和真实类实现同一个接口了。取而代之的是代理类实现了InvocationHandler,并Override了invoke方法。在方法里可以统一对实现方法做处理(方法调用前,方法调用后)。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DBQueryHandler implements InvocationHandler{
IDBQuery realQuery = null;//定义主题接口
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
//如果第一次调用,生成真实主题
if(realQuery == null){
realQuery = new DBQuery();
}
//method.invoke(target, args); 执行调用的方法。
//返回真实主题完成实际的操作
return realQuery.request();
}
public static IDBQuery createProxy(){
IDBQuery proxy = (IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new DBQueryHandler()); // 注意,生成的代理类实例被强转为IDBQuery
return proxy;
}
}
1.2.3. CGLib代理
CGLib一个直观的最大的特点就是真实类无需实现接口(当然实现了也没关系)。
接口类
public interface BookProxy {
public void addBook();
}
实现类
//该类并没有申明 BookProxy 接口
public class BookProxyImpl {
public void addBook() {
System.out.println("增加图书的普通方法...");
}
}
代理类
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class BookProxyLib implements MethodInterceptor {
private Object target;
/**
* 创建代理对象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
// 回调方法
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("事物开始");
proxy.invokeSuper(obj, args);
System.out.println("事物结束");
return null;
}
}
调用方法
public class TestCglib {
public static void main(String[] args) {
BookProxyLib cglib=new BookProxyLib();
BookProxyImpl bookCglib=(BookProxyImpl)cglib.getInstance(new BookProxyImpl());
bookCglib.addBook(); //可以看到BookProxyLib并没有声明BookProxy接口,但是仍然可以调用addBook方法
}
}
1.3. 区别和共同点
静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
https://blog.csdn.net/neosmith/article/details/51072840
2. 实际应用场景举例
todo:没有理解cglib不用真实类实现接口的意义。因为真实类没有实现接口,但是暴露了public的方法。这和直接调用有啥区别?
另外cglib的试用场景,真实类没有实现的接口意义何在?如果没有接口来规范统一的调用逻辑,例如一堆的实现类实现了A接口,因此必须实现A接口中定义的B方法。这样才有意义吧?
查看spring源码,了解spring中,cglib的使用方法,来解答上述疑问。
代理模式的意义
- 有代理,便于解耦
- 静态代理太麻烦,每个都要
- JDK代理受限于要实现接口
- CGLib不需要实现接口,看上去无法统一接口的方法,但是可能是用在一些common的方法,例如Object的方法。用在类创建时刻。
- 另外让方法运行只是最基本的,代理模式的最大用途是管理原方法的运行前,后,时(切面,AOP)。