Spring AOP編程
一,概述:
Aop识颊, aspect object programming 面向切面編程
功能: 讓關注點代碼與業(yè)務代碼分離苍碟!
關注點,
重復代碼就叫做關注點;
切面寻狂,
關注點形成的類,就叫切面(類)萝映!
面向切面編程檩小,就是指 對很多功能都有的重復的代碼抽取,再在運行的時候網(wǎng)業(yè)務方法上動態(tài)植入“切面類代碼”杜耙。
切入點搜骡,
執(zhí)行目標對象方法,動態(tài)植入切面代碼佑女。
可以通過切入點表達式记靡,指定攔截哪些類的哪些方法; 給指定的類在運行的時候植入切面類代碼团驱。
1.1 手動實現(xiàn)aop編程
UserDao.java
// 保存一個用戶
public void add(User user) {
Session session = null;
Transaction trans = null;
try {
session = HibernateSessionFactoryUtils.getSession(); // 【關注點代碼】
trans = session.beginTransaction(); // 【關注點代碼】
session.save(user); // 核心業(yè)務代碼
trans.commit(); //…【關注點代碼】
} catch (Exception e) {
e.printStackTrace();
if(trans != null){
trans.rollback(); //..【關注點代碼】
}
} finally{
HibernateSessionFactoryUtils.closeSession(session); ////..【關注點代碼】
}
}
分析總結:
*關注點代碼摸吠,就是指重復執(zhí)行的代碼。
*業(yè)務代碼與關注點代碼分離嚎花,好處寸痢?
--→ 關注點代碼寫一次即可;
-→開發(fā)者只需要關注核心業(yè)務紊选;
-→運行時期啼止,執(zhí)行核心業(yè)務代碼時候動態(tài)植入關注點代碼; 【代理】
如何分離兵罢?
過程式/對象式/代理模式分離
所以,aop編程,可以理解為面向重復代碼編程.
1.2 xml方式實現(xiàn)aop編程
Xml實現(xiàn)aop編程:
1) 引入jar文件 【aop 相關jar族壳, 4個】
先引入aop相關jar文件 (aspectj aop優(yōu)秀組件) spring-beans-4.1.6.jar
spring-context-4.1.6.jar
spring-core-4.1.6.jar
spring-expression-4.1.6
spring-aop-4.1.6.RELEASE.jar 【spring4源碼】
aopalliance-1.0.jar 【spring2.5源碼/lib/aopalliance】
aspectjweaver.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
aspectjrt-1.8.1.jar 【spring2.5源碼/lib/aspectj】或【aspectj-1.8.2\lib】
commons-logging-1.1.3
2) 引入aop名稱空間
3)aop 配置
* 配置切面類 (重復執(zhí)行代碼形成的類)
* aop配置
攔截哪些方法 / 攔截到方法后應用通知代碼
1.3 創(chuàng)建工程,項目結構
其中的bean.xml就是applicationContext.xml文件
主要類的代碼:
IUserDao.java
package com.it.a_aop;
public interface IUserDao {
public void save();
}
UserDao.java,實現(xiàn)接口IUSerDao
package com.it.a_aop;
public class UserDao implements IUserDao{
/*
* AOP核心業(yè)務代碼和關注點代碼(重復代碼)分離
* */
@Override
public void save() {
System.out.println("---已經(jīng)保存數(shù)據(jù)----");//核心業(yè)務代碼
}
}
Admin.java,不實現(xiàn)接口IUserDao
package com.it.a_aop;
public class AdminDao {
public void save(){
System.out.println("---admin-save");
}
}
Aop.java,切面類(重點)
package com.it.a_aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class Aop {
/*
* 切面類
*
* 面向切面:面向重復代碼,把重復代碼放到一個類
* */
//重復執(zhí)行的代碼
//前置通知 : 在執(zhí)行目標方法之前執(zhí)行
public void begin() {
System.out.println("開始事務");
}
//后置/最終通知:在執(zhí)行目標方法之后執(zhí)行 【無論是否出現(xiàn)異常最終都會執(zhí)行】
public void after() {
System.out.println("提交事務/關閉");
}
//返回通知:在調用目標方法結束后執(zhí)行
public void afterReturning() {
System.out.println("afterReturning");
}
//異常通知,當目標方法執(zhí)行異常時候執(zhí)行此關注點代碼
public void afterThrowing() {
System.out.println("afterThrowing");
}
//環(huán)繞通知:環(huán)繞目標方法執(zhí)行
public void around(ProceedingJoinPoint pjd) throws Throwable{
System.out.println("環(huán)繞前");
pjd.proceed();//執(zhí)行目標方法
System.out.println("環(huán)繞后");
}
}
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="com.it.a_aop.UserDao"></bean>
<bean id="adminDao" class="com.it.a_aop.AdminDao"></bean>
<!-- 切面類 -->
<bean id="aop" class="com.it.a_aop.Aop"></bean>
<!-- aop配置 -->
<aop:config>
<!-- 定義一個切入點表達式,攔截哪些方法 -->
<aop:pointcut expression="execution(* com.it.a_aop.*.*(..))" id="pt"/>
<!-- 切面類 -->
<aop:aspect ref="aop">
<!-- 環(huán)繞通知 -->
<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>
App.java測試
package com.it.a_aop;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
@Test
public void dynamicProxyAop() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/it/a_aop/bean.xml");
IUserDao userDao = (IUserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
@Test
public void cglibProxyAop() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/it/a_aop/bean.xml");
AdminDao adminDao = (AdminDao) ac.getBean("adminDao");
System.out.println(adminDao.getClass());
adminDao.save();
}
}
運行測試方法:dynamicProxyAop()的結果
class com.sun.proxy.$Proxy6
環(huán)繞前
開始事務
---已經(jīng)保存數(shù)據(jù)----
環(huán)繞后
提交事務/關閉
afterReturning
根據(jù)打印的class信息,可以知道這是JDK的動態(tài)代理,UserDao實現(xiàn)了接口IUserDao;
運行測試方法cglibProxyAop()的結果
class com.it.a_aop.AdminDao$$EnhancerBySpringCGLIB$$871fa5a8
環(huán)繞前
開始事務
---admin-save
環(huán)繞后
提交事務/關閉
afterReturning
可以知道,這是cglib代理,AdminDao沒有實現(xiàn)接口.
1.3 注解方式實現(xiàn)aop
新建項目,目錄如下
IUserDao.java接口類不變
UserDao.java
package com.it.b_aop_anno;
import org.springframework.stereotype.Component;
@Component(value="userDao")
public class UserDao implements IUserDao{
/*
* AOP核心業(yè)務代碼和關注點代碼(重復代碼)分離
* */
@Override
public void save() {
System.out.println("---已經(jīng)保存數(shù)據(jù)----");//核心業(yè)務代碼
}
}
AdminDao.java
package com.it.b_aop_anno;
import org.springframework.stereotype.Component;
@Component(value="adminDao")
public class AdminDao {
public void save(){
System.out.println("---admin-save");
}
}
Aop.java(重點)
package com.it.b_aop_anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component(value="aop")
@Aspect //指定當前類為切面類
public class Aop {
/*
* 切面類
*
* 面向切面:面向重復代碼,把重復代碼放到一個類
* */
//重復執(zhí)行的代碼
//指定切入點表達式,攔截哪些方法:即為哪些類生產(chǎn)代理對象
@Pointcut("execution(* com.it.b_aop_anno.*.*(..))")
public void pointcut() {
}
//前置通知 : 在執(zhí)行目標方法之前執(zhí)行
@Before("pointcut()")
public void begin() {
System.out.println("開始事務");
}
//后置/最終通知:在執(zhí)行目標方法之后執(zhí)行 【無論是否出現(xiàn)異常最終都會執(zhí)行】
@After("pointcut()")
public void after() {
System.out.println("提交事務/關閉");
}
//返回通知:在調用目標方法結束后執(zhí)行
@AfterReturning("pointcut()")
public void afterReturning() {
System.out.println("afterReturning");
}
//異常通知,當目標方法執(zhí)行異常時候執(zhí)行此關注點代碼
@AfterThrowing("pointcut()")
public void afterThrowing() {
System.out.println("afterThrowing");
}
//環(huán)繞通知:環(huán)繞目標方法執(zhí)行
@Around("pointcut()")
public void around(ProceedingJoinPoint pjd) throws Throwable{
System.out.println("環(huán)繞前");
pjd.proceed();//執(zhí)行目標方法
System.out.println("環(huán)繞后");
}
}
bean.xml:開啟注冊掃描和開啟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">
<!--掃描該包com.it.b_aop_anno的注解-->
<context:component-scan base-package="com.it.b_aop_anno"></context:component-scan>
<!-- 開啟aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
測試類代碼App.java
package com.it.b_aop_anno;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
@Test
public void dynamicProxyAop() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/it/b_aop_anno/bean.xml");
IUserDao userDao = (IUserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());
userDao.save();
}
@Test
public void cglibProxyAop() {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("com/it/b_aop_anno/bean.xml");
AdminDao adminDao = (AdminDao) ac.getBean("adminDao");
System.out.println(adminDao.getClass());
adminDao.save();
}
}
運行測試方法:dynamicProxyAop()的結果
class com.sun.proxy.$Proxy14
環(huán)繞前
開始事務
---已經(jīng)保存數(shù)據(jù)----
環(huán)繞后
提交事務/關閉
afterReturning
根據(jù)打印的class信息,可以知道這是JDK的動態(tài)代理,UserDao實現(xiàn)了接口IUserDao;
運行測試方法cglibProxyAop()的結果
class com.it.b_aop_anno.AdminDao$$EnhancerBySpringCGLIB$$5e3ea260
環(huán)繞前
開始事務
---admin-save
環(huán)繞后
提交事務/關閉
afterReturning
可以知道,這是cglib代理,AdminDao沒有實現(xiàn)接口.
1.5 常見的切入點表達式:
切入點表達式,
可以對指定的“方法”進行攔截; 從而給指定的方法所在的類生層代理對象趣些。
<!-- 定義一個切入點表達式,攔截哪些方法 -->
<aop:pointcut expression="execution(* com.it.a_aop.*.*(..))" id="pt"/>
<!-- 攔截所有public方法 -->
<aop:pointcut expression="execution(public * *(..))" id="pt"/>
攔截所有save開頭方法
<aop:pointcut expression="execution(* save*(..))" id="pt"/>
攔截指定類的指定方法 ,要定位到方法
<aop:pointcut expression="execution(public * com.it.a_aop.UserDao.save(..))" id="pt"/>
攔截指定類所有方法
<aop:pointcut expression="execution(* com.it.a_aop.UserDao.*(..))" id=""/>
攔截指定包,以及其子包下的所有類的所有方法
<aop:pointcut expression="execution(* com..*.*(..))" id="pt"/>
多個表達式
<aop:pointcut expression="execution(* com.it.a_aop.UserDao.save() or execution(* com.it.a_aop.AdminDao.save()))" id="pt"/>
<aop:pointcut expression="execution(* com.it.a_aop.UserDao.save() || execution(* com.it.a_aop.AdminDao.save()))" id="pt"/>
以下的兩個且表達式?jīng)]有意義
<aop:pointcut expression="execution(* com.it.a_aop.UserDao.save() and execution(* com.it.a_aop.AdminDao.save()))" id="pt"/>
<aop:pointcut expression="execution(* com.it.a_aop.UserDao.save() && execution(* com.it.a_aop.AdminDao.save()))" id="pt"/>
非
<aop:pointcut expression="!execution(* com.it.a_aop.UserDao.save()" id="pt"/>
<aop:pointcut expression=" not execution(* com.it.a_aop.UserDao.save()" id="pt"/>