《一、静态代理匹配切面》
一、 RegexpMethodPointcutAdvisor 静态正则表达式匹配切面
目标类:
package com.pointcut;
/**
* @author JCL
* @date 2019-12-11 22:18
*/
public class GreetWaiter {
public void greeting(){
System.out.println("welcome to china");
}
}
前置增强类:
package com.pointcut;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* 前置增强类
* @author JCL
* @date 2019-12-11 22:24
*/
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("===执行了beforeAdvice增强类方法====");
}
}
xml配置:
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="greetingWaiter" class="com.pointcut.GreetWaiter"/>
<!--增强类bean-->
<bean id="beforeAdvice" class="com.pointcut.BeforeAdvice"/>
<!-- 切面-->
<bean id="regexAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="beforeAdvice"> <!--为切面添加增强类,增强类一般配合切面使用-->
<property name="patterns">
<list>
<!-- 配置目标类方法的正则表达式 -->
<value>.*greeting.*</value>
</list>
</property>
</bean>
<!-- p:pattern=".*greeting.*"/>-->
<bean id="waiterGreet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="regexAdvisor" <!--切面bean-->
p:target-ref="greetingWaiter" <!--代理目标类的bean-->
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
测试类:
@Test
public void test18(){
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext(new String[]{"pointcut.xml"});
//此时执行的不是目标类的bean了,而是代理类的bean
GreetWaiter greetWaiter=(GreetWaiter)context.getBean("waiterGreet");
greetWaiter.greeting();
}
结果输出:
===执行了beforeAdvice增强类方法====
welcome to china
二、StaticMethodMatcherPointcutAdvisor 静态普通方法名匹配切面
除了切面配置,其他类同上。
创建切面:
package com.pointcut;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import java.lang.reflect.Method;
/**
* @author JCL
* @date 2019-12-12 06:48
*/
public class MethodPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> aClass) {
if ("greeting".equals(method.getName())){
return true;
}
return false;
}
// 不覆盖此方法则是在所有类中寻找匹配方法
@Override
public ClassFilter getClassFilter() {
return new ClassFilter(){
@Override
public boolean matches(Class aClass) {
//isAssignableFrom方法判断两个类是否是父子类关系
return GreetWaiter.class.isAssignableFrom(aClass);
}
};
}
}
xml配置:
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="greetingWaiter" class="com.pointcut.GreetWaiter"/>
<!--增强类bean-->
<bean id="beforeAdvice" class="com.pointcut.BeforeAdvice"/>
<!-- 切面-->
<bean id="methodPointcutAdvisor" class="com.pointcut.MethodPointcutAdvisor"
p:advice-ref="beforeAdvice"/>
<bean id="waiterGreet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="methodPointcutAdvisor"
p:target-ref="greetingWaiter"
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
使用StaticMethodMatcherPointcutAdvisor需要写切面类,而使用 RegexpMethodMatcherPointcutAdvisor直接配置bean即可。
《二、动态代理匹配切面》
动态代理对性能影响比较大,所以一般都先用静态代理过滤以下再执行动态代理。
动态代理一般用 DynamicMethodMatcherPointcut创建切点和DefaultPointcutAdvisor创建切面配合使用
目标类:
package com.pointcut;
/**
* @author JCL
* @date 2019-12-11 22:18
*/
public class GreetWaiter {
public void greeting(){
System.out.println("welcome to china");
}
public void greeting2(String name){
System.out.println("welcome"+name+" to test 动态切点");
}
}
创建动态代理切点(not 切面):
package com.pointcut.DynamicPointcut;
import com.pointcut.GreetWaiter;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.DynamicMethodMatcherPointcut;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
/**
* 定义动态切点
* @author JCL
* @date 2019-12-12 07:46
*/
public class DynamicPointcut extends DynamicMethodMatcherPointcut {
//对类进行静态节点检查
public ClassFilter getClassFilter() {
return new ClassFilter(){
@Override
public boolean matches(Class<?> aClass) {
//判断是否是GreetWaiter类或其子类
return GreetWaiter.class.isAssignableFrom(aClass);
}
};
}
//对方法进行静态切点检查
public boolean matches(Method method, @Nullable Class<?> targetClass) {
return method.getName().equals("greeting2");
}
//进行动态切点检查
@Override
public boolean matches(Method method, Class<?> aClass, Object... objects) {
String arg=(String) objects[0];
return "john".equals(arg);
}
}
xml配置:
<?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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="greetingWaiter" class="com.pointcut.GreetWaiter"/>
<!--增强类bean-->
<bean id="beforeAdvice" class="com.pointcut.BeforeAdvice"/>
<!-- 切点-->
<bean id="dynamicPointcut" class="com.pointcut.DynamicPointcut.DynamicPointcut"/>
<!-- 切面-->
<bean id="dynamicPointcutAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="dynamicPointcut"
p:advice-ref="beforeAdvice"/>
<bean id="waiterGreet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="dynamicPointcutAdvisor"
p:target-ref="greetingWaiter"
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
测试类:
@Test
public void test19(){
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext(new String[]{"dynamicPointcut.xml"});
GreetWaiter greetWaiter=(GreetWaiter)context.getBean("waiterGreet");
greetWaiter.greeting2("john");
}
输出:
===执行了beforeAdvice增强类方法====
welcomejohn to test 动态切点
《三、流程切面》
流程切点:由某个方法直接或间接发起调用的其他方法。
我的理解:对某个类所有或部分方法的代理。
流程切面和动态切面一样,都性能影响很大。
要代理的类:
package com.pointcut;
/**
* @author JCL
* @date 2019-12-11 22:18
*/
public class GreetWaiter {
public void greeting(){
System.out.println("welcome to china");
}
public void greeting2(String name){
System.out.println("welcome "+name+" 执行了greeting2方法");
}
}
代理类:
package com.pointcut.controlFlowPointcut;
import com.pointcut.GreetWaiter;
/**
* @author JCL
* @date 2019-12-22 11:04
*/
public class WaiterDelegate {
private GreetWaiter greetWaiter;
public void services(String name){
greetWaiter.greeting();
greetWaiter.greeting2(name);
}
public void setGreetWaiter(GreetWaiter greetWaiter) {
this.greetWaiter = greetWaiter;
}
}
xml配置:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="greetingWaiter" class="com.pointcut.GreetWaiter"/>
<!--增强类bean-->
<bean id="beforeAdvice" class="com.pointcut.BeforeAdvice"/>
<!-- 流程切点-->
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
<constructor-arg type="java.lang.Class" value="com.pointcut.controlFlowPointcut.WaiterDelegate"/>
<constructor-arg type="java.lang.String" value="services"/>
</bean>
<!-- 切面-->
<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="controlFlowPointcut"
p:advice-ref="beforeAdvice"/>
<bean id="waiterGreet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="controlFlowAdvisor"
p:target-ref="greetingWaiter"
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
测试类:
@Test
public void test22(){
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext(new String[]{"controlFlowPointcut.xml"});
GreetWaiter greetWaiter=(GreetWaiter)context.getBean("waiterGreet");
greetWaiter.greeting();
greetWaiter.greeting2("ff");
WaiterDelegate waiterDelegate=new WaiterDelegate();
waiterDelegate.setGreetWaiter(greetWaiter);
waiterDelegate.services("xuehuan");
}
输出:
welcome to china
welcome ff 执行了greeting2方法
===执行了beforeAdvice增强类方法====
welcome to china
===执行了beforeAdvice增强类方法====
welcome 滴滴 执行了greeting2方法
可以看到直接调用 GreetWaiter类的两个方法无法织入增强,
调用WaiterDelegate#services可以对GreetWaiter类的方法织入增强
《四、复合切点切面》
复合切点:由多个单独的切点共同确定的切点
以上例中流程切点为例,上例中只有一个切点,现对其增加一个切点,使用复合切点。
被代理的类:
package com.pointcut;
/**
* @author JCL
* @date 2019-12-11 22:18
*/
public class GreetWaiter {
public void greeting(){
System.out.println("welcome to china");
}
public void greeting2(String name){
System.out.println("welcome "+name+" 执行了greeting2方法");
}
}
复合切点类:
package com.pointcut.composablePointcut;
import com.pointcut.controlFlowPointcut.WaiterDelegate;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;
/**
* @author JCL
* @date 2019-12-22 13:25
*/
public class GreetingComposablePointcut {
//类型返回是Pointcut类型的
public Pointcut getComposablePointcut(){
//创建一个复合切点
ComposablePointcut composablePointcut=new ComposablePointcut();
//创建一个流程切点
Pointcut flowPointcut=new ControlFlowPointcut(WaiterDelegate.class,"services");
//创建一个方法切点
NameMatchMethodPointcut methodPointcut=new NameMatchMethodPointcut();
Pointcut pointcut2 =methodPointcut.addMethodName("greeting");
//intersection是求多个切点的交集,参数必须是Pointcut类,所以必须转以下
return composablePointcut.intersection(flowPointcut).intersection(pointcut2);
}
}
xml配置:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="greetingWaiter" class="com.pointcut.GreetWaiter"/>
<!--增强类bean-->
<bean id="beforeAdvice" class="com.pointcut.BeforeAdvice"/>
<!-- 复合切点-->
<bean id="composablePointcut" class="com.pointcut.composablePointcut.GreetingComposablePointcut"/>
<!-- 切面-->
<!-- 此处是p:pointcut="" 而不是p:pointcut-ref="",引用GreetingComposablePointcut类的getComposablePointcut方法-->
<!-- 方法引入时#{....}的形式,若引入的是get方法则需要去除"get"直接写后面的-->
<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut="#{composablePointcut.composablePointcut}"
p:advice-ref="beforeAdvice"/>
<bean id="waiterGreet" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="composableAdvisor"
p:target-ref="greetingWaiter"
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
测试类:
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext(new String[]{"composablePointcut.xml"});
GreetWaiter greetWaiter=(GreetWaiter)context.getBean("waiterGreet");
WaiterDelegate waiterDelegate=new WaiterDelegate();
waiterDelegate.setGreetWaiter(greetWaiter);
waiterDelegate.services("滴滴");
执行结果:
===执行了beforeAdvice增强类方法====
welcome to china
welcome 滴滴 执行了greeting2方法
可以看到,只有执行了greeting方法时才会织入增强类,执行greeting2方法时增强类没被织入。
《五、引质切面》
原理:定义一个接口,然后把接口方法织入到目标类中
目标类:
package com.pointcut.controllablePointcut;
/**
* @author JCL
* @date 2019-12-22 22:29
*/
public class Performance {
public void test1(){
System.out.println("coming in test1 method");
}
public void test2(){
System.out.println("coming in test2 method");
}
}
定义接口方法:目的是在目标类中织入接口中方法
package com.pointcut.controllablePointcut;
/**
* @author JCL
* @date 2019-12-22 22:22
*/
public interface Monitor {
void setNeedMonitor(boolean needMonitor);
}
定义引质增强类:
继承: org.springframework.aop.support.DelegatingIntroductionInterceptor类,实现自定义的接口
package com.pointcut.controllablePointcut;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
/**
* 引质增强类
* @author JCL
* @date 2019-12-22 22:21
*/
public class ControllablePerformance extends DelegatingIntroductionInterceptor
implements Monitor{
private ThreadLocal<Boolean> monitorStatusMap=new ThreadLocal<Boolean>();
@Override
public void setNeedMonitor(boolean needMonitor) {
monitorStatusMap.set(needMonitor);
}
//此方法用于拦截目标类的所有方法的调用
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object obj=null;
if (monitorStatusMap.get() !=null && monitorStatusMap.get()) {
System.out.println("值为true, doSomthing...");
}
obj=super.invoke(mi);
return obj;
}
}
XML配置:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!--目标类bean-->
<bean id="performanceTarget" class="com.pointcut.controllablePointcut.Performance"/>
<!--增强类bean-->
<!-- <bean id="controllablePerformance" class="com.pointcut.controllablePointcut.ControllablePerformance"/>-->
<!--定义引质切面-->
<bean id="introductionAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
<constructor-arg>
<bean class="com.pointcut.controllablePointcut.ControllablePerformance"/>
</constructor-arg>
</bean>
<bean id="performance" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="introductionAdvisor"
p:target-ref="performanceTarget"
p:proxyTargetClass="true"/> <!--代理目标是否为类-->
</beans>
测试类:
@Test
public void test24(){
ClassPathXmlApplicationContext context=
new ClassPathXmlApplicationContext(new String[]{"introductionPointcut.xml"});
Performance performance=(Performance)context.getBean("performance");
performance.test1();
performance.test2();
//能强制转换为Monitor说明目标类确实通过引质增强实现类Monitor
Monitor monitor=(Monitor)performance;
monitor.setNeedMonitor(true);
performance.test1();
performance.test2();
}
结果输出:
coming in test1 method
coming in test2 method
值为true, doSomthing...
coming in test1 method
值为true, doSomthing...
coming in test2 method
说明引质切面成功了。