代理,就是代替原来的角色去做事,下面这张图对代理的解释很生动形象:
静态代理
23种设计模式中,代理模式是一种很经典的模式,当我们想改变一个类的行为的时候(比如添加额外的服务像添加日志等),可以创造一个跟这个类实现相同接口的类,重写对应的方法。这就相当于为一个类创建了一个“替身”。
代理模式的结构如下:
比如要给原来的业务逻辑添加日志功能,则可以添加代理类,重写需要添加日志的业务类。
用户管理接口
public interface UserManager {
public void addUser(User user);
}
用户管理实现类
public class UserManagerImpl implements UserManager {
@Override
public void addUser(User user) {
System.out.println("---------addUser()---------");
}
}
用户管理代理类
public class UserManagerProxyImpl implements UserManager{
private UserManager userManager;
public UserManagerProxyImpl(UserManager userManager){
this.userManager=userManager;
}
@Override
public void addUser(User user) {
userManager.addUser(user);
//添加额外服务-日志功能
logger.info("添加用户:"+user.getUserName());
}
}
此时调用这个新的方法,不会改变系统原来的稳定性。
动态代理
上面的静态代理,每个代理类只能为一个业务类服务,如果放在实际应用中,肯定需要使用多个代理,同时会带来大量的重复代码。动态代理可以为任何类动态生成一个代理类来实现全部的代理功能。
静态代理由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
在程序运行时,运用反射机制动态创建而成。
JDK和Cglib都实现了动态代理,但略有差异,下面分别说明。
JDK动态代理
动态代理类克服了上面静态代理类需要继承唯一接口,并且要实现相应的方法的缺陷。他可以为任何类创建代理类并且在运行时动态创建代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LogHandler implements InvocationHandler{
//被代理的目标类
private Object targetObject;
//创建代理
public Object createProxyInstance(Object targetObject){
this.targetObject=targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//代理服务方法
insertLog(method,args);
//调用目标方法(原业务逻辑)
Object ret=method.invoke(targetObject, args);
return ret;
}
private void insertLog(Method method,Object[] args){
System.out.println("logInfo:【methodName:"+method.getName()+";args:"+args+"】");
}
}
实现动态代理,利用Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
方法获得代理类,loader
就是被代理类的类加载器,interfaces
是被代理类实现的接口,它实现了几个接口,就会为其生成几个相应的代理类。此外必须要实现InvocationHandler
接口,重写其invoke
方法,用java.lang.reflect.Method.invoke()
方法调用目标方法(可以在调用之前或者之后添加代理服务方法)。
客户端调用如下:
public class Client {
public static void main(String[] args) {
LogHandler logHandler=new LogHandler();
UserManager userManager=(UserManager)logHandler.createProxyInstance(new UserManagerImpl());
userManager.addUser(new User("Danny","123456"));
OrderManager orderManager=(OrderManager)logHandler.createProxyInstance(new OrderManagerImpl());
orderManager.addOrder(new Order("Danny","B20160726085911223004"));
}
}
如上,调用动态代理的时候,new一个动态代理的实例,通过动态代理来创建原业务类的代理类,然后直接调用接口就行。这样不仅可以为UserManager这一个接口服务了,其他接口的方法如果需要添加日志,都可以通过这一个动态代理类来实现。
Cglib动态代理
添加jar包asm-commons-2.2.2.jar、asm-util-2.2.2.jar、asm-2.2.2.jar、cglib-nodep-2.1_3.jar。
注意:如果用了cglib-nodep-2.1_3.jar,就不能用cglib-2.2.2.jar了,这两个jar包会冲突;而且Spring的asm-2.2.2.jar和Hibernate的asm.jar也会冲突,不能共存。否则会报如下的错:
java.lang.NoSuchMethodError: org.objectweb.asm.ClassWriter.(Z)V
Cglib代理实现
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
// 设置需要创建子类的类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通过字节码技术动态创建子类实例
return enhancer.create();
}
// 实现MethodInterceptor接口方法
public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
//代理服务方法
insertLog(method,args);
// 通过代理类调用父类中的方法
Object result = proxy.invokeSuper(obj, args);
return result;
}
private void insertLog(Method method,Object[] args){
System.out.println("logInfo:【methodName:"+method.getName()+";args:"+args+"】");
}
}
客户端调用如下:
public class Client {
public static void main(String[] args) {
CglibProxy cglibProxy=new CglibProxy();
UserManagerImpl userManager=(UserManagerImpl)cglibProxy.getProxy(UserManagerImpl.class);
userManager.addUser(new User("Danny","123456"));
}
}
最后简单总结两点JDK和Cglib实现的动态代理的区别:
1、JDK实现动态代理的代理对象和目标对象必须实现相同的接口;Cglib实现动态代理的原理则是为目标对象创建一个子类座位代理对象。如果目标类实现了接口,则必须用JDK动态代理,否则,两个都可以用。
2、JDK在运算量小的时候性能优于Cglib,运算量大的时候Cglib性能较优。详参考《Cglib 与 JDK动态代理的运行性能比较》
【 转载请注明出处——胡玉洋《静态代理与动态代理》】