设计模式之动态代理模式实战
昨天分享了静态代理的概念及存在的缺点,所以今天讲的动态代理模式十分重要。动态代理在我们工作当中应用相当广泛,如Srping AOP就是动态代理的在开源框架的比较出名的应用。
动态代理有两种试,一是通过JDK自带的API实现动态代理,二是通过别的字节码框架实现,如cglib。
需要注意的是JDK只能针对接口实现动态代理,不能代理普通类,使用具有局限性。而cglib可以代理接口及所有的普通类。
下面拿昨天保存用户信息的例子继续用动态代理来实现。
用户接口
public interface UserInterface {
boolean saveUser(User user);
}
用户接口实现
public class UserInterfaceImpl implements UserInterface {
@Override
public boolean saveUser(User user) {
System.out.println("保存用户: " + user.getName());
return true;
}
}
public class Test {
public static void main(String[] args) {
// JDK动态代理
testJDKProxy();
// Cglib接口代理
testCglibInterfaceProxy();
// Cglib类代理
testCglibClassProxy();
}
private static void testJDKProxy() {
User user = new User();
user.setName("tom");
UserProxy.getUserProxy().saveUser(user);
}
static class UserProxy {
private static final InvocationHandler USER_HANDLE = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK接口动态代理-开始保存用户");
Object result = method.invoke(new UserInterfaceImpl(), args);
System.out.println("JDK接口动态代理-保存用户结果: " + result);
System.out.println();
return result;
}
};
public static UserInterface getUserProxy() {
UserInterface userInterface = (UserInterface) Proxy.newProxyInstance(UserProxy.class.getClassLoader(),
new Class[] { UserInterface.class }, USER_HANDLE);
return userInterface;
}
}
private static void testCglibInterfaceProxy() {
User user = new User();
user.setName("tom");
UserCglibProxy.getUserProxy().saveUser(user);
}
static class UserCglibProxy {
private static final net.sf.cglib.proxy.InvocationHandler USER_HANDLE = new net.sf.cglib.proxy.InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Cglib接口动态代理-开始保存用户");
Object result = method.invoke(new UserInterfaceImpl(), args);
System.out.println("Cglib接口动态代理-保存用户结果: " + result);
System.out.println();
return result;
}
};
public static UserInterface getUserProxy() {
UserInterface userInterface = (UserInterface) net.sf.cglib.proxy.Proxy.newProxyInstance(
UserCglibProxy.class.getClassLoader(), new Class[] { UserInterface.class }, USER_HANDLE);
return userInterface;
}
}
private static void testCglibClassProxy() {
User user = new User();
user.setName("tom");
UserInterfaceImpl userImpl = (UserInterfaceImpl) ClassCgLibProxy.getUserProxy(new UserInterfaceImpl());
userImpl.saveUser(user);
}
static class ClassCgLibProxy {
private static final MethodInterceptor USER_HANDLE = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Cglib类动态代理-开始保存用户");
Object result = proxy.invokeSuper(obj, args);
System.out.println("Cglib类动态代理-保存用户结果: " + result);
System.out.println();
return result;
}
};
public static Object getUserProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(USER_HANDLE);
return enhancer.create();
}
}
}
结果输出:
JDK接口动态代理-开始保存用户
保存用户: tom
JDK接口动态代理-保存用户结果: true
Cglib接口动态代理-开始保存用户
保存用户: tom
Cglib接口动态代理-保存用户结果: true
Cglib类动态代理-开始保存用户
保存用户: tom
Cglib类动态代理-保存用户结果: true
从例子看出,使用也并不复杂,动态代理与静态代理最主要的区别在于,静态代理是编译期间就确定好的代理关系,而动态代理是运行期间由JVM通过反射等技术生成的代理对象,不存在class文件,代理类与被代理类之间的关系是继承关系,所以,普通类final的方法是不能被动态代理的。