av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

面試官:說說你對SpringAOP的實現(xiàn)機制的理解!

AOP(Aspect Orient Programming),一般稱為面向切面編程,作為面向對象的一種補充,用于處理系統(tǒng)中分布于各個模塊的橫切關注點,比如事務管理、日志、緩存等等。AOP實現(xiàn)的關鍵在于AOP框架自動創(chuàng)建的AOP代理,AOP代理主要分為靜態(tài)代理和動態(tài)代理,靜態(tài)代理的代表為AspectJ;而動態(tài)代理則以Spring AOP為代表。靜態(tài)代理是編譯期實現(xiàn),動態(tài)代理是運行期實現(xiàn),可想而知前者擁有更好的性能。

創(chuàng)新互聯(lián)是一家專注于做網站、網站制作與策劃設計,昆都侖網站建設哪家好?創(chuàng)新互聯(lián)做網站,專注于網站建設十余年,網設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:昆都侖等地區(qū)。昆都侖做網站價格咨詢:028-86922220

本文主要介紹Spring AOP的兩種代理實現(xiàn)機制,JDK動態(tài)代理和CGLIB動態(tài)代理。

靜態(tài)代理是編譯階段生成AOP代理類,也就是說生成的字節(jié)碼就織入了增強后的AOP對象;動態(tài)代理則不會修改字節(jié)碼,而是在內存中臨時生成一個AOP對象,這個AOP對象包含了目標對象的全部方法,并且在特定的切點做了增強處理,并回調原對象的方法。

Spring AOP中的動態(tài)代理主要有兩種方式,JDK動態(tài)代理和CGLIB動態(tài)代理。JDK動態(tài)代理通過反射來接收被代理的類,并且要求被代理的類必須實現(xiàn)一個接口。JDK動態(tài)代理的核心是InvocationHandler接口和Proxy類。

如果目標類沒有實現(xiàn)接口,那么Spring AOP會選擇使用CGLIB來動態(tài)代理目標類。CGLIB(Code Generation Library),是一個代碼生成的類庫,可以在運行時動態(tài)的生成某個類的子類,注意,CGLIB是通過繼承的方式做的動態(tài)代理,因此如果某個類被標記為final,那么它是無法使用CGLIB做動態(tài)代理的,諸如private的方法也是不可以作為切面的。

我們分別通過實例來研究AOP的具體實現(xiàn)。

直接使用Spring AOP

