首页 小编推荐正文

署理办法界说

首要咱们来看看署理办法:

所谓署理办法,是指客户端(Client)并不直接调用实践的方针(下图右下角的RealSu男帅哥bject),而是经过调用署理(ProxySubject),来直接的调用实践的方针。署理办法的运用场合,一般是因为客户端不想直接拜访实践方针,或许拜访实践的方针存在技术上的妨碍,因而经过署理方针作为桥梁,来完结直接拜访。

事务场景

首要有个UserService接口,接口里有一个增加用户的办法

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()办法专心写自己的事务逻辑。

静态署理

依据类图,创立一个静态署理类

public class UserStaticProxy implemen孔军超ts 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();
}
}

运转成果:

如此,一个静态署理类就创立好了,咱们能够专心在Service写事务逻辑,增加日志等非事务逻辑交给这个静态署理类来完结。

静态署理的缺陷

缺陷一:接口增加办法,署理类需求同步保护

跟着事务扩展,UserService类里不知有addUser办法,还有updateUser、deleteUser、batchUpdateUser、batchDeleteUser等办法,这些办法都需求记载日志。

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 UserStati个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下cProxy(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 batchUpdateUse个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下r() {
userService.batchUpdateUser();
System.out.println("拿个小本本记载一下");
}
@Override
public void batchDeleteUser() {
userService.batchDeleteUser();
System.out.println("拿个小本本记载一下");
}
}

从上面咱们能够看到深入敌后的奔跑,署理类里有许多重复的日志代码。因为署理类和方针方针完结同一个接口,一旦接口增加办法,署理类也得同步增加办法而且得同步增加重复的额定功用代码,增大了代码量

缺陷二:接口越多,导致署理类繁复

假如需求增加事务类,如StudentService,TeacherService等等,这些类里的办法也都需求完结增加日志的办法,那么就需求同步创立对应的署理类。此外静态署理类不是主动生成的,需求在编译之前就编写好的,假如事务越来越巨大,那么创立的署理类越来越多,这样又增大了代码量

怎样处理这些缺陷呢?这时分就需求动态署理办法了

JDK动态署理

其实动态署理和静态署理的实质是相同的,终究程序运转时都需求生成一个署理方针实例,经过它来完结相关增强以及事务逻辑,只不过静态署理需求硬编码的办法指定,而动态署理支撑运转时动态生成这种完结办法。

JDK自身帮咱们完结了动态署理,只需求运用newProxyInstance办法:

 public static Object newProxyInstance(Cl个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下assLoader loader,Class

留意该办法是在Proxy类中是静态办法,且接纳的三个参数依次为:

  • ClassLoader loader,:指定当时方针方针运用类加载器
  • Class
  • InvocationHandler h:调用处理程序,将方针方针的办法分派到该调用处理程序

代码示例:

public class DynamicProxy implements InvocationHandler {
pr个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下ivate 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 us豁翎子erService = (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());

综上,动态署理处理了静态署理的缺陷

用arthas检查JDK动态署理生成的类

动态署理是运转时分动态生成署理类的,这个类放在内存中,咱们要怎样才华看到这个类呢?

artias是阿里开源的一个牛逼闪闪的Java确诊东西。

这儿咱们增加一个断点:

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 Met个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下hod m7;
private static Method m0;
private static Method m3;
private static Method m4;
private static Method m5;
public final void addUser() {
try {
th潸潸is.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("个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下proxy.UserService").g江门野协etMethod("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 throwab花形敬le) {
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类。

cglib动态署理

经过调查上面的静态署理和JDK动态署理办法,发现要求方针方针完结一个接口,可是有时分方针方针仅仅一个独自的方针,并没有完结任何的接口。这时分要怎样处理呢?下面引出大名鼎鼎的CGlib动态署理

cglib署理,也叫作子类署理,它是在内存中构建一个子类方针然后完结对方针方针功用的扩展。

要用cglib需求引进它的jar包,因为spring现已集成了它,因而引进sprin污组词g包即可

编写署理类:

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.o易丽美ut.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再封装导致的,也请知道的大大答复一下。

用arthas检查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 stati西方女性c 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) {
U战亚楠serServiceImpl$$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 UserServic个人年终总结,只需掌握了这三种署理办法,才华进军Spring AOP!,无敌天下eImpl

能够看到生成的署理类承继了方针方针,因而有两个留意点:

  1. 方针方针不能处理被final关键字润饰,因血煞狂龙为被final润饰的方针是不行承继的。
  2. 方针方针的办法假如为final/胡定欣老公static,那么就不会被阻拦,即不会履行方针方针额定的事务办法.
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。