一、代理的优势
代理通过继承或一些其它接口方法来实现原实现类额外的功能,而不修改实现类,可以避免额外功能代码对原代码的侵入。
假设有一个接口
public interface userservice {
boolean login(String name,String passwd);
boolean logout();
}
对接口的实现类为
public class userserviceimpl1 implements userservice {
public boolean login(String name, String passwd) {
System.out.println("----login-----");
return false;
}
public boolean logout() {
System.out.println("----logout-----");
return false;
}
}
如何在不修改实现类的情况下实现功能的增加。
二、代理的实现方式
(1)静态代理
public class staticproxy implements userservice{
userserviceimpl1 l;
staticproxy(userserviceimpl1 u)
{l=u;}
@Override
public boolean login(String name, String passwd) {
System.out.println("----before-----");
boolean res=l.login(name,passwd);
System.out.println("----after-----");
return res;
}
@Override
public boolean logout() {
System.out.println("----before-----");
boolean res=l.logout();
System.out.println("----after-----");
return false;
}
}
通过新建一个类继承接口,同时设置一个实现类的对象属性,来完成方法的调用和额外功能的实现。如果有多个类需要代理,则需要创建多个代理类,代码冗余且容易出错。
(2)动态代理
通过JDK或CGlib两种工具来实现java动态字节码的加载。
-
JDK方式
通过
Proxy.newProxyInstance(ClassLoader,Interfaces,InvocationHandler)
来返回一个代理对象,可以强制类型转换为接口类型,之后使用接口方法,调用的是Handler中的方法,即自定义方法。前面参数传入的是实现类的类加载器和接口,之后自定义实InvocationHandler接口类。
public class userviceproxy implements InvocationHandler {
Object object;
userviceproxy(Object target)
{
object=target;
}
userviceproxy(){}
public Object bind(Object target)
{
object=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
System.out.println("----before-----");
Object t=method.invoke(object,args);
System.out.println("----after-----");
return t;
}
}
类加载器(ClassLoader)的作用:
- 将.java文件编译后得到的.class文件字节码加载进JVM方法区中。
- 通过类加载器创建类的Class对象,进而创建这个类的对象。
类加载器和这个类本身组成了类在JVM中的唯一性,即只有经过相同的类加载器加载的class文件类才是同一个类,否则即使来源于同一个class文件,在同一个JVM中使用,但由不同的classloader加载,那么也是不同的类。相同是指包括代表类的Class对象的equals()方法、 isAssignableFrom() 方法、 isInstance() 方法的返回结果。在加载class文件时,jvm会给类分配一个独特的classloader。动态代理中,不存在java文件和class文件,因此不需要classloader加载,但jvm中不存在代理类的class对象,需要一个classloader来创建class对象,此时借用被代理类的classloader来创建。
-
CGlib方式实现动态代理
UserService userService = new UserService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("--- cglib log ----");
Object ret = method.invoke(userService, args);
// 执行原始方法
return ret;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login("zhenyu", "123456");
userServiceProxy.register(new User());
三、总结
JDK
|
Cglib
|
classloader
|
enhancer.setClassLoader()
|
Interfaces
|
setSuperClass
|
InvocationHandler
|
methodIntercepter
|
Proxy.newProxyInstance() cast to interface
|
enhancer.create() cast to superclass
|
- JDK通过实现接口来创建动态代理,要求原始类实现接口,只代理接口上的方法,方法返回也只能转换为接口类型proxy.newProxyInstance(classloader,interfaces,invocationhandler)。
- CGlib通过继承原始类来创建动态代理,不需要原始类实现接口,方法返回转换为父类类型。