第三章 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