首先咱们来看看代理模式: java
代理模式的使用场合,通常是因为客户端不想直接访问实际对象,或者访问实际的对象存在技术上的障碍,于是经过代理对象做为桥梁,来完成间接访问。spring
首先有个UserService接口,接口里有一个添加用户的方法ide
public interface UserService {
void addUser();
}
复制代码
这是它的实现类函数
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加一个用户");
}
}
复制代码
如今须要在添加用户的时候记录一下日志。固然,你能够直接在addUser里面直接写添加日志的代码,工具
public void addUser() {
System.out.println("添加一个用户");
System.out.println("拿个小本本记一下");
}
复制代码
可是Java推崇单一职责原则,若是这样写就违背了这个原则,咱们须要将添加日志的代码解耦出来,让addUser()方法专一写本身的业务逻辑。测试
根据类图,建立一个静态代理类this
public class UserStaticProxy implements UserService{
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService = userService;
}
@Override
public void addUser() {
userService.addUser();
System.out.println("拿个小本本记录一下");
}
}
复制代码
咱们创建一个测试类来测试静态代理:编码
public class Test {
public static void main(String[] args) {
UserStaticProxy userStaticProxy = new UserStaticProxy(new UserServiceImpl());
userStaticProxy.addUser();
}
}
复制代码
运行结果: spa
随着业务扩大,UserService类里不知有addUser方法,还有updateUser、deleteUser、batchUpdateUser、batchDeleteUser等方法,这些方法都须要记录日志。3d
UserServiceImpl类以下:
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加一个用户");
}
@Override
public void updateUser() {
System.out.println("更新一个用户");
}
@Override
public void deleteUser() {
System.out.println("删除一个用户");
}
@Override
public void batchUpdateUser() {
System.out.println("批量更新用户");
}
@Override
public void batchDeleteUser() {
System.out.println("批量删除用户");
}
}
复制代码
那么对应的静态代理类以下:
public class UserStaticProxy implements UserService{
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userService = userService;
}
@Override
public void addUser() {
userService.addUser();
System.out.println("拿个小本本记录一下");
}
@Override
public void updateUser() {
userService.updateUser();
System.out.println("拿个小本本记录一下");
}
@Override
public void deleteUser() {
userService.deleteUser();
System.out.println("拿个小本本记录一下");
}
@Override
public void batchUpdateUser() {
userService.batchUpdateUser();
System.out.println("拿个小本本记录一下");
}
@Override
public void batchDeleteUser() {
userService.batchDeleteUser();
System.out.println("拿个小本本记录一下");
}
}
复制代码
从上面咱们能够看到,代理类里有不少重复的日志代码。由于代理类和目标对象实现同一个接口,一旦接口增长方法,代理类也得同步增长方法而且得同步增长重复的额外功能代码,增大了代码量
若是须要增长业务类,如StudentService,TeacherService等等,这些类里的方法也都须要实现增长日志的方法,那么就须要同步建立对应的代理类。此外静态代理类不是自动生成的,须要在编译以前就编写好的,若是业务愈来愈庞大,那么建立的代理类愈来愈多,这样又增大了代码量
如何解决这些缺点呢?这时候就须要动态代理方法了
其实动态代理和静态代理的本质是同样的,最终程序运行时都须要生成一个代理对象实例,经过它来完成相关加强以及业务逻辑,只不过静态代理须要硬编码的方式指定,而动态代理支持运行时动态生成这种实现方式。
JDK自己帮咱们实现了动态代理,只须要使用newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 复制代码
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
代码示例:
public class DynamicProxy implements InvocationHandler {
private Object target; // 目标对象
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
System.out.println("拿个小本本记录一下");
return result;
}
}
复制代码
上文的invoke方法,负责加强目标对象的方法,接口类的全部方法都会走这个invoke方法。另外bind方法简单封装了JDK的代理方法newProxyInstance,负责返回接口类。
测试类:
public static void main(String[] args) {
DynamicProxy dynamicProxy = new DynamicProxy();
UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
}
复制代码
运行结果以下:
如图UserService接口里的全部方法都已经加上了日志逻辑了,此外,咱们看一下UserDynamicProxy这个类里的target属性是Object类型的。因此,这个动态代理的方法一样能够给其余Service复用。能够这样调用:
DynamicProxy dynamicProxy = new DynamicProxy();
TeacherService teacherService = (TeacherService)dynamicProxy.bind(new TeacherServiceImpl());
复制代码
综上,动态代理解决了静态代理的缺点
动态代理是运行时候动态生成代理类的,这个类放在内存中,咱们要怎么才能看到这个类呢?
artias是阿里开源的一个牛逼闪闪的Java诊断工具,不懂的能够看看这篇文章http://www.dblearn.cn/article/5,用它就能够线上反编译代码。
这里咱们添加一个断点:
public static void main(String[] args) throws IOException {
DynamicProxy dynamicProxy = new DynamicProxy();
UserService userService = (UserService)dynamicProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
System.in.read();
}
复制代码
运行 arthas
用jad命令反编译,java生成的代理类都在com.sun.proxy目录下。所以反编译命令以下
jad com.sun.proxy.$Proxy0
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.UserService;
public final class $Proxy0 extends Proxy implements UserService {
private static Method m1;
private static Method m6;
private static Method m2;
private static Method m7;
private static Method m0;
private static Method m3;
private static Method m4;
private static Method m5;
public final void addUser() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void updateUser() {
try {
this.h.invoke(this, m4, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void deleteUser() {
try {
this.h.invoke(this, m5, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void batchUpdateUser() {
try {
this.h.invoke(this, m6, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void batchDeleteUser() {
try {
this.h.invoke(this, m7, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m6 = Class.forName("proxy.UserService").getMethod("batchUpdateUser", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m7 = Class.forName("proxy.UserService").getMethod("batchDeleteUser", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("proxy.UserService").getMethod("addUser", new Class[0]);
m4 = Class.forName("proxy.UserService").getMethod("updateUser", new Class[0]);
m5 = Class.forName("proxy.UserService").getMethod("deleteUser", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
复制代码
由上面的代码能够看到咱们的代理类已经生成好了,没当咱们调用方法如 addUser(),实际分派到h变量的invoke方法上执行:
this.h.invoke(this, m3, null);
h变量是什么呢?其实就是咱们实现了InvocationHandler的DynamicProxy类。
经过观察上面的静态代理和JDK动态代理模式,发现要求目标对象实现一个接口,可是有时候目标对象只是一个单独的对象,并无实现任何的接口。这时候要怎么处理呢?下面引出大名鼎鼎的CGlib动态代理
cglib代理,也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
要用cglib须要引入它的jar包,由于spring已经集成了它,所以引入spring包便可
编写代理类:
public class CGLibProxy implements MethodInterceptor {
private Object target; // 目标对象
public Object bind(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(this.target.getClass());
//设置回调函数
enhancer.setCallback(this);
//建立子类(代理对象)
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(obj, args);
System.out.println("拿个小本本记录一下");
return result;
}
}
复制代码
其中,Enhancer须要设置目标对象为父类(由于生成的代理类须要继承目标对象)
测试类:
public static void main(String[] args) throws IOException {
CGLibProxy cgLibProxy = new CGLibProxy();
UserServiceImpl userService = (UserServiceImpl)cgLibProxy.bind(new UserServiceImpl());
userService.addUser();
userService.updateUser();
System.in.read();
}
复制代码
运行结果:
咱们看到已经成功代理了。可是结果有乱码出现,此处设置一个// TODO,我猜想是Spring对CGlib再封装致使的,也请知道的大大回答一下。
步骤和JDK代理类雷同,只不过cglib的代理类生成在和测试类同一个包下,因为代码太多,只上部分代码
package com.example.demo.proxy;
import com.example.demo.proxy.UserServiceImpl;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3 extends UserServiceImpl implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$deleteUser$0$Method;
private static final MethodProxy CGLIB$deleteUser$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$addUser$1$Method;
private static final MethodProxy CGLIB$addUser$1$Proxy;
private static final Method CGLIB$updateUser$2$Method;
private static final MethodProxy CGLIB$updateUser$2$Proxy;
private static final Method CGLIB$batchUpdateUser$3$Method;
private static final MethodProxy CGLIB$batchUpdateUser$3$Proxy;
private static final Method CGLIB$batchDeleteUser$4$Method;
private static final MethodProxy CGLIB$batchDeleteUser$4$Proxy;
private static final Method CGLIB$equals$5$Method;
private static final MethodProxy CGLIB$equals$5$Proxy;
private static final Method CGLIB$toString$6$Method;
private static final MethodProxy CGLIB$toString$6$Proxy;
private static final Method CGLIB$hashCode$7$Method;
private static final MethodProxy CGLIB$hashCode$7$Proxy;
private static final Method CGLIB$clone$8$Method;
private static final MethodProxy CGLIB$clone$8$Proxy;
public final void deleteUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$deleteUser$0$Method, CGLIB$emptyArgs, CGLIB$deleteUser$0$Proxy);
return;
}
super.deleteUser();
}
public final void addUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$addUser$1$Method, CGLIB$emptyArgs, CGLIB$addUser$1$Proxy);
return;
}
super.addUser();
}
public final void updateUser() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$updateUser$2$Method, CGLIB$emptyArgs, CGLIB$updateUser$2$Proxy);
return;
}
super.updateUser();
}
复制代码
其中
public class UserServiceImpl$$EnhancerByCGLIB$$3ca8cfc3 extends UserServiceImpl
能够看到生成的代理类继承了目标对象,所以有两个注意点: