原创文章,转载请务必将下面这段话置于文章开头处。
本文转发自Jason’s Blog,原文链接 http://www.jasongj.com/design_pattern/dynamic_proxy_cglib/
静态代理 VS. 动态代理
静态代理,是指程序运行前就已经存在了代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定。
上一篇文章《Java设计模式(六) 代理模式 VS. 装饰模式》所讲的代理为静态代理。如上文所讲,一个静态代理类只代理一个具体类。如果需要对实现了同一接口的不同具体类作代理,静态代理需要为每一个具体类创建相应的代理类。
动态代理类的字节码是在程序运行期间动态生成,所以不存在代理类的字节码文件。代理类和被代理类的关系是在程序运行时确定的。
JDK动态代理
JDK从1.3开始引入动态代理。可通过java.lang.reflect.Proxy
类的静态方法Proxy.newProxyInstance
动态创建代理类和实例。并且由它动态创建出来的代理类都是Proxy类的子类。
定义代理行为
代理类往往会在代理对象业务逻辑前后增加一些功能性的行为,如使用事务或者打印日志。本文把这些行为称之为代理行为。
使用JDK动态代理,需要创建一个实现java.lang.reflect.InvocationHandler
接口的类,并在该类中定义代理行为。
package com.jasongj.proxy.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SubjectProxyHandler implements InvocationHandler {
private static final Logger LOG = LoggerFactory.getLogger(SubjectProxyHandler.class);
private Object target;
@SuppressWarnings("rawtypes")
public SubjectProxyHandler(Class clazz) {
try {
this.target = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
LOG.error("Create proxy for {} failed", clazz.getName());
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preAction();
Object result = method.invoke(target, args);
postAction();
return result;
}
private void preAction() {
LOG.info("SubjectProxyHandler.preAction()");
}
private void postAction() {
LOG.info("SubjectProxyHandler.postAction()");
}
}
从上述代码中可以看到,被代理对象的类对象作为参数传给了构造方法,原因如下
- 如上文所述,动态代理可以代理多种类,而且具体代理哪种类并非台静态代理那样编译时确定,而是在运行时指定
- 之所以不传被代理类的实例而是传类对象,是为了与上文《Java设计模式(六) 代理模式 VS. 装饰模式》吻合——被代理对象不由客户端创建而由代理创建,客户端甚至都不需要知道被代理对象的存在。具体传被代理类的实例还是传类对象,并无严格规定
- 一些讲JDK动态代理的例子会专门使用一个public方法去接收该参数。但笔者个人认为最好不要在具体类中实现未出现在接口定义中的public方法
注意,SubjectProxyHandler定义的是代理行为而非代理类本身。实际上代理类及其实例是在运行时通过反射动态创建出来的。
JDK动态代理使用方式
代理行为定义好后,先实例化SubjectProxyHandler(在构架方法中指明被代理类),然后通过Proxy.newProxyInstance动态创建代理类的实例。
package com.jasongj.client;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import com.jasongj.proxy.jdkproxy.SubjectProxyHandler;
import com.jasongj.subject.ConcreteSubject;
import com.jasongj.subject.ISubject;
public class JDKDynamicProx