代理模式分为静态代理和动态代理,静态代理很简单,像我们常用controller 就是,如下代码
@Controller("/a")
public class AController{
@autowired
private AService a;
@RequestMapping("/test")
public void test(){
//方法调用前干点事
a.xx();
//方法调用后干点事
}
}
AController 可以代理很多service,但是每次都要改代码;而动态代理则是可以动态实现,不需要改代码,动态代理的实现又分为JDK,和CGLIB
JDK动态代理
package com.wei;
public interface AService {
void xx();
}
package com.wei;
public class AServiceImpl implements AService {
public void xx() {
System.out.println("xx");
}
}
package com.wei;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
/**
* 获取目标对象的代理对象
* @return 代理对象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 执行目标对象的方法
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目标对象的方法执行之前简单的打印一下
System.out.println("------------------before------------------");
// 执行目标对象的方法
Object result = method.invoke(target, args);
// 在目标对象的方法执行之后简单的打印一下
System.out.println("-------------------after------------------");
return result;
}
}
public static void main(String[] args) {
// 实例化InvocationHandler
MyInvocationHandler invocationHandler = new MyInvocationHandler(new AServiceImpl());
// 根据目标对象生成代理对象
AService proxy = (AService) invocationHandler.getProxy();
// 调用代理对象的方法
proxy.xx();
}
执行结果:
------------------before------------------
xx
-------------------after------------------
大家写过的都懂,即jdk生成一个代理类(用断点放到proxy可看到)实现AService接口,用代理类调用xx方法,这个类是在内存中,那我们把main方法改造下,加一个生产动态代理的方法,看看这个代理类长什么样
public static void main(String[] args) throws Exception {
// 实例化InvocationHandler
MyInvocationHandler invocationHandler = new MyInvocationHandler(new AServiceImpl());
// 根据目标对象生成代理对象
AService proxy = (AService) invocationHandler.getProxy();
// 调用代理对象的方法
proxy.xx();
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[] { AService.class });
FileOutputStream os = new FileOutputStream("E://$Proxy0.class");//生成的代理类放到E盘
os.write(bytes);
os.close();
}
反编译E盘的这个类
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements AService
{
// 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
/**
* 这个方法是关键部分
*/
public final void xx()
{
try
{
// 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
// 在静态代码块中获取了4个方法:Object中的equals方法、AService中的xx方法、Object中的hashCode方法、Object中toString方法
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("dynamic.proxy.AService").getMethod("xx", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
可以看到 xx() 方法就是调用了super.h.invoke(this, m3, null); 那h到底是什么,先看构造方法
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
,通过new 的对象传入的h=传入的invocationhandler,
再回到上面的main方法
invocationHandler.getProxy(),打开这个方法,看到
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
打开newProxyInstance 这个方法
···
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
....
通过传过来的参数找到对应的代理类,并且实例化对象,传入h,即上一步的this对象
return cons.newInstance(new Object[]{h});
...
···
由此可知,最终proxy.xx();调用的是MyInvocationHandler.invoke 方法
当然
// 执行目标对象的方法
Object result = method.invoke(target, args);
这段代码是通过反射找到目标方法进行执行,所以说执行效率肯定不高
我们可以自己手写一个MyProxy和MyClassLoader 来模拟jdk的过程
package com.wei;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class MyProxy {
public static Object newProxyInstance(MyClassLoader classLoader, Class>[] interfaces, MyInvocationHandler h) {
try {
// 1.动态生成java文件
String src = generateSoure(interfaces);
String filePath = MyProxy.class.getResource("").getPath();
File f = new File(filePath + "$Proxy0.java");
// 2.java文件写入文件流
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
// System.out.println(f);
// 3.把生成的java文件编译成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);
Iterable iterable = manage.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
task.call();
manage.close();
// 4.生成的class文件加载到 JVM
Class proxyClass = classLoader.findClass("$Proxy0");
Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);
f.delete();
// 5.加载的class重新实例化对象
return c.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String generateSoure(Class>[] interfaces) {
StringBuffer sb = new StringBuffer();
sb.append("package com.wei;");
sb.append("import java.lang.reflect.Method;");
// 这里只生成第一个接口的方法
sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{");
sb.append("MyInvocationHandler h;");
sb.append("public $Proxy0(MyInvocationHandler h) { ");
sb.append("this.h = h;");
sb.append("}");
for (Method m : interfaces[0].getMethods()) {
sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {");
sb.append("try{");
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + m.getName()
+ "\",new Class[]{});");
sb.append("this.h.invoke(this,m,null);");
sb.append("}catch(Throwable e){");
sb.append("e.printStackTrace();");
sb.append("}");
sb.append("}");
}
sb.append("}");
System.out.println(sb);
return sb.toString();
}
}
package com.wei;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoader extends ClassLoader {
private File classPathFile;
public MyClassLoader() {
String classPath = MyClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
protected Class> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if (classPathFile != null) {
File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");
if (classFile.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try {
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
//通过父类的defineClass方法找到我们生成的class文件加载并返回
return defineClass(className, out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
改动
public Object getProxy() {
return MyProxy.newProxyInstance(new MyClassLoader(),
target.getClass().getInterfaces(), this);
}
运行效果是一样的
------------------before------------------
xx
-------------------after------------------
CGLIB
public class MyInvocationHandler implements MethodInterceptor{
public Object getInstance(Class> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
//把传过来的类设置为父类
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/**
* 执行目标对象的方法
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws
Throwable {
// 在目标对象的方法执行之前简单的打印一下
System.out.println("------------------before------------------");
// 执行父类的方法
Object result = methodProxy.invokeSuper(o,objects);
// 在目标对象的方法执行之后简单的打印一下
System.out.println("-------------------after------------------");
return result;
}
}
public static void main(String[] args) throws Exception {
// 实例化InvocationHandler
AServiceImpl proxy = (AServiceImpl)new MyInvocationHandler().getInstance(AServiceImpl.class);
// 调用代理对象的方法
proxy.xx();
}
运行结果是一样的,只不过CGLib 代理的目标对象不需要实现任何接口,它是通过动态继承目标对象来重写代理类,性能为什么这么快,因为cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法。
CGLib 和 JDK 动态代理对比
1.JDK 动态代理是实现了被代理对象的接口,CGLib 是继承了被代理对象
2.JDK 和 CGLib 都是在运行期生成字节码,JDK 是直接写 Class 字节码,CGLib 使用 ASM框架写 Class 字节码,Cglib 代理实现更复杂,生成代理类比 JDK 效率低
3.JDK 调用代理方法,是通过反射机制调用,CGLib 是通过 FastClass 机制直接调用方法,CGLib 执行效率更高
静态代理和动态的本质区别
1、静态代理只能通过手动完成代理操作,如果被代理类增加新的方法,代理类需要同步新增,违背开闭原则
2、动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则
3、若动态代理要对目标类的增强逻辑扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码
代理模式的优缺点
优点:
1、代理模式能将代理对象与真实被调用的目标对象分离
2、一定程度上降低了系统的耦合度,扩展性好
3、可以起到保护目标对象的作用
4、可以对目标对象的功能增强
缺点:
1、代理模式会造成系统设计中类的数量增加
2、在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢
3、增加了系统的复杂度