springAOP
前言:
靜態代理
靜態代理類:
1、原則:和目標方法類功能一致且和目標類實現同樣的接口。
2、缺點:- 往往在開發我們自己寫的不僅僅是一個業務,兩個業務,而我們的業務會有很多,如果每一個業務開發一個靜態代理類,不僅沒有減少我們的工作量,而且讓我們的工作量多了很多。
- 如何解決這個問題?能不能爲我們現有的業務層動態的創建代理類,通過動態代理去解決我們現有的業務層中的業務代碼冗餘問題。
動態代理
通過jdk提供的Proxy類,動態爲現有的業務生成代理類。
代碼示例:
/**
* JDK中的動態代理
* @author MOTUI
*
*/
public class TestUserServiceImplDynamicProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
//使用JDK提供的Proxy對象,用來產生動態代理類
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = {UserService.class};
InvocationHandler h = new InvocationHandler() {
//參數1:生成的代理對象
//參數2:當前調用的方法對象
//參數3:當前帶哦啊用方法參數
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
try {
System.out.println("開啓事務");
//調用真正的業務邏輯
Object invoke = method.invoke(userService, args);
System.out.println("提交事務");
return invoke;
} catch (Exception e) {
System.out.println("回滾事務");
e.printStackTrace();
}
return null;
}
};
//參數1:當前線程的類加載器 加載.class文件
//參數2:生成代理的對象的目標類的接口類型
//參數3:當通過代理對象去調方法時,會優先進入InvocationHander類的invoke方法
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(loader, interfaces, h);//返回靜態代理對象
//userServiceProxy.save();
String query = userServiceProxy.query("小王");
System.out.println(query);
}
}
學習AOP需要知道幾個重要的概念:
- 通知(Advice):處理目標方法以外的操作都稱之爲通知
- 切入點(PointCut):要爲哪些類中的哪些方法加入通知
切面(Aspect): 通知 + 切入點
前置通知:
代碼:public class RecordMethodNameBeforeAdvice implements MethodBeforeAdvice { /** * 參數1:當前目標類中調用的方法對象 * 參數2:當前目標類中調用的方法的參數 * 參數3:目標對象 */ @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("當前執行的方法:"+method.getName()); } }
配置文件:
<!-- 管理Bean --> <bean id="userService" class="com.motui.first.UserServiceImpl"/> <!-- 配置通知 --> <bean id="methodBeforeAdvice" class="com.motui.first.RecordMethodNameBeforeAdvice"></bean> <!-- 配置切面 --> <aop:config> <!-- 配置切入點 expression:指定哪個類的那個方法需要加入通知--> <aop:pointcut expression="execution(* com.motui.first.UserServiceImpl.*(..))" id="pc"/> <!-- 組合切入點和通知 --> <!-- advice-ref:告訴當前切面使用的是哪個通知 書寫通知bean的id pointcut-ref:當前通知加入哪個切入點 --> <aop:advisor advice-ref="methodBeforeAdvice" pointcut-ref="pc"/> </aop:config>
後置通知:
代碼:public class MyAfter implements AfterReturningAdvice ,ThrowsAdvice { //參數1: 目標類中當前執行的方法的返回值 //參數2: 目標類中當前執行的方法對象 //參數3: 目標類中當前執行的方法的參數 //參數4: 目標對象 @Override public void afterReturning(Object result, Method m, Object[] args, Object traget) throws Throwable { System.out.println("返回值:"+result); System.out.println("當前目標類的目標方法"+m.getName()); System.out.print("參數列表:"); if (args.length>0) { for (Object object : args) { System.out.println(object); } }else{ System.out.println("參數爲空"); } System.out.println("目標:"+traget); } //出現異常時進入的通知方法 public void afterThrowing(Method method, Object[] args, Object target,Exception ex){ System.out.println("異常通知的方法的名稱: "+method.getName()); System.out.println("異常通知的方法的參數: "+args); System.out.println("異常通知的目標對象"+target); System.out.println("異常通知的異常的信息 :" +ex.getMessage()); } }
配置文件:
<!-- 管理bean --> <bean id="deptService" class="com.motui.after.DeptServiceImpl"/> <bean id="myAfter" class="com.motui.after.MyAfter"/> <!-- 配置切入點 --> <aop:config> <aop:pointcut expression="execution(* com.motui.after.DeptServiceImpl.*(..))" id="pc"/> <aop:advisor advice-ref="myAfter" pointcut-ref="pc"/> </aop:config>
環繞通知:
代碼:/** * 記錄運行時長 * @author MOTUI * */ public class TestAroundAOP implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("記錄運行時長"); long start = new Date().getTime(); Object proceed = mi.proceed();//返回值爲目標方法的返回值,這個返回值在通知類中返回 long end = new Date().getTime(); System.out.println("運行時長:"+(end-start)); return proceed; } }
配置文件:
<!-- 管理Service --> <bean id="addressService" class="com.motui.around.AddressServiceImpl"/> <bean id="testAroundAOP" class="com.motui.around.TestAroundAOP"/> <!-- 配置切入點 --> <aop:config> <aop:pointcut expression="execution(* com.motui.around.AddressServiceImpl.*(..))" id="pc"/> <aop:advisor advice-ref="testAroundAOP" pointcut-ref="pc"/> </aop:config>
異常通知:
代碼在後置通知中。
execution表達式:
execution(返回值類型 包名.類名.方法名(參數列表)) 示例: execution(* com.motui.service.impl.UserServiceImpl.save(..)) 返回值:任意 包:com.motui.service.impl 類:UserServiceImpl 方法:save 參數:任意 execution(* com.motui.service.impl.UserServiceImpl.*(..)) 返回值:任意 包:com.motui.service.impl 類:UserServiceImpl 方法:任意方法 參數:任意
在spring中有兩種代理模式(JDK中的proxy和CGLIB):
在spring配置文件中可以通過proxy-target-class屬性指定,true是CGLIB代理;false是Proxy代理
總結:
Spring的核心思想
IOC:控制反轉 DI:依賴注入
AOP:面向切面的編程思想
AOP:通過動態爲現有項目中的目標類創建代理對象,解決項目中的一些通用問題。
AOP幾個重要概念:
通知(Advice):除了目標方法以外的操作稱之爲通知
切入點(Pointcut):項目中的哪些類的哪些方法需要加入通知
切面(Aspect):通知 + 切入點
AOP的底層實現
1、JDK的動態代理 Proxy 基於接口生成的代理
2、Spring CGBIL (CGLIB用來生成動態代理) 基於實現類生成的代理
3、JDK Proxy和CGLIB生成代理的區別?
Proxy根據接口生成動態代理對象,產生對象的返回值是接口類型
CGLIB根據實現類生成代理對象,產生對象的返回值是實現類類型(目標類型)