在前面使用增强的时候,我们发现增强会被织入到目标类的所有的方法中。我们如果把增强织入到目标类的特定的方法中,需要使用切点进行目标连接点的定位。然后我们可以通过切点及增强生成一个切面了。
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的前置增强:
http://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 id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
<!-- 目标Bean -->
<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!-- 管理切面类。p:advice-ref把增强放入切面 -->
<bean id="staticAdvisor" class="cn.framelife.spring.advisor.StaticAdvisor" p:advice-ref="userDaoBeforeAdvice"></bean>
<!--
设置父代理类
p:interceptorNames 放切面,而不再是增强
-->
<bean id="paramProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="staticAdvisor"
p:proxyTargetClass="true" />
<!-- 设置子代理类 -->
<bean id="userDaoProxy" 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的前置增强:
http://blog.csdn.net/p_3er/article/details/9239925
b、配置
<!-- 增强Bean -->
<bean id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
<!-- 目标Bean -->
<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!--
设置切面Bean
patterns 用正则表达式定义目标类全限定方法名的匹配模式串
目标类全限定方法名,指的是带类名的方法名。如: cn.framelife.spring.dao.impl.UserDaoImpl.delete()
<value>.*delete</value> 匹配模式串
-->
<bean id="regexpAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="userDaoBeforeAdvice">
<property name="patterns">
<list>
<value>.*delete</value>
</list>
</property>
</bean>
<!--
设置代理类
-->
<bean id="regexpProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
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的前置增强:
http://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 id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
<!-- 目标Bean -->
<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!-- 切面 -->
<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<!-- 设置切点 -->
<property name="pointcut">
<bean class="cn.framelife.spring.pointcut.DynamicPointcut"></bean>
</property>
<!-- 织入增强 -->
<property name="advice" ref="userDaoBeforeAdvice"></property>
</bean>
<!--
设置代理
-->
<bean id="dynamicProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
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的前置增强:
http://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 id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
<!-- 目标Bean -->
<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!-- 切点 -->
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
<!-- 指定流程切点的类 -->
<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 id="controlFlowAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="controlFlowPointcut"
p:advice-ref="userDaoBeforeAdvice"/>
<!--
设置代理
-->
<bean id="controlFlowProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="controlFlowAdvisor"
p:target-ref="userDao"
p:proxyTargetClass="true" />
<!-- 把使用目标类的Bean交由Spring管理 -->
<bean id="userService" class="cn.framelife.spring.service.impl.UserServiceImpl">
<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的前置增强:
http://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 id="userDaoBeforeAdvice" class="cn.framelife.spring.advice.UserDaoBeforeAdvice"></bean>
<!-- 目标Bean -->
<bean id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!-- 切点所在类 -->
<bean id="myPoincut" class="cn.framelife.spring.pointcut.MyPointcut"></bean>
<!--
切面
p:pointcut #{ myPoincut.myComposablePointcut} 是由MyPointcut的getMyComposablePointcut方法获取的
-->
<bean id="composableAdvisor"
class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut="#{ myPoincut.myComposablePointcut}"
p:advice-ref="userDaoBeforeAdvice"/>
<!--
设置代理
-->
<bean id="composableProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="composableAdvisor"
p:target-ref="userDao"
p:proxyTargetClass="true" />
<!-- 把使用目标类的Bean交由Spring管理 -->
<bean id="userService" class="cn.framelife.spring.service.impl.UserServiceImpl">
<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 id="userDao" class="cn.framelife.spring.dao.impl.UserDaoImpl"></bean>
<!--
切面
constructor-arg 设置引介增强
-->
<bean id="introductionAdvisor"
class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg>
<bean class="cn.framelife.spring.advice.IntroductionAdvice"></bean>
</constructor-arg>
</bean>
<!--
设置代理
-->
<bean id="incluctionProxy"
class="org.springframework.aop.framework.ProxyFactoryBean"
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
保存用户...
方法执行后执行