切入点的高级使用
1、使用控制流切入点
public class TestBean {
public void foo() {
System.out.println("foo()");
}
}
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
public class ControlFlowDemo {
public static void main(String... args) {
ControlFlowDemo ex = new ControlFlowDemo();
ex.run();
}
public void run() {
TestBean target = new TestBean();
Pointcut pc = new ControlFlowPointcut(ControlFlowDemo.class, "test");
Advisor advisor = new DefaultPointcutAdvisor(pc,
new SimpleBeforeAdvice());
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
TestBean proxy = (TestBean) pf.getProxy();
System.out.println("\tTrying normal invoke");
proxy.foo();
System.out.println("\n\tTrying under ControlFlowDemo.test()");
test(proxy);
}
private void test(TestBean bean) {
bean.foo();
}
}
2、使用组合切入点
import java.lang.reflect.Method;
import org.springframework.aop.Advisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcher;
public class ComposablePointcutDemo {
public static void main(String... args) {
GrammyGuitarist johnMayer = new GrammyGuitarist();
ComposablePointcut pc = new ComposablePointcut(ClassFilter.TRUE,
new SingMethodMatcher());
System.out.println("Test 1 >> ");
GrammyGuitarist proxy = getProxy(pc, johnMayer);
testInvoke(proxy);
System.out.println();
System.out.println("Test 2 >> ");
pc.union(new TalkMethodMatcher());
proxy = getProxy(pc, johnMayer);
testInvoke(proxy);
System.out.println();
System.out.println("Test 3 >> ");
pc.intersection(new RestMethodMatcher());
proxy = getProxy(pc, johnMayer);
testInvoke(proxy);
}
private static GrammyGuitarist getProxy(ComposablePointcut pc,
GrammyGuitarist target) {
Advisor advisor = new DefaultPointcutAdvisor(pc,
new SimpleBeforeAdvice());
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
return (GrammyGuitarist) pf.getProxy();
}
private static void testInvoke(GrammyGuitarist proxy) {
proxy.sing();
proxy.sing(new Guitar());
proxy.talk();
proxy.rest();
}
private static class SingMethodMatcher extends StaticMethodMatcher {
@Override
public boolean matches(Method method, Class<?> cls) {
return (method.getName().startsWith("si"));
}
}
private static class TalkMethodMatcher extends StaticMethodMatcher {
@Override
public boolean matches(Method method, Class<?> cls) {
return "talk".equals(method.getName());
}
}
private static class RestMethodMatcher extends StaticMethodMatcher {
@Override
public boolean matches(Method method, Class<?> cls) {
return (method.getName().endsWith("st"));
}
}
}
引入入门
当一个功能是横切的并且使用传统通知不易实现时,就需要动态添加该功能。可以通过IntroductionIntercepter接口来创建引入,该接口扩展了MethodInterceptor和DynamicIntroductionAdvice。使用addAdvisor()方法并传入IntroductionAdvisor接口的实例来创建引用。
使用引入进行对象修改检查
public interface IsModified {
boolean isModified();
}
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class IsModifiedMixin extends DelegatingIntroductionInterceptor
implements IsModified {
private boolean isModified = false;
private Map<Method, Method> methodCache = new HashMap<>();
@Override
public boolean isModified() {
return isModified;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (!isModified) {
if ((invocation.getMethod().getName().startsWith("set"))
&& (invocation.getArguments().length == 1)) {
Method getter = getGetter(invocation.getMethod());
if (getter != null) {
Object newVal = invocation.getArguments()[0];
Object oldVal = getter.invoke(invocation.getThis(),null);
if((newVal == null) && (oldVal == null)) {
isModified = false;
} else if((newVal == null) && (oldVal != null)) {
isModified = true;
} else if((newVal != null) && (oldVal == null)) {
isModified = true;
} else {
isModified = !newVal.equals(oldVal);
}
}
}
}
return super.invoke(invocation);
}
private Method getGetter(Method setter) {
Method getter = methodCache.get(setter);
if (getter != null) {
return getter;
}
String getterName = setter.getName().replaceFirst("set", "get");
try {
getter = setter.getDeclaringClass().getMethod(getterName, null);
synchronized (methodCache) {
methodCache.put(setter, getter);
}
return getter;
} catch (NoSuchMethodException ex) {
return null;
}
}
}
import org.springframework.aop.support.DefaultIntroductionAdvisor;
public class IsModifiedAdvisor extends DefaultIntroductionAdvisor {
public IsModifiedAdvisor() {
super(new IsModifiedMixin());
}
}
public class Contact {
private String name;
private String phoneNumber;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
public class IntroductionDemo {
public static void main(String... args) {
Contact target = new Contact();
target.setName("John Mayer");
IntroductionAdvisor advisor = new IsModifiedAdvisor();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
pf.setOptimize(true);
Contact proxy = (Contact) pf.getProxy();
IsModified proxyInterface = (IsModified)proxy;
System.out.println("Is Contact?: " + (proxy instanceof Contact));
System.out.println("Is IsModified?: " + (proxy instanceof IsModified));
System.out.println("Has been modified?: " +
proxyInterface.isModified());
proxy.setName("John Mayer");
System.out.println("Has been modified?: " +
proxyInterface.isModified());
proxy.setName("Eric Clapton");
System.out.println("Has been modified?: " +
proxyInterface.isModified());
}
}
AOP服务框架
- 使用ProxyFactoryBean
- 使用Spring aop名称空间
- 使用@AspectJ样式注解
使用ProxyFactoryBean
public class GrammyGuitarist {
public void sing() {
System.out.println("sing: Gravity is working against me\n" +
"And gravity wants to bring me down");
}
public void sing(Guitar guitar) {
System.out.println("play: " + guitar.play());
}
public void talk(){
System.out.println("talk");
}
}
public class Documentarist {
protected GrammyGuitarist guitarist;
public void execute() {
guitarist.sing();
guitarist.talk();
}
public void setGuitarist(GrammyGuitarist guitarist) {
this.guitarist = guitarist;
}
}
import org.aspectj.lang.JoinPoint;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class AuditAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("Executing: " + method);
}
}
import org.springframework.context.support.GenericXmlApplicationContext;
public class ProxyFactoryBeanDemo {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("spring/app-context-xml.xml");
ctx.refresh();
Documentarist documentaristOne = ctx.getBean("documentaristOne", Documentarist.class);
Documentarist documentaristTwo = ctx.getBean("documentaristTwo", Documentarist.class);
System.out.println("Documentarist One >>");
documentaristOne.execute();
System.out.println("\nDocumentarist Two >> ");
documentaristTwo.execute();
}
}
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="johnMayer" class="com.apress.prospring5.ch5.GrammyGuitarist"/>
<bean id="advice" class="com.apress.prospring5.ch5.AuditAdvice"/>
<bean id="documentaristOne" class="com.apress.prospring5.ch5.Documentarist"
p:guitarist-ref="proxyOne"/>
<bean id="proxyOne" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="johnMayer" p:interceptorNames-ref="interceptorAdviceNames"/>
<util:list id="interceptorAdviceNames">
<value>advice</value>
</util:list>
<bean id="documentaristTwo" class="com.apress.prospring5.ch5.Documentarist"
p:guitarist-ref="proxyTwo"/>
<bean id="proxyTwo" class="org.springframework.aop.framework.ProxyFactoryBean"
p:target-ref="johnMayer" p:interceptorNames-ref="interceptorAdvisorNames">
</bean>
<util:list id="interceptorAdvisorNames">
<value>advisor</value>
</util:list>
<bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:advice-ref="advice">
<property name="pointcut">
<bean class="org.springframework.aop.aspectj.AspectJExpressionPointcut"
p:expression="execution(* sing*(..))"/>
</property>
</bean>
</beans>
使用ProxyFactoryBean进行引入
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.framework.ProxyFactory;
public class IntroductionDemo {
public static void main(String... args) {
Contact target = new Contact();
target.setName("John Mayer");
IntroductionAdvisor advisor = new IsModifiedAdvisor();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvisor(advisor);
pf.setOptimize(true);
Contact proxy = (Contact) pf.getProxy();
IsModified proxyInterface = (IsModified)proxy;
System.out.println("Is Contact?: " + (proxy instanceof Contact));
System.out.println("Is IsModified?: " + (proxy instanceof IsModified));
System.out.println("Has been modified?: " +
proxyInterface.isModified());
proxy.setName("John Mayer");
System.out.println("Has been modified?: " +
proxyInterface.isModified());
proxy.setName("Eric Clapton");
System.out.println("Has been modified?: " +
proxyInterface.isModified());
}
}
使用aop名称空间
public class NewDocumentarist extends Documentarist {
@Override
public void execute() {
guitarist.sing();
Guitar guitar = new Guitar();
guitar.setBrand("Gibson");
guitarist.sing(guitar);
//guitarist.sing(new Guitar());
guitarist.talk();
}
}
public class SimpleAdvice {
public void simpleBeforeAdvice(JoinPoint joinPoint) {
System.out.println("Executing: " +
joinPoint.getSignature().getDeclaringTypeName() + " "
+ joinPoint.getSignature().getName());
}
}
public class AopNamespaceDemo {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
//ctx.load("classpath:spring/app-context-xml-01.xml");
//ctx.load("classpath:spring/app-context-xml-02.xml");
ctx.load("classpath:spring/app-context-xml-03.xml");
ctx.refresh();
NewDocumentarist documentarist = ctx.getBean("documentarist", NewDocumentarist.class);
documentarist.execute();
ctx.close();
}
}
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="singExecution"
expression="execution(* com.apress.prospring5.ch5..sing*(com.apress.prospring5.ch2.common.Guitar))"/>
<aop:aspect ref="advice">
<aop:before pointcut-ref="singExecution"
method="simpleBeforeAdvice"/>
</aop:aspect>
</aop:config>
<bean id="advice"
class="com.apress.prospring5.ch5.SimpleAdvice"/>
<bean id="johnMayer"
class="com.apress.prospring5.ch5.GrammyGuitarist"/>
<bean id="documentarist"
class="com.apress.prospring5.ch5.NewDocumentarist"
p:guitarist-ref="johnMayer"/>
</beans>
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="singExecution"
expression="execution(* sing*(com.apress.prospring5.ch2.common.Guitar)) and args(value) and bean(john*)"/>
<aop:aspect ref="advice">
<aop:before pointcut-ref="singExecution"
method="simpleBeforeAdvice"/>
</aop:aspect>
</aop:config>
<bean id="advice"
class="com.apress.prospring5.ch5.ComplexAdvice"/>
<bean id="johnMayer"
class="com.apress.prospring5.ch5.GrammyGuitarist"/>
<bean id="documentarist"
class="com.apress.prospring5.ch5.NewDocumentarist"
p:guitarist-ref="johnMayer"/>
</beans>
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config>
<aop:pointcut id="singExecution"
expression="execution(* sing*(com.apress.prospring5.ch2.common.Guitar)) and args(value) and bean(john*)"/>
<aop:aspect ref="advice">
<aop:before pointcut-ref="singExecution"
method="simpleBeforeAdvice"/>
<aop:around pointcut-ref="singExecution"
method="simpleAroundAdvice"/>
</aop:aspect>
</aop:config>
<bean id="advice"
class="com.apress.prospring5.ch5.ComplexAdvice"/>
<bean id="johnMayer"
class="com.apress.prospring5.ch5.GrammyGuitarist"/>
<bean id="documentarist"
class="com.apress.prospring5.ch5.NewDocumentarist"
p:guitarist-ref="johnMayer"/>
</beans>
使用<aop:around>声明环绕通知
public class ComplexAdvice {
public void simpleBeforeAdvice(JoinPoint joinPoint, Guitar value) {
if(value.getBrand().equalsIgnoreCase("Gibson")) {
System.out.println("Executing: " +
joinPoint.getSignature().getDeclaringTypeName() + " "
+ joinPoint.getSignature().getName());
}
}
public Object simpleAroundAdvice(ProceedingJoinPoint pjp, Guitar value) throws Throwable {
System.out.println("Before execution: " +
pjp.getSignature().getDeclaringTypeName() + " "
+ pjp.getSignature().getName()
+ " argument: " + value.getBrand());
Object retVal = pjp.proceed();
System.out.println("After execution: " +
pjp.getSignature().getDeclaringTypeName() + " "
+ pjp.getSignature().getName()
+ " argument: " + value.getBrand());
return retVal;
}
}
使用@AspectJ样式注解
@Component("johnMayer")
public class GrammyGuitarist implements Singer {
@Override public void sing() {
System.out.println("sing: Gravity is working against me\n" +
"And gravity wants to bring me down");
}
public void sing(Guitar guitar) {
System.out.println("play: " + guitar.play());
}
public void rest(){
System.out.println("zzz");
}
public void talk(){
System.out.println("talk");
}
}
@Component("documentarist")
public class NewDocumentarist {
protected GrammyGuitarist guitarist;
public void execute() {
guitarist.sing();
Guitar guitar = new Guitar();
guitar.setBrand("Gibson");
guitarist.sing(guitar);
guitarist.talk();
}
@Autowired
@Qualifier("johnMayer")
public void setGuitarist(GrammyGuitarist guitarist) {
this.guitarist = guitarist;
}
}
@Component
@Aspect
public class AnnotatedAdvice {
@Pointcut("execution(* com.apress.prospring5.ch5..sing*(com.apress.prospring5.ch2.common.Guitar)) && args(value)")
public void singExecution(Guitar value) {
}
@Pointcut("bean(john*)")
public void isJohn() {
}
@Before("singExecution(value) && isJohn()")
public void simpleBeforeAdvice(JoinPoint joinPoint, Guitar value) {
if(value.getBrand().equals("Gibson")) {
System.out.println("Executing: " +
joinPoint.getSignature().getDeclaringTypeName() + " "
+ joinPoint.getSignature().getName() + " argument: " + value.getBrand());
}
}
@Around("singExecution(value) && isJohn()")
public Object simpleAroundAdvice(ProceedingJoinPoint pjp, Guitar value) throws Throwable {
System.out.println("Before execution: " +
pjp.getSignature().getDeclaringTypeName() + " "
+ pjp.getSignature().getName()
+ " argument: " + value.getBrand());
Object retVal = pjp.proceed();
System.out.println("After execution: " +
pjp.getSignature().getDeclaringTypeName() + " "
+ pjp.getSignature().getName()
+ " argument: " + value.getBrand());
return retVal;
}
}
@Configuration
@ComponentScan(basePackages = {"com.apress.prospring5.ch5"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}
AspectJ集成
public class MessageWriter {
public void writeMessage() {
System.out.println("foobar!");
}
public void foo() {
System.out.println("foo");
}
}
MessageWrapper.aj文件:
public aspect MessageWrapper {
private String prefix;
private String suffix;
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public String getPrefix() {
return this.prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getSuffix() {
return this.suffix;
}
pointcut doWriting() :
execution(*
com.apress.prospring5.ch5.MessageWriter.writeMessage());
before() : doWriting() {
System.out.println(prefix);
}
after() : doWriting() {
System.out.println(suffix);
}
}
import org.springframework.context.support.GenericXmlApplicationContext;
public class AspectJDemo {
public static void main(String... args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:spring/app-context-xml.xml");
ctx.refresh();
MessageWriter writer = new MessageWriter();
writer.writeMessage();
writer.foo();
}
}