1、静态代理与动态代理区别
java class文件加载
一个正常的java工程,是在编辑器里编辑代码,通过编译生成class文件,存储在磁盘中。这些class文件是二进制文件,JVM虚拟机通过读取这些字节码文件,取出二进制数据,加载到内存中,解析.class文件内的信息,生成对应的class对象。
代理模式
代理模式是一种设计模式,提供了对目标对象另外的访问方式。即通过代理对象访问目标对象,这样,在代理对象添加额外操作,可以扩展目标对象的功能。java中可以分为静态代理和动态代理。
静态代理
需要为每一个目标对象都在代码阶段实现一个代理对象。即在系统运行时,此代理对象已经存在,这样会导致我们的系统的类数量繁多,不易维护。
动态代理
为了避免静态代理的问题,可以动态的创建代理。在运行阶段,需要代理的地方,可以根据目标类和目标类的接口,动态的穿件一个代理,这个动态代理不会存在class文件,在用完之后就会销毁,这样就避免系统中类冗余繁多的问题。
spring的两大核心思想是Ioc(控制反转)和AOP(切面编程)。AOP主要通过java的动态代理和cglib代理实现。动态代理需要所代理的目标类要有接口。而cglib是通过继承所代理的目标类来实现,一般情况下优先使用java的动态代理。
2、 jdk动态代理实例
下面通过一个例子说明动态代理的实现以及需要注意的事项。
UserService对于一个增删改查的接口。
public interface UserService {
void insert();
void update();
void query();
void delete();
}
而UserServiceImpl是上述接口的实现
public class UserServiceImpl implements UserService {
/* (non-Javadoc)
* @see com.test.aop.dongtaidaili.UserService#insert()
*/
@Override
public void insert() {
// TODO Auto-generated method stub
System.out.println("插入");
}
/* (non-Javadoc)
* @see com.test.aop.dongtaidaili.UserService#update()
*/
@Override
public void update() {
// TODO Auto-generated method stub
System.out.println("更新");
}
/* (non-Javadoc)
* @see com.test.aop.dongtaidaili.UserService#query()
*/
@Override
public void query() {
// TODO Auto-generated method stub
System.out.println("查询");
}
/* (non-Javadoc)
* @see com.test.aop.dongtaidaili.UserService#delete()
*/
@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("删除");
}
}
我们需要对UserServiceImpl进行动态代理,增强其内部方法。
public class UserServiceProxy implements InvocationHandler{
/**
* @param us
*/
public UserServiceProxy(Object us) {
super();
this.us = us;
}
private Object us;
public Object getUserServiceProxy(){
Object newProxyInstance =Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
us.getclass().getInterfaces(), this);
return newProxyInstance;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("事务开始"); //对目标类的方法进行代码增强
System.out.println("Method:" + method);
System.out.println("proxy:" + proxy.getClass());
Object invoke = method.invoke(us, args);
System.out.println("事务提交"); //对目标类的方法进行代码增强
return invoke;
}
}
动态代理的代理类有两个关键点:
一是要实现InvocationHandler接口。这个接口有一个方法,当我们通过动态代理类调用方法时,就会转而调用这个代理类的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args)
在这个方法里,我们并进行相关代码增强(即实现相关权限检查,事务等逻辑)。三个参数
Object proxy:**是被代理的目标类。比如上述的例子中,就是我们传入的UserServiceImpl的类的实例。而不是代理类本身。 如果我们传入代理类本身,会发现结果是不断递归调用,直到栈溢出。这是需要注意的。**
Method method:代理类的方法
Object[] args:代理类的方法的参数
代理类的另一个关键是Proxy,用于动态生成代理对象的类。其常用方法
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
ClassLoader loader:类加载器,可以用代理类或者被代理类的类加载器(其实是同一个)
Class>[] interfaces:被代理类的接口数组
InvocationHandler h:代理类,指向UserServiceProxy本身
最后,看结果验证:
public class Test {
@org.junit.Test
public void test(){
UserService userService=new UserServiceImpl();
ServiceProxy proxy=new ServiceProxy(userService);
Object userServiceProxy = proxy.getUserServiceProxy();
System.out.println("userServiceProxy is "+userServiceProxy.getClass().getName());
((UserService) userServiceProxy).delete();
}
}
结果:
userServiceProxy is com.sun.proxy.$Proxy4
事务开始
Method:public abstract void com.test.aop.dongtaidaili.UserService.delete()
proxy:class com.sun.proxy.$Proxy4
删除
事务提交
结果分析:
1、userServiceProxy is com.sun.proxy.$Proxy4 表明 Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象。
2、事务开始、事务提交是我们对被代理对象的方法增强内容,其在代理类的invoke()方法中实现。
3、在invoke方法中 System.out.println("Method:" + method); 其结果是Method:public abstract void com.test.aop.dongtaidaili.UserService.delete() 表明invoke的method就是被代理对象的方法
4、System.out.println("userServiceProxy is "+userServiceProxy.getClass().getName());的结果是proxy:class com.sun.proxy.$Proxy4,表明invoke的第一个参数Object proxy是代理对象。
5、Object newProxyInstance = Proxy.newProxyInstance(ServiceProxy.class.getClassLoader(),us.getClass().getInterfaces(), this);这个生成代理对象,传入了类加载器、代理类的接口以及自己。在调用代理对象的方法时,其内部是 this.h.invoke(this, m3, null);其中m3就是被代理对象的方法。所以我们在代理类中的invoke方法中实现代理逻辑,并且最终调用目标对象的方法。代理类中药传入目标对象的实现,也是为了在这里使用。即Object invoke = method.invoke(us, args);这里的us为目标对象,通过反射调用目标对象的方法。
6、由于代理对象是extends了Proxy,因此其只能对接口进行代理
。
3、jdk动态代理的原理
根据第二节的举例,实现动态代理的实例就是newProxyInstance :
private Object us;
public Object getUserServiceProxy(){
Object newProxyInstance =Proxy.newProxyInstance(UserServiceProxy.class.getClassLoader(),
us.getclass().getInterfaces(), this);
return newProxyInstance;
}
通过字面意思,可以理解为通过目标类的接口集合,生成代理类,其中的this是指实现了InvocationHandler接口的实例,通过第二节,知道所有的动态代理类的方法访问,最终都会回到它的 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}方法。
查看Proxy.newProxyInstance的源码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
}
/*
* Look up or generate the designated proxy class.
* 这里是生成了动态代理的类,并进行了加载,返回的cl应该类似于下面
* Class> clazz = classLoader.defineMyClass(result, 0, read);
* 即已经被jvm加载了
*/
Class> cl = getProxyClass0(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//constructorParams是指包含InvocationHandler.class一个元素的数组
//这里就是生成了以InvocationHandler为输入参数的构造方法
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//返回了以InvocationHandler为入参的构造方法进行了动态代理类的实例化
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
上述的重点都在注释中标注了,就是动态代理类的生成及加载,然后生成一个以InvocationHandler为入参的构造方法的实例。所以,重点在于如何生成这个动态代理类,或者说这个动态代理类到底长什么样?
JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName,class[] interfaces) 底层方法来产生动态代理类的字节码,因此,可以定义一个工具:
public class ProxyUtils {
/*
* 将根据类信息 动态生成的二进制字节码保存到硬盘中,
* 默认的是clazz目录下
* params :clazz 需要生成动态代理类的类
* proxyName : 为动态生成的代理类的名称
*/
public static void generateClassFile(Class clazz,String proxyName)
{
//根据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, clazz.getInterfaces());
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
//保留到硬盘中
out = new FileOutputStream(paths+proxyName+".class");
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
然后,在我们的动态代理类的定义中,加入
public Object getUserServiceProxy(){
Object newProxyInstance = Proxy.newProxyInstance(ServiceProxy.class.getClassLoader(),
us.getClass().getInterfaces(), this);
//加入这一句,将动态代理的class文件存入硬盘
ProxyUtils.generateClassFile(us.getClass(), "xxxService");
return newProxyInstance;
}
。然后,通过反编译工具如jd-gui.exe 打开:
import com.test.aop.proxy.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class xxxService extends Proxy
implements UserService
{
private static Method m1;
private static Method m5;
private static Method m6;
private static Method m2;
private static Method m4;
private static Method m0;
private static Method m3;
public xxxService(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void insert()
throws
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void query()
throws
{
try
{
this.h.invoke(this, m6, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void delete()
throws
{
try
{
//都是转而调用InvocationHandler 的invoke方法实现
this.h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void update()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
//为每一个需要方法对象,当调用相应的方法时,分别将方法对象作为参数传递给InvocationHandler处理
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m5 = Class.forName("com.test.aop.proxy.UserService").getMethod("insert", new Class[0]);
m6 = Class.forName("com.test.aop.proxy.UserService").getMethod("query", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("com.test.aop.proxy.UserService").getMethod("delete", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.test.aop.proxy.UserService").getMethod("update", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
从反编译的动态代理类可知:
1、xxxService extends Proxy implements UserService,因此,jdk的动态代理类的目标类需要实现接口,因为我们的动态代理类已经继承了Proxy这个类,不能再继承其他类,那么动态代理类和目标类产生关联,只能通过接口了。(类与类之间产生关系的可以通过实现功能接口或者继承实现。)
2、动态代理类对于接口的方法实现,均是调用InvocationHandler的public Object invoke(Object proxy, Method method, Object[] args) 方法实现。