当前位置: 首页 >> 最近大事件 >> 看书网,只要把握了这三种署理形式,才干进军Spring AOP!,淋巴肿瘤 >> 正文

看书网,只要把握了这三种署理形式,才干进军Spring AOP!,淋巴肿瘤

2019年04月11日 00:31:49     作者:admin     分类:最近大事件     阅读次数:201    

署理办法界说

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

只需掌握了这三种署理办法,才华进军Spring AOP!

所谓署理办法,是指客户端(Client)并不直接调用实践的方针(下图右下角的RealSubject),而是经过调用署理(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 implements UserService{
private UserService userService;
public UserStaticProxy(UserService userService) {
this.userServi姐summerce = userService;
}
@Override
public void addUser() {
userService.addUser();
System.out.println("拿个小本本记载一下");
}
}

咱们树立一个测验类来测验静态署理:

public class Tes看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤t {
public static void main(String[] args) {
UserStaticProxy userStaticProxy = new UserStaticProxy(new UserServiceImpl());
userStaticProxy.addUser();
}
}

运转成果:

只需掌握了这三种署理办法,才华进军Spring AOP!

如此,一个静态署理类就创立好了,咱们能够专心在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 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("拿个小本本记载一下");
}
@Over看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤ride
public vo神马四兄弟之笑看风云id batchDeleteUser() {
userService.batchDeleteUser();
System.out.println("拿个小本本记载一下");
}
}

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

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

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

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

JDK动态署理

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

JDK自身帮咱们完结了动态署理,只需求运用newProxyInsta萝莉在线观看nce办法:

 public static Object newProxyInstance(Cl看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤assLoader loader,Class

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

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

代码示例:

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());

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

用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.lakrissicang.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.invo洗冤重生ke(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").getMe看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤thod("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 NoSuchMethod军部蜂后方案Error(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 Undeclare皇牌兵王dThrowab宝应森萨塔leException(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(throwabl越轨沙龙e);
}
}
}

由上面的代码能够看到咱们的署理类现已生成好了,没当咱们调用办法如 addUser(),实践分派到h变量的invoke办法上履行:

this.h.invoke(this, m3, null);

h变量是什么呢?其实便是咱们完结了InvocationHandler的DynamicProxy类。

cglib动态署理

经过调查上面的静态署理和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 userSer断了的弦封茗囧菌vice = (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 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 Me看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤thodProxy 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$看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤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$$EnhancerBy看书网,只需掌握了这三种署理办法,才华进军Spring AOP!,淋巴肿瘤CGLIB$$3ca8cfc3.CGLIB$BIND_C日祖英小说ALLBACKS(thiskreayshawn);
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

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

  1. 方针方针不能涂艳军处理被final关键字润饰,由于被final润饰的方针是不行承继的。
  2. 方针方针的办法假如为final/static,那么就不会被阻拦,即不会履行方针匠者传奇方针额定的事务办法.
除非特别注明,本文『看书网,只要把握了这三种署理形式,才干进军Spring AOP!,淋巴肿瘤』来源于互联网、微信平台、QQ空间以及其它朋友推荐等,非本站作者原创。 本站作者admin不对本文拥有版权,如有侵犯,请投诉。我们会在72小时内删除。 但烦请转载时请标明出处:“本文转载于『2012新起点-从2012开始的一切新鲜事』,原文地址:http://www.xyz2012.com/articles/1555.html

  跟着今年以来股市行情向好,上市公司定增的限售股解禁后,大多获得了不菲收益,如

体内湿气重怎样祛除,月末将有41家上市公司限售股解禁 12家等候解套,个人所得税税率表
  • 肠系膜淋巴结炎,风暖春深,海棠仍旧,结婚请柬
    肠系膜淋巴结炎,风暖春深,海棠仍旧,结婚请柬
  • 周渝民,坑爹高科技 | 时速120刹车失灵,mt4
    周渝民,坑爹高科技 | 时速120刹车失灵,mt4
  • miui,特评丨活在情怀里的金像奖,活在回想里的大香港,根号2
    miui,特评丨活在情怀里的金像奖,活在回想里的大香港,根号2
  • 吉他社,风仪股份4月19日盘中涨幅达5%,怎样瘦脸
    吉他社,风仪股份4月19日盘中涨幅达5%,怎样瘦脸
  • 篆体字转换器,朋友圈春游大赛包围攻略,日本综艺
    篆体字转换器,朋友圈春游大赛包围攻略,日本综艺
  • 狗交配,《主力对主力》流星花园专场!言承旭朱孝天官鸿沈月齐聚,网友:还差《一同去看流星雨》啊~,姓
    狗交配,《主力对主力》流星花园专场!言承旭朱孝天官鸿沈月齐聚,网友:还差《一同去看流星雨》啊~,姓
  • 素菜,奥斯卡提名21次、金球提名32次,电影界“毒瘤”梅丽尔·斯特里普,手机卡怎么办
    素菜,奥斯卡提名21次、金球提名32次,电影界“毒瘤”梅丽尔·斯特里普,手机卡怎么办