什么是AOP?
1.AOP概念介紹
所謂AOP,即Aspect orientied program,就是面向方面(切面)的編程。
功能: 讓關注點代碼與業務代碼分離!
關注點:
重復代碼就叫做關注點;
業務代碼:
核心業務的代碼
業務代碼與關注點代碼分離,好處?
--> 關注點代碼寫一次即可;
-->開發者只需要關注核心業務;
-->運行時期,執行核心業務代碼時候動態植入關注點代碼; 【代理】
如何分離?
過程式/對象式/代理模式分離
AOP的好處是可以動態地添加和刪除在切面上的邏輯而不影響原來的執行代碼
切面,
關注點形成的類,就叫切面(類)!
面向切面編程,就是指 對很多功能都有的重復的代碼抽取,再在運行的時候往業務方法上動態植入“切面類代碼”。
切入點(攔截的作用)
執行目標對象方法,動態植入切面代碼。
可以通過切入點表達式,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼。
注解方式實現AOP編程
步驟:
1) 先引入aop相關jar文件 (aspectj aop優秀組件)
spring-aop-3.2.5.RELEASE.jar 【spring3.2源碼】
aopalliance.jar 【spring2.5源碼/lib/aopalliance】
aspectjweaver.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
aspectjrt.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能會有問題。
需要升級aspectj組件,即使用aspectj-1.8.2版本中提供jar文件提供。
2) bean.xml中引入aop名稱空間
3) 開啟aop注解
4) 使用注解
@Aspect 指定一個類為切面類
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入點表達式
@Before("pointCut_()") 前置通知: 目標方法之前執行
@After("pointCut_()") 后置通知:目標方法之后執行(始終執行)
@AfterReturning("pointCut_()") 返回后通知: 執行方法結束前執行(異常不執行)
@AfterThrowing("pointCut_()") 異常通知: 出現異常時候執行
@Around("pointCut_()") 環繞通知: 環繞目標方法執行
代碼示例:
1、bean.xml中引入aop名稱空間
2、 開啟aop注解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 使用注解時要開啟注解掃描 要掃描的包 --> <context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan> <!-- 開啟aop注解方式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
3、指定切面類
@Component //加入IOC容器 @Aspect // 指定當前類為切面類 public class Aop { // 指定切入點表達式: 攔截哪些方法; 即為哪些類生成代理對象
//解釋@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))")
//@Pointcut("execution(* 切入點表達式固定寫法, cn.itcast.e_aop_anno表示包.類名(可以用*表示包下所有的類).方法名(可以用*表示類下所有的方法)(..)表示參數可以用..
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") public void pointCut_(){ }
//@Before("execution(* cn.itcast.e_aop_anno.*.*(..))")每個方法需要寫相同的引用,所以將相同的部分抽取到一個空的方法中pointCut_(), // 前置通知 : 在執行目標方法之前執行 @Before("pointCut_()") public void begin(){ System.out.println("開始事務/異常"); } // 后置/最終通知:在執行目標方法之后執行 【無論是否出現異常最終都會執行】 @After("pointCut_()") public void after(){ System.out.println("提交事務/關閉"); } // 返回后通知: 在調用目標方法結束后執行 【出現異常不執行】 @AfterReturning("pointCut_()") public void afterReturning() { System.out.println("afterReturning()"); } // 異常通知: 當目標方法執行異常時候執行此關注點代碼 @AfterThrowing("pointCut_()") public void afterThrowing(){ System.out.println("afterThrowing()"); } // 環繞通知:環繞目標方式執行 @Around("pointCut_()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("環繞前...."); pjp.proceed(); // 執行目標方法 System.out.println("環繞后...."); } }
接口
// 接口 public interface IUserDao { void save(); }
4、目標對象類一:實現接口
/** * 目標對象 */ @Component // 加入容器 public class UserDao implements IUserDao{ @Override public void save() { System.out.println("-----核心業務:保存!!!------"); } }
目標對象類二:沒有實現接口
/** * 目標對象*/ @Component // 加入容器 @Scope("prototype") public class OrderDao{ public void save() { System.out.println("-----核心業務:保存!!!------"); } }
測試類
public class App { ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/e_aop_anno/bean.xml"); // 目標對象有實現接口,spring會自動選擇“JDK代理” @Test public void testApp() { IUserDao userDao = (IUserDao) ac.getBean("userDao"); System.out.println(userDao.getClass());//$Proxy001 userDao.save(); } // 目標對象沒有實現接口, spring會用“cglib代理” @Test public void testCglib() { OrderDao orderDao = (OrderDao) ac.getBean("orderDao"); System.out.println(orderDao.getClass()); orderDao.save(); }
輸出結果:
開始事務/異常
-----核心業務:保存!!!------
提交事務/關閉
XML方式實現AOP編程
Xml實現aop編程:
1) 引入jar文件 【aop 相關jar, 4個】
2) 引入aop名稱空間
3)aop 配置
* 配置切面類 (重復執行代碼形成的類)
* aop配置
攔截哪些方法 / 攔截到方法后應用通知代碼
代碼示例:
bean.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- dao 實例 在這里配置后就不用在類中使用注解 --> <bean id="userDao" class="cn.itcast.f_aop_xml.UserDao"></bean> <bean id="orderDao" class="cn.itcast.f_aop_xml.OrderDao"></bean> <!-- 切面類 --> <bean id="aop" class="cn.itcast.f_aop_xml.Aop"></bean> <!-- Aop配置 --> <aop:config> <!-- 定義一個切入點表達式: 攔截哪些方法 --> <aop:pointcut expression="execution(* cn.itcast.f_aop_xml.*.*(..))" id="pt"/> <!-- 切面 --> <aop:aspect ref="aop"> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pt"/> <!-- 前置通知: 在目標方法調用前執行 --> <aop:before method="begin" pointcut-ref="pt"/> <!-- 后置通知: --> <aop:after method="after" pointcut-ref="pt"/> <!-- 返回后通知 --> <aop:after-returning method="afterReturning" pointcut-ref="pt"/> <!-- 異常通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/> </aop:aspect> </aop:config> </beans>
全部使用xml配置后,類中注解全部去掉,代碼同上
