JDK動(dòng)態(tài)代理在Spring AOP中的實(shí)現(xiàn)
動(dòng)態(tài)代理機(jī)制
通過(guò)實(shí)現(xiàn) InvocationHandler 接口創(chuàng)建自己的調(diào)用處理器
通過(guò)為 Proxy 類指定 ClassLoader 對(duì)象和一組 interface 來(lái)創(chuàng)建動(dòng)態(tài)代理類
通過(guò)反射機(jī)制獲得動(dòng)態(tài)代理類的構(gòu)造函數(shù)阶剑,其唯一參數(shù)類型是調(diào)用處理器接口類型
通過(guò)構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類實(shí)例猜谚,構(gòu)造時(shí)調(diào)用處理器對(duì)象作為參數(shù)被傳入
AOP思想
OOP中引入封裝锚国、繼承和多態(tài)性等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu)闻妓,用以模擬公共行為的一個(gè)集合。
AOP技術(shù)利用一種稱為“橫切”的技術(shù)却舀,解剖封裝的對(duì)象內(nèi)部夹厌,并將那些影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊列疗,這樣就能減少系統(tǒng)的重復(fù)代碼稽揭,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性肥卡。
AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)溪掀,業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)步鉴。
動(dòng)態(tài)代理源碼實(shí)現(xiàn) AopProxyFactory
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 表示在有接口實(shí)現(xiàn)的時(shí)候采用JDK動(dòng)態(tài)代理
return new JdkDynamicAopProxy(config);
}
// 在沒(méi)有接口實(shí)現(xiàn)的時(shí)候采用Cglib動(dòng)態(tài)代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
Spring中AOP的實(shí)現(xiàn)
程序自上而下執(zhí)行揪胃,與主業(yè)務(wù)邏輯的關(guān)系不大的橫切性問(wèn)題璃哟,aop面向切面編程,就是將主業(yè)務(wù)與橫切代碼分離喊递,做到解耦随闪。
常見(jiàn)實(shí)現(xiàn)有:
- 統(tǒng)一日志處理
- 統(tǒng)一異常處理
- Spring事務(wù)管理
代碼實(shí)現(xiàn)
定義接口
/**
* @ClassName: BuyService
* @Description: 購(gòu)買基礎(chǔ)接口
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:52
* @Version: 1.0
*/
public interface BuyService {
String buyPhone(BuyService buyService);
String buyComputer(BuyService buyService);
}
定義實(shí)現(xiàn)類
/**
* @ClassName: BusiServiceImpl
* @Description: 購(gòu)買實(shí)現(xiàn)類
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:53
* @Version: 1.0
*/
@Service
public class BuyServiceImpl implements BuyService {
@Intercept("buyPhone")
@Override
public String buyPhone(BuyService buyService) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========當(dāng)前類描述 " + buyService.getClass().getName() + "=============" + " buyPhone");
this.buyComputer(this);
return "buy phone";
}
@Intercept("buyComputer")
@Override
public String buyComputer(BuyService buyService) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========當(dāng)前類描述 " + buyService.getClass().getName() + "=============" + " buyComputer");
return "buy computer";
}
}
自定義攔截注解
/**
* @ClassName: Intercept
* @Description: 自定義攔截器注解
* @Author: 尚先生
* @CreateDate: 2019/6/17 16:28
* @Version: 1.0
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercept {
String value() default "";
}
自定義攔截器實(shí)現(xiàn)
/**
* @ClassName: Interceptor
* @Description: 攔截器實(shí)現(xiàn)類
* @Author: 尚先生
* @CreateDate: 2019/6/17 15:12
* @Version: 1.0
*/
@Component
@Aspect
public class Interceptor {
@Pointcut(value = "@annotation(com.learn.demo.java.proxy.Intercept)")
public void buySomething() {
System.out.println("===========自定義切入點(diǎn)===============");
}
@Around("buySomething()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
//通過(guò)獲取 Intercept 注解
Method proxyMethod = ((MethodSignature) point.getSignature()).getMethod();
Method targetMethod = point.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
Intercept intercept = targetMethod.getAnnotation(Intercept.class);
String methodName = targetMethod.getName();
//處理注解邏輯
String value = intercept.value();
System.err.println("=========== " + methodName + " 獲取前置攔截信息 ===========" + value);
return point.proceed();
} catch (Throwable e) {
System.out.println("執(zhí)行異常"+ e.getMessage());
}finally {
System.err.println("=========== " + " 后置處理結(jié)果返回 ===========");
}
return "執(zhí)行異常,請(qǐng)查看詳細(xì)日志信息";
}
}
自定義攔截器配置類
/**
* @ClassName: AspectJConfig
* @Description: 開(kāi)啟Spring對(duì)AspectJ的支持
* @Author: 尚先生
* @CreateDate: 2019/6/17 18:39
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.learn.demo.java.proxy")
@EnableAspectJAutoProxy
public class AspectJConfiguration {
}
啟動(dòng)引導(dǎo)類
/**
* @ClassName: Bootstrap
* @Description: 啟動(dòng)測(cè)試類
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:58
* @Version: 1.0
*/
public class Bootstrap {
public static void main(String[] args) {
// spring 采用的 jdk 動(dòng)態(tài)代理
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.learn.demo.java.proxy");
context.register(Interceptor.class);
context.refresh();
BuyService bean = context.getBean("buyServiceImpl", BuyService.class);
String phone = bean.buyPhone(bean);
System.err.println("=========Bootstrap.class============== " + phone);
// 輸出代理對(duì)象 class 文件
createProxyClassFile();
}
/**
* 生成代理文件
*/
private static void createProxyClassFile() {
String name = "ProxyBuyService";
byte[] data = ProxyGenerator.generateProxyClass(name,
new Class[] {BuyService.class});
FileOutputStream out = null;
try {
out = new FileOutputStream("D://" + name + ".class");
out.write(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != out)
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
執(zhí)行結(jié)果
=========== buyPhone 獲取前置攔截信息 ===========buyPhone
==========當(dāng)前類描述 com.sun.proxy.$Proxy22============= buyPhone
=========== 后置處理結(jié)果返回 ===========
=========Bootstrap.class============== buy phone
==========當(dāng)前類描述 com.learn.demo.java.proxy.BuyServiceImpl============= buyComputer
流程分析
從執(zhí)行結(jié)果中可以明確看到在buyPhone()中執(zhí)行的對(duì)象com.sun.proxy.$Proxy22骚勘,而后執(zhí)行this.buyComputer(this);執(zhí)行的對(duì)象變?yōu)閏om.learn.demo.java.proxy.BuyServiceImpl铐伴,所以在面向切面編程時(shí)只會(huì)攔截buyPhone()方法。
主要是生成的代理對(duì)象跟被代理對(duì)象不是同一個(gè)俏讹,所以后者調(diào)用類方法就像是直接調(diào)用当宴,不會(huì)走切面。
代碼已經(jīng)在GitHub中更新泽疆,更多詳情可關(guān)注dwyanewede户矢。更多JDK動(dòng)態(tài)代理,可參見(jiàn)上篇文章:JDK動(dòng)態(tài)代理實(shí)現(xiàn)原理
更多優(yōu)秀文章
java界的小學(xué)生
https://blog.csdn.net/shang_xs