首先定義需要切入的接口和實現(xiàn)。為了簡單起見,定義一個Speakable接口和一個具體的實現(xiàn)類,只有兩個方法sayHi()和sayBye()。

 
 
 
 
  1. public interface Speakable {  
  2.  void sayHi();  
  3.  void sayBye();  
  4. }  
  5. @Service  
  6. public class PersonSpring implements Speakable {  
  7.  @Override  
  8.  public void sayHi() {  
  9.  try {  
  10.  Thread.currentThread().sleep(30);  
  11.  } catch (Exception e) {  
  12.  throw new RuntimeException(e);  
  13.  }  
  14.  System.out.println("Hi!!");  
  15.  }  
  16.  @Override  
  17.  public void sayBye() {  
  18.  try {  
  19.  Thread.currentThread().sleep(10);  
  20.  } catch (Exception e) {  
  21.  throw new RuntimeException(e);  
  22.  }  
  23.  System.out.println("Bye!!");  
  24.  }  

接下來我們希望實現(xiàn)一個記錄sayHi()和sayBye()執(zhí)行時間的功能。

定義一個MethodMonitor類用來記錄Method執(zhí)行時間

 
 
 
 
  1. public class MethodMonitor {  
  2.  private long start;  
  3.  private String method;  
  4.  public MethodMonitor(String method) {  
  5.  this.method = method;  
  6.  System.out.println("begin monitor..");  
  7.  this.start = System.currentTimeMillis();  
  8.  }  
  9.  public void log() {  
  10.  long elapsedTime = System.currentTimeMillis() - start;  
  11.  System.out.println("end monitor..");  
  12.  System.out.println("Method: " + method + ", execution time: " + elapsedTime + " milliseconds.");  
  13.  }  

光有這個類還是不夠的,希望有個靜態(tài)方法用起來更順手,像這樣

 
 
 
 
  1. MonitorSession.begin();  
  2. doWork();  
  3. MonitorSession.end(); 

說干就干,定義一個MonitorSession

 
 
 
 
  1. public class MonitorSession {  
  2.  private static ThreadLocal monitorThreadLocal = new ThreadLocal<>();  
  3.  public static void begin(String method) {  
  4.  MethodMonitor logger = new MethodMonitor(method);  
  5.  monitorThreadLocal.set(logger);  
  6.  }  
  7.  public static void end() {  
  8.  MethodMonitor logger = monitorThreadLocal.get();  
  9.  logger.log();  
  10.  }  

萬事具備,接下來只需要我們做好切面的編碼,

 
 
 
 
  1. @Aspect  
  2. @Component  
  3. public class MonitorAdvice {  
  4.  @Pointcut("execution (* com.deanwangpro.aop.service.Speakable.*(..))")  
  5.  public void pointcut() {  
  6.  }  
  7.  @Around("pointcut()")  
  8.  public void around(ProceedingJoinPoint pjp) throws Throwable {  
  9.  MonitorSession.begin(pjp.getSignature().getName());  
  10.  pjp.proceed();  
  11.  MonitorSession.end();  
  12.  }  

如何使用?我用了spring boot,寫一個啟動函數(shù)吧。

 
 
 
 
  1. @SpringBootApplication  
  2. public class Application {  
  3.  @Autowired  
  4.  private Speakable personSpring;  
  5.  public static void main(String[] args) {  
  6.  SpringApplication.run(Application.class, args);  
  7.  }  
  8.  @Bean  
  9.  public CommandLineRunner commandLineRunner(ApplicationContext ctx) {  
  10.  return args -> {  
  11.  // spring aop  
  12.  System.out.println("******** spring aop ******** ");  
  13.  personSpring.sayHi();  
  14.  personSpring.sayBye();  
  15.  System.exit(0);  
  16.  };  
  17.  }  

運行后輸出: 

 
 
 
 
  1. jdk dynamic proxy begin monitor..Hi!!end monitor..Method: sayHi, execution time: 32 milliseconds.begin monitor..Bye!!end monitor..Method: sayBye, execution time: 22 milliseconds. 

JDK動態(tài)代理

剛剛的例子其實內部實現(xiàn)機制就是JDK動態(tài)代理,因為Person實現(xiàn)了一個接口。

為了不和***個例子沖突,我們再定義一個Person來實現(xiàn)Speakable, 這個實現(xiàn)是不帶Spring Annotation的,所以他不會被Spring托管。

 
 
 
 
  1. public class PersonImpl implements Speakable {  
  2.  @Override  
  3.  public void sayHi() {  
  4.  try {  
  5.  Thread.currentThread().sleep(30);  
  6.  } catch (Exception e) {  
  7.  throw new RuntimeException(e);  
  8.  }  
  9.  System.out.println("Hi!!");  
  10.  }  
  11.  @Override  
  12.  public void sayBye() {  
  13.  try {  
  14.  Thread.currentThread().sleep(10);  
  15.  } catch (Exception e) {  
  16.  throw new RuntimeException(e);  
  17.  }  
  18.  System.out.println("Bye!!");  
  19.  }  

重頭戲來了,我們需要利用InvocationHandler實現(xiàn)一個代理,讓它去包含Person這個對象。那么再運行期實際上是執(zhí)行這個代理的方法,然后代理再去執(zhí)行真正的方法。所以我們得以在執(zhí)行真正方法的前后做一些手腳。JDK動態(tài)代理是利用反射實現(xiàn),直接看代碼。

 
 
 
 
  1. public class DynamicProxy implements InvocationHandler {  
  2.  private Object target;  
  3.  public DynamicProxy(Object object) {  
  4.  this.target = object;  
  5.  }  
  6.  @Override  
  7.  public Object invoke(Object arg0, Method arg1, Object[] arg2)  
  8.  throws Throwable {  
  9.  MonitorSession.begin(arg1.getName());  
  10.  Object obj = arg1.invoke(target, arg2);  
  11.  MonitorSession.end();  
  12.  return obj;  
  13.  }  
  14.  @SuppressWarnings("unchecked")  
  15.  public  T getProxy() {  
  16.  return (T) Proxy.newProxyInstance(  
  17.  target.getClass().getClassLoader(),  
  18.  target.getClass().getInterfaces(),  
  19.  this  
  20.  );  
  21.  }  

通過getProxy可以得到這個代理對象,invoke就是具體的執(zhí)行方法,可以看到我們在執(zhí)行每個真正的方法前后都加了Monitor。

我實現(xiàn)了一個工廠類來獲取Person代理對象

 
 
 
 
  1. public class PersonProxyFactory {  
  2.  public static Speakable newJdkProxy() {  
  3.  // 代理PersonImpl  
  4.  DynamicProxy dynamicProxy = new DynamicProxy(new PersonImpl());  
  5.  Speakable proxy = dynamicProxy.getProxy();  
  6.  return proxy;  
  7.  }  

具體使用

 
 
 
 
  1. // jdk dynamic proxy  
  2. System.out.println("******** jdk dynamic proxy ******** ");  
  3. Speakable jdkProxy = PersonProxyFactory.newJdkProxy();  
  4. jdkProxy.sayHi();  
  5. jdkProxy.sayBye(); 

輸出結果:

 
 
 
 
  1. ******** jdk dynamic proxy ********   
  2. begin monitor..  
  3. Hi!!  
  4. end monitor..  
  5. Method: sayHi, execution time: 32 milliseconds.  
  6. begin monitor..  
  7. Bye!!  
  8. end monitor..  
  9. Method: sayBye, execution time: 22 milliseconds. 

CGLib動態(tài)代理

我們再新建一個Person來,這次不實現(xiàn)任何接口。

 
 
 
 
  1. public class Person {  
  2.  public void sayHi() {  
  3.  try {  
  4.  Thread.currentThread().sleep(30);  
  5.  } catch (Exception e) {  
  6.  throw new RuntimeException(e);  
  7.  }  
  8.  System.out.println("Hi!!");  
  9.  }  
  10.  public void sayBye() {  
  11.  try {  
  12.  Thread.currentThread().sleep(10);  
  13.  } catch (Exception e) {  
  14.  throw new RuntimeException(e);  
  15.  }  
  16.  System.out.println("Bye!!");  
  17.  }  

如果Spring識別到所代理的類沒有實現(xiàn)Interface,那么就會使用CGLib來創(chuàng)建動態(tài)代理,原理實際上成為所代理類的子類。

 
 
 
 
  1. public class CGLibProxy implements MethodInterceptor {  
  2.  private static CGLibProxy instance = new CGLibProxy();  
  3.  private CGLibProxy() {  
  4.  }  
  5.  public static CGLibProxy getInstance() {  
  6.  return instance;  
  7.  }  
  8.  private Enhancer enhancer = new Enhancer();  
  9.  @SuppressWarnings("unchecked")  
  10.  public  T getProxy(Class clazz) {  
  11.  enhancer.setSuperclass(clazz);  
  12.  enhancer.setCallback(this);  
  13.  return (T) enhancer.create();  
  14.  }  
  15.  @Override  
  16.  public Object intercept(Object arg0, Method arg1, Object[] arg2,  
  17.  MethodProxy arg3) throws Throwable {  
  18.  MonitorSession.begin(arg1.getName());  
  19.  Object obj = arg3.invokeSuper(arg0, arg2);  
  20.  MonitorSession.end();  
  21.  return obj;  
  22.  }  

類似的通過getProxy可以得到這個代理對象,intercept就是具體的執(zhí)行方法,可以看到我們在執(zhí)行每個真正的方法前后都加了Monitor。

在工廠類中增加獲得Person代理類的方法,

 
 
 
 
  1. public static Person newCglibProxy() {  
  2.  CGLibProxy cglibProxy = CGLibProxy.getInstance();  
  3.  Person proxy = cglibProxy.getProxy(Person.class);  
  4.  return proxy;  

具體使用

 
 
 
 
  1. // cglib dynamic proxy  
  2. System.out.println("******** cglib proxy ******** ");  
  3. Person cglibProxy = PersonProxyFactory.newCglibProxy();  
  4. cglibProxy.sayHi();  
  5. cglibProxy.sayBye(); 

輸出結果:

 
 
 
 
  1. begin monitor..Hi!!end monitor..Method: sayHi, execution time: 53 milliseconds.begin monitor..Bye!!end monitor..Method: sayBye, execution time: 14 milliseconds. 

小結

對比JDK動態(tài)代理和CGLib代理,在實際使用中發(fā)現(xiàn)CGLib在創(chuàng)建代理對象時所花費的時間卻比JDK動態(tài)代理要長,實測數(shù)據(jù)

 
 
 
 
  1. Method: newJdkProxy, execution time: 5 milliseconds.  
  2. Method: newCglibProxy, execution time: 18 milliseconds. 

所以CGLib更適合代理不需要頻繁實例化的類。

在具體方法執(zhí)行效率方面,理應是不通過反射的CGlib更快一些,然后測試結果并非如此,還需要高手指教。 

 
 
 
 
  1. JDKMethod: sayHi, execution time: 32 milliseconds.CGLibMethod: sayHi, execution time: 53 milliseconds.  

網站欄目:面試官:說說你對SpringAOP的實現(xiàn)機制的理解!
地址分享:http://uogjgqi.cn/article/djjeooj.html
掃二維碼與項目經理溝通

我們在微信上24小時期待你的聲音

解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯(lián)網交流