面向切面編程
,經過預編譯方式
和運行期動態代理
實現程序功能的統一維護的一種技術。AOP是OOP(面向對象編程)的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程
的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離
,從而使得業務邏輯各部分之間的耦合度下降
,提升程序的可重用性
,同時提升了開發的效率。代理方式
向目標類織入加強代碼。特殊的通知
,在不修改類代碼的前提下,Introduction 能夠在運行期爲類動態地添加一些方法或Field。一個線是一個特殊的面。
一個切入點和一個通知,組成成一個特殊的面。
詳解如圖01:html
詳解如圖02:java
動態代理web
代理對象UserDaospring
package com.itzhouq.spring.demo1; public interface UserDao { public void save(); public void update(); public void find(); public void delete(); }
實現類express
package com.itzhouq.spring.demo1; public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存用戶"); } @Override public void update() { System.out.println("更新用戶"); } @Override public void find() { System.out.println("查找用戶"); } @Override public void delete() { System.out.println("刪除用戶"); } }
package com.itzhouq.spring.demo1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /* * 使用JDK動態代理對UserDao產生代理 */ public class JDKProxy implements InvocationHandler { // 將被加強的對象傳遞到代理總 private UserDao userDao; public JDKProxy(UserDao userDao) { this.userDao = userDao; } /* * 產生UserDao代理的方法 */ public UserDao createProxy() { UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance( userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 判斷方法名是否是save if("save".equals(method.getName())) { // 加強 System.out.println("權限校驗=============="); return method.invoke(userDao, args); } return method.invoke(userDao, args); } }
測試類編程
package com.itzhouq.spring.demo1; import org.junit.Test; public class SpringDemo1 { @Test //JDK動態代理 public void test1() { UserDao userDao = new UserDaoImpl(); // 建立代理 UserDao proxy = new JDKProxy(userDao).createProxy(); proxy.save(); proxy.find(); proxy.delete(); proxy.update(); //權限校驗============== //保存用戶 //查找用戶 //刪除用戶 //更新用戶 } }
目標類:沒有實現接口緩存
package com.itzhouq.spring.demo2; public class CustomerDao { public void save() { System.out.println("保存客戶"); } public void update() { System.out.println("更新客戶"); } public void find() { System.out.println("查找客戶"); } public void delete() { System.out.println("刪除客戶"); } }
package com.itzhouq.spring.demo2; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; /* * Cglib動態代理 */ public class CglibProxy implements MethodInterceptor { private CustomerDao customerDao; public CglibProxy(CustomerDao customerDao) { this.customerDao = customerDao; } // 使用Cglib產生代理的方法 public CustomerDao createProxy() { // 1. 建立cglib的核心類對象 Enhancer enhancer = new Enhancer(); // 2. 設置父類 enhancer.setSuperclass(customerDao.getClass()); // 3. 設置回調(相似於InvocationHandler對象) enhancer.setCallback(this); // 4. 建立代理對象 CustomerDao proxy = (CustomerDao) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { // 判斷方法是否爲save if("save".equals(method.getName())) { // 加強 System.out.println("權限校驗========"); return methodProxy.invokeSuper(proxy, arg); } return methodProxy.invokeSuper(proxy, arg); } }
package com.itzhouq.spring.demo2; import org.junit.Test; public class SpringDemo2 { /* * cglib的測試 */ @Test public void test1() { CustomerDao customerDao = new CustomerDao(); CustomerDao proxy = new CglibProxy(customerDao).createProxy(); proxy.save(); proxy.find(); proxy.update(); proxy.delete(); //權限校驗======== //保存客戶 //查找客戶 //更新客戶 //刪除客戶 } }
Spring AOP的底層是經過JDK動態代理或者Cglib動態代理技術爲目標bean執行橫向織入的。安全
Spring兩種開發方式app
除去基本的6個包,在web項目中添加aop開發的相關jar包框架
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> </beans>
目標接口
package com.itzhouq.spring.demo3; public interface ProductDao { public void save(); public void update(); public void find(); public void delete(); }
目標類
package com.itzhouq.spring.demo3; public class ProductDaoImpl implements ProductDao { @Override public void save() { System.out.println("保存商品"); } @Override public void update() { System.out.println("更新商品"); } @Override public void find() { System.out.println("查找商品"); } @Override public void delete() { System.out.println("刪除商品"); } }
配置目標對象
<!-- 配置目標對象:被加強的對象 --> <bean id="productDao" class="com.itzhouq.spring.demo3.ProductDaoImpl"></bean>
package com.itzhouq.spring.demo3; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /* * AOP入門 */ @RunWith(SpringJUnit4ClassRunner.class) //這兩個註釋就是Spring整合了JUnit單元測試 @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo3 { //注入productDao @Resource(name="productDao") private ProductDao productDao; @Test public void test1() { productDao.save(); productDao.update(); productDao.find(); productDao.delete(); // 保存商品 // 更新商品 // 查找商品 // 刪除商品 } }
package com.itzhouq.spring.demo3; /* * 切面類 */ public class MyAspectXML { public void checkPri() { System.out.println("權限校驗。。。"); } }
<!-- 將切面類交給Spring管理 --> <bean id="myAspect" class="com.itzhouq.spring.demo3.MyAspectXML"></bean>
<!-- 經過AOP的配置完成對目標類產生代理 --> <aop:config> <!-- 表達式配置哪些類的哪些方法須要進行加強 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <aop:before method="checkPri" pointcut-ref="pointcust1"/> </aop:aspect> </aop:config>
@RunWith(SpringJUnit4ClassRunner.class) //這兩個註釋就是Spring整合了JUnit單元測試 @ContextConfiguration("classpath:applicationContext.xml") public class SpringDemo3 { //注入productDao @Resource(name="productDao") private ProductDao productDao; @Test public void test1() { productDao.save(); productDao.update(); productDao.find(); productDao.delete(); // 權限校驗。。。 // 保存商品 // 更新商品 // 查找商品 // 刪除商品 } }
配置
<!-- 配置切面 --> <aop:aspect ref="myAspect"> <aop:before method="checkPri" pointcut-ref="pointcut1"/> </aop:aspect>
好比在切面類中加入參數JoinPoint
public class MyAspectXML { public void checkPri(JoinPoint joinPoint) { System.out.println("權限校驗。。。"+joinPoint); } }
測試打印的效果
// 權限校驗。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 刪除商品
配置後置通知
<!-- 經過AOP的配置完成對目標類產生代理 --> <aop:config> <!-- 表達式配置哪些類的哪些方法須要進行加強 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 後置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> </aop:aspect> </aop:config>
/* * 後置通知演示 */ public void writeLog(Object result) { System.out.println("日誌記錄======="+result); }
測試
權限校驗。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 刪除商品 // 日誌記錄=======kkk
/** * 性能監控 * @throws Throwable */ public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("環繞前通知==========="); Object obj = joinPoint.proceed();//這一步至關於執行目標程序 System.out.println("環繞後通知========="); return obj; }
配置環繞通知
<aop:config> <!-- 表達式配置哪些類的哪些方法須要進行加強 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 後置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pointcut3"/> </aop:aspect> </aop:config>
測試
// 權限校驗。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 環繞前通知=========== // 更新商品 // 環繞後通知========= // 查找商品 // 刪除商品 // 日誌記錄=======kkk
在find方法上模擬一個異常
@Override public void find() { System.out.println("查找商品"); int i = 1 / 0; }
/* * 異常拋出通知 */ public void afterThrowing(Throwable ex) { System.out.println("異常拋出通知=======" + ex); }
配置異常通知
<aop:config> <!-- 表達式配置哪些類的哪些方法須要進行加強 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 後置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> <!-- 環繞通知 --> <aop:around method="around" pointcut-ref="pointcut3"/> <!-- 異常拋出通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/> </aop:aspect> </aop:config>
測試
// 權限校驗。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 環繞前通知=========== // 更新商品 // 環繞後通知========= // 查找商品 // 異常拋出通知=======java.lang.ArithmeticException: / by zero
/* * 最終通知:至關於finally代碼塊中的內容 */ public void after() { System.out.println("最終通知========="); }
配置最終通知
<!-- 配置最終通知 --> <aop:after method="after" pointcut-ref="pointcut4"/>
1.execution() 用於描述方法【掌握】 語法:execution(修飾符 返回值 包.類.方法名(參數) throws異常) 修飾符,通常省略 public 公共方法 * 任意 返回值,不能省略 void 返回沒有值 String 返回值字符串 * 任意 包,[能夠省略] com.itheima.crm 固定的包 com.itheima.crm.*.service crm包下面的任意子包,固定目錄service(例如:com.itheima.crm.staff.service) com.itheima.crm.. crm包下面的全部子包(含本身) com.itheima.crm.*.service.. crm包下面的任意子包,固定目錄service,service目錄任意包(含本身) 類,[能夠省略] UserServiceImpl 指定的類 *Impl 以Impl結尾的類 User* 以User開頭的類 * 任意的類 方法名,不能省略 addUser 固定的方法名 add* 以add開頭的方法名 *Do 以Do結尾的方法名 * 任意的方法名 (參數) () 無參 (int) 一個整型 (int, int) 兩個整型 (..) 參數任意 throws,[能夠省略],通常省略。 綜合案例1: execution(* com.itheima.crm.*.service..*.*(..)) 綜合案例2: <aop:pointcut expression="execution(* com.itheima.*WithCommit.*(..)) || execution(* com.itheima.*Service.*(..))" id="myPointCut"/> 2.within:匹配包或子包中的方法(瞭解) within(com.itheima.aop..*) 3.this:匹配實現了接口的代理對象中的方法(瞭解) this(com.itheima.aop.user.UserDAO) 4.target:匹配實現了接口的目標對象中的方法(瞭解) target(com.itheima.aop.user.UserDAO) 5.args:匹配參數格式符合標準的方法(瞭解) args(int, int) 6.bean(id):對指定的bean全部的方法(瞭解) bean('userServiceId')