閱讀424 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

2com\.abc\.service\..*表示com.abc.service.包下所有的類的所有的方法。

Com.abc.service.a.User.setName()

Com.abc.service.UserService.save()

3com\.abc\.service\..*Service\..*表示com.abc.service包下以Service結尾的類的所有的方法。如:com.abc.service.UserService.save()

4com\.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(目標類)的兩個方法。

流程切麵使用ControlFlowPointcutDefaultPointcutAdvisor結合形成。

a、增強類

在這裏使用的是3.3.2的前置增強:

https://blog.csdn.net/p_3er/article/details/9239925

隻是UserDaoImpl中有兩個方法,save方法和delete方法。然後我們給save方法配置切麵。


bUserServiceImpl

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引介增強裏麵的東西(接口與增強類)

https://blog.csdn.net/p_3er/article/details/9239925

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

  上一篇:go 金庸筆下的愛情故事
  下一篇:go NHibernate - ICriteria 查詢