第三章 AOP 通過Java API創建切麵
在前麵使用增強的時候,我們發現增強會被織入到目標類的所有的方法中。我們如果把增強織入到目標類的特定的方法中,需要使用切點進行目標連接點的定位。然後我們可以通過切點及增強生成一個切麵了。
3.4.1切點類型
靜態方法切點:org.springframework.aop.support.StaticMethodMatcherPointcut
動態方法切點:org.springframework.aop.support.DynamicMethodMatcherPointcut
注解切點:org.springframework.aop.support.annotation.AnnotationMatchingPointcut
表達式切點:org.springframework.aop.support.ExpressionPointcut
流程切點:org.springframework.aop.support.ControlFlowPointcut
3.4.2靜態方法來匹配切麵
org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor
a、增強類
在這裏使用的是3.3.2的前置增強:
https://blog.csdn.net/p_3er/article/details/9239925
隻是UserDaoImpl中有兩個方法,save方法和delete方法。然後我們給save方法配置切麵。
b、切麵類繼承StaticMethodMatcherPointcutAdvisor
public class StaticAdvisor extends StaticMethodMatcherPointcutAdvisor { /* * 切點方法匹配規則 */ public boolean matches(Method method, Class<?> clazz) { return method.getName().equals("save"); } /* * 重寫StaticMethodMatcherPointcut的getClassFilter() * 匹配哪個類下的方法 */ public ClassFilter getClassFilter() { ClassFilter classFilter = new ClassFilter() { public boolean matches(Class<?> clazz) { return UserDaoImpl.class.isAssignableFrom(clazz); } }; return classFilter; } }
c、配置
<!-- 增強Bean --> <bean ></bean> <!-- 目標Bean --> <bean ></bean> <!-- 管理切麵類。p:advice-ref把增強放入切麵 --> <bean p:advice-ref="userDaoBeforeAdvice"></bean> <!-- 設置父代理類 p:interceptorNames 放切麵,而不再是增強 --> <bean p:interceptorNames="staticAdvisor" p:proxyTargetClass="true" /> <!-- 設置子代理類 --> <bean parent="paramProxy" p:target-ref="userDao"></bean>
d、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserDao userDao = (UserDao) context.getBean("userDaoProxy"); //UserDaoImpl中有兩個方法,save方法中有切麵,delete中沒切麵 userDao.save(); System.out.println("----------"); userDao.delete();
e、結果
我是前置增強:save 保存用戶... ---------- 刪除用戶...
3.4.3靜態正則表達式方法配置切麵
這種方法不需要自己寫切麵類。
org.springframework.aop.support.RegexpMethodPointcutAdvisor是正則表達式方法匹配的切麵實現類。
a、增強類
在這裏使用的是3.3.2的前置增強:
https://blog.csdn.net/p_3er/article/details/9239925
b、配置
<!-- 增強Bean --> <bean ></bean> <!-- 目標Bean --> <bean ></bean> <!-- 設置切麵Bean patterns 用正則表達式定義目標類全限定方法名的匹配模式串 目標類全限定方法名,指的是帶類名的方法名。如: cn.framelife.spring.dao.impl.UserDaoImpl.delete() <value>.*delete</value> 匹配模式串 --> <bean p:advice-ref="userDaoBeforeAdvice"> <property name="patterns"> <list> <value>.*delete</value> </list> </property> </bean> <!-- 設置代理類 --> <bean p:interceptorNames="regexpAdvisor" p:target-ref="userDao" p:proxyTargetClass="true" />
c、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserDao userDao = (UserDao) context.getBean("regexpProxy"); userDao.save(); System.out.println("----------"); userDao.delete();
d、結果
保存用戶... ---------- 我是前置增強:delete 刪除用戶...
常用的正則表達式規則
1、.*set.*表示所有類中的以set為前綴的方法。如:com.abc.UserDao.setName()。
2、com\.abc\.service\..*表示com.abc.service.包下所有的類的所有的方法。
Com.abc.service.a.User.setName()
Com.abc.service.UserService.save()
3、com\.abc\.service\..*Service\..*表示com.abc.service包下以Service結尾的類的所有的方法。如:com.abc.service.UserService.save()
4、com\.abc\.service\..*Service\.save.+匹配所有以save為前綴的方法,而且save後必須擁有一個或多個字符。如:com.abc.service.UserService.saveUser()
3.4.4動態切麵
org.springframework.aop.support.DynamicMethodMatcherPointcut類即有靜態切點檢查的方法,也有動態切點檢查的方法。由於動態切點檢查會對性能造成很大的影響,我們應當避免在運行時每次都對目標類的各個方法進行檢查。
Spring檢查動態切點的機製:在創建代理時對目標類的每個連接點進行靜態切點檢查,如果僅靜態切點檢查就可以知道連接點是不匹配的,在運行期就不進行動態檢查了;如果靜態切點檢查是匹配的,在運行期才進行動態方法檢查。
動態切麵通過DefaultPointcutAdvisor切麵類與DynamicMethodMatcherPointcut切點結合起來生成。主要是針對連接點方法的參數。
a、增強類
在這裏使用的是3.3.2的前置增強:
https://blog.csdn.net/p_3er/article/details/9239925
隻是UserDaoImpl中多了一個out方法,帶一個參數。
b、切點類繼承DynamicMethodMatcherPointcut
public class DynamicPointcut extends DynamicMethodMatcherPointcut { //用list集合保存參數名 private List<String> specialNames = new ArrayList<String>(); public DynamicPointcut(){ specialNames.add("aa"); specialNames.add("bb"); } /* * 對類進行靜態切點檢查 */ public ClassFilter getClassFilter() { return new ClassFilter() { @Override public boolean matches(Class<?> targetClass) { System.out.println("使用getClassFilter靜態檢查:"+targetClass.getName()); return UserDaoImpl.class.isAssignableFrom(targetClass); } }; } /* *對方法進行靜態切點檢查 */ public boolean matches(Method method, Class<?> targetClass) { System.out.println("使用matches(method,targetClass)方法靜態檢查:"+targetClass.getName()+"--"+method.getName()); return method.getName().equals("out"); } /* *對方法進行動態切點檢查 */ public boolean matches(Method method, Class<?> targetClass, Object[] args) { System.out.println("使用matches(method,targetClass)方法動態檢查:"+targetClass.getName()+"--"+method.getName()+"的參數"); String name = (String)args[0]; return specialNames.contains(name); } }
c、配置
<!-- 增強Bean --> <bean ></bean> <!-- 目標Bean --> <bean ></bean> <!-- 切麵 --> <bean > <!-- 設置切點 --> <property name="pointcut"> <bean ></bean> </property> <!-- 織入增強 --> <property name="advice" ref="userDaoBeforeAdvice"></property> </bean> <!-- 設置代理 --> <bean p:interceptorNames="dynamicAdvisor" p:target-ref="userDao" p:proxyTargetClass="true" />
d、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserDao userDao = (UserDao) context.getBean("dynamicProxy"); //UserDaoImpl中多了一個out方法,帶一個參數。 System.out.println("----------"); userDao.save(); System.out.println("----------"); userDao.out("aa"); System.out.println("----------"); userDao.out("1111"); System.out.println("----------");
e、結果
使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--out 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--save 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--delete 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--clone 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--toString ---------- 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--save 保存用戶... ---------- 使用getClassFilter靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl 使用matches(method,targetClass)方法靜態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--out 使用matches(method,targetClass)方法動態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--out的參數 我是前置增強:out out輸出名字為:aa ---------- 使用matches(method,targetClass)方法動態檢查:cn.framelife.spring.dao.impl.UserDaoImpl--out的參數 out輸出名字為:1111 ----------
3.4.5流程切麵
一個類中的某一方法使用目標類的兩個方法,那麼我們可以使用流程切麵給這兩個方法都積入增強。
如:UserServiceImpl類(使用類)operate方法中使用UserDaoImpl(目標類)的兩個方法。
流程切麵使用ControlFlowPointcut與DefaultPointcutAdvisor結合形成。
a、增強類
在這裏使用的是3.3.2的前置增強:
https://blog.csdn.net/p_3er/article/details/9239925
隻是UserDaoImpl中有兩個方法,save方法和delete方法。然後我們給save方法配置切麵。
b、UserServiceImpl類
public class UserServiceImpl implements UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void addUser() { userDao.save(); } @Override public void operate() { //這裏同時使用了userDao的兩個方法 userDao.delete(); userDao.save(); } }
c、配置
<!-- 增強Bean --> <bean ></bean> <!-- 目標Bean --> <bean ></bean> <!-- 切點 --> <bean > <!-- 指定流程切點的類 --> <constructor-arg type="java.lang.Class" value="cn.framelife.spring.service.impl.UserServiceImpl"></constructor-arg> <!-- 指定流程切點的方法 --> <constructor-arg type="java.lang.String" value="operate"></constructor-arg> </bean> <!-- 切麵 --> <bean p:pointcut-ref="controlFlowPointcut" p:advice-ref="userDaoBeforeAdvice"/> <!-- 設置代理 --> <bean p:interceptorNames="controlFlowAdvisor" p:target-ref="userDao" p:proxyTargetClass="true" /> <!-- 把使用目標類的Bean交由Spring管理 --> <bean > <property name="userDao" ref="controlFlowProxy"></property> </bean>
d、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService userService = (UserService) context.getBean("userService"); userService.operate(); System.out.println("----------------"); userService.addUser();
e、結果
我是前置增強:delete 刪除用戶... 我是前置增強:save 保存用戶... ---------------- 保存用戶...
3.4.6複合切麵
在前麵的例子中,我們所定義的切麵都隻有一個切點而已。有時候我們一個切麵需要多個切點,也就是多個條件才能決定連接點。多個切點組成一個切點,這樣的切點是複合切點。由複合切點加上增強形成的切麵,稱為複合切麵。
a、增強類
在這裏使用的是3.3.2的前置增強:
https://blog.csdn.net/p_3er/article/details/9239925
b、一個普通類中有一個獲取Pointcut的方法
public class MyPointcut { //獲取Pointcut的方法名是get開頭 public Pointcut getMyComposablePointcut(){ //創建一個複合切點 ComposablePointcut cp = new ComposablePointcut(); //創建一個流程切點(參數:使用類、類中的方法)這裏使用的是3.4.5中的UserServiceImpl類 Pointcut pt1 = new ControlFlowPointcut(UserServiceImpl.class,"operate"); //創建一個靜態方法切點 Pointcut pt2 = new StaticMethodMatcherPointcut() { public boolean matches(Method method, Class<?> clazz) { return UserDaoImpl.class.isAssignableFrom(clazz)&&method.getName().equals("delete"); } }; //兩個切點進行交集操作. return cp.intersection(pt1).intersection(pt2); } }
c、配置
<!-- 增強Bean --> <bean ></bean> <!-- 目標Bean --> <bean ></bean> <!-- 切點所在類 --> <bean ></bean> <!-- 切麵 p:pointcut #{ myPoincut.myComposablePointcut} 是由MyPointcut的getMyComposablePointcut方法獲取的 --> <bean p:pointcut="#{ myPoincut.myComposablePointcut}" p:advice-ref="userDaoBeforeAdvice"/> <!-- 設置代理 --> <bean p:interceptorNames="composableAdvisor" p:target-ref="userDao" p:proxyTargetClass="true" /> <!-- 把使用目標類的Bean交由Spring管理 --> <bean > <property name="userDao" ref="composableProxy"></property> </bean>
d、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserService userService = (UserService) context.getBean("userService"); userService.operate();
e、結果
我是前置增強:delete 刪除用戶... 保存用戶...
3.4.7引介切麵
a、使用3.3.6引介增強裏麵的東西(接口與增強類)
b、配置
<!-- 目標Bean --> <bean ></bean> <!-- 切麵 constructor-arg 設置引介增強 --> <bean > <constructor-arg> <bean ></bean> </constructor-arg> </bean> <!-- 設置代理 --> <bean p:interceptorNames="introductionAdvisor" p:target-ref="userDao" p:proxyTargetClass="true" />
c、測試
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"}); UserDao userDao = (UserDao) context.getBean("aProxy"); userDao.save(); System.out.println("-------------"); AInterface a = (AInterface)userDao; a.say(); System.out.println("-------------"); userDao.save();
d、結果
方法執行前執行 org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation save 保存用戶... 方法執行後執行 ------------- 方法執行前執行 org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation say UserDao要說話 方法執行後執行 ------------- 方法執行前執行 org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation save 保存用戶... 方法執行後執行
最後更新:2017-04-03 16:48:31