关键字: spring, aop
一、基础接口和类
1、Person接口的源码
- public interface Person {
- public void info();
- public void show(String message);
- }
public interface Person {
public void info();
public void show(String message);
}
2、PersonImpl类的源码
- public class PersonImpl implements Person {
- private String name;
- private int age;
- public void setName(String name) {
- this.name = name;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public void info() {
- System.out.println("/t我叫" + name + ",今年" + age + "岁。");
- }
- public void show(String message) {
- System.out.println(message);
- }
- }
public class PersonImpl implements Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void info() {
System.out.println("/t我叫" + name + ",今年" + age + "岁。");
}
public void show(String message) {
System.out.println(message);
}
}
3、bean的配置
- <!-- 目标对象 -->
- <bean id="personTarget" class="com.cjm.aop.PersonImpl">
- <property name="name" value="Raymond.chen"/>
- <property name="age" value="30"/>
- </bean>
二、Spring AOP支持的通知类型
一)环绕通知(Around advice)
实现环绕通知需要实现org.aopalliance.intercept.MethodInterceptor接口。
1、PersonAroundAdvice类的源码
- public class PersonAroundAdvice implements MethodInterceptor {
- public Object invoke(MethodInvocation invocation) throws Throwable {
- System.out.println("AroundAdvice:方法调用前");
- //不要忘记调用invocation的proceed方法哦
- Object result = invocation.proceed();
- System.out.println("AroundAdvice:方法调用后");
- return result;
- }
- }
public class PersonAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("AroundAdvice:方法调用前");
//不要忘记调用invocation的proceed方法哦
Object result = invocation.proceed();
System.out.println("AroundAdvice:方法调用后");
return result;
}
}
2、bean配置
- <bean id="personAroundAdvice" class="com.cjm.aop.PersonAroundAdvice"/>
- <!-- 代理工厂bean -->
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personAroundAdvice</value>
- </list>
- </property>
- </bean>
3、测试代码
- ApplicationContext context = new FileSystemXmlApplicationContext("classpath:com/cjm/aop/beans.xml");
- Person p = (Person)context.getBean("person");
- p.info();
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:com/cjm/aop/beans.xml");
Person p = (Person)context.getBean("person");
p.info();
二)前置通知(Before advice)
实现前置通知需要实现org.springframework.aop.MethodBeforeAdvice接口。
1、PersonBeforeAdvice类的源码
- public class PersonBeforeAdvice implements MethodBeforeAdvice {
- public void before(Method method, Object[] args, Object target) throws Throwable {
- System.out.println("BeforeAdvice:方法调用前");
- }
- }
public class PersonBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("BeforeAdvice:方法调用前");
}
}
2、bean配置
- <bean id="personBeforeAdvice" class="com.cjm.aop.PersonBeforeAdvice"/>
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personBeforeAdvice</value>
- </list>
- </property>
- </bean>
三)返回后通知(After Returning advice)
实现返回后通知需要实现org.springframework.aop.AfterReturningAdvice接口。
1、PersonAfterReturningAdvice类的源码
- public class PersonAfterReturningAdvice implements AfterReturningAdvice {
- public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
- System.out.println("AfterReturningAdvice:方法调用后");
- }
- }
public class PersonAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("AfterReturningAdvice:方法调用后");
}
}
2、bean配置
- <bean id="personAfterReturningAdvice" class="com.cjm.aop.PersonAfterReturningAdvice"/>
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personAfterReturningAdvice</value>
- </list>
- </property>
- </bean>
3、以上的配置中,通知对目标对象的所有方法都会起作用。如果需要过滤掉一部分方法,可以用正则表达式切入点配置器或者方法名匹配切入点配置器实现。
- <!-- 通知与正则表达式切入点一起配置 -->
- <!-- Advisor等于切入点加通知 -->
- <!-- 方法名匹配切入点配置器:org.springframework.aop.support.NameMatchMethodPointcutAdvisor -->
- <bean id="personPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
- <property name="advice" ref="personAfterReturningAdvice"/>
- <property name="patterns">
- <list>
- <value>.*info.*</value>
- </list>
- </property>
- </bean>
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personPointcutAdvisor</value>
- </list>
- </property>
- </bean>
四)异常通知(Throws advice)
当连接点抛出异常时,异常通知被调用。实现异常通知需要实现org.springframework.aop.ThrowsAdvice接口,该接口不包含任何方法,但在实现该接口时必须实现如下形式的方法:
afterThrowing([Method], [args], [target], Throwable subclass)
可以实现一个或多个这样的方法。在这些方法中,只有第四个参数是必需的,前三个参数可选。
1、PersonThrowsAdvice类的源码
- public class PersonThrowsAdvice implements ThrowsAdvice {
- public void afterThrowing(FileNotFoundException ex){
- System.out.println("ThrowsAdvice >> FileNotFoundException:" + ex.toString());
- }
- public void afterThrowing(Object[] args, Exception ex){
- System.out.println("ThrowsAdvice >> Exception:" + ex.getMessage());
- }
- public void afterThrowing(Method method, Object[] args, Object target, Throwable ex){
- System.out.println("ThrowsAdvice >> Throwable:" + ex.getMessage());
- }
- }
public class PersonThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(FileNotFoundException ex){
System.out.println("ThrowsAdvice >> FileNotFoundException:" + ex.toString());
}
public void afterThrowing(Object[] args, Exception ex){
System.out.println("ThrowsAdvice >> Exception:" + ex.getMessage());
}
public void afterThrowing(Method method, Object[] args, Object target, Throwable ex){
System.out.println("ThrowsAdvice >> Throwable:" + ex.getMessage());
}
}
2、bean配置
- <bean id="personThrowsAdvice" class="com.cjm.aop.PersonThrowsAdvice"/>
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personThrowsAdvice</value>
- </list>
- </property>
- </bean>
五)引入通知(Introduction advice)
引入通知是一种特殊的通知,它能将新的成员变量、成员方法引入到目标类中。它不能作用于任何切入点,因为它只作用于类层次,而不是方法层次。实现引入通知需要实现IntroductionAdvisor和IntroductionInterceptor接口。
引入通知不能调用proceed方法。Advisor必须针对每个实例,并且是有状态的。
引入通知的效果类似于设计模式中的访问者模式(Visitor Pattern)。
1、Lockable接口的源码
- public interface Lockable {
- void lock();
- void unlock();
- boolean locked();
- }
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
2、LockableImpl类的源码
- public class LockableImpl extends DelegatingIntroductionInterceptor implements Lockable {
- private boolean locked;
- public void lock() {
- this.locked = true;
- }
- public void unlock() {
- this.locked = false;
- }
- public boolean locked() {
- return this.locked;
- }
- @Override
- public Object invoke(MethodInvocation invocation) throws Throwable {
- if(this.locked){
- throw new RuntimeException("加锁,无法执行");
- }
- //这里不能调用invocation的proceed方法
- //通常不需要改写invoke方法,直接调用父类的该方法即可
- return super.invoke(invocation);
- }
- }
public class LockableImpl extends DelegatingIntroductionInterceptor implements Lockable {
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if(this.locked){
throw new RuntimeException("加锁,无法执行");
}
//这里不能调用invocation的proceed方法
//通常不需要改写invoke方法,直接调用父类的该方法即可
return super.invoke(invocation);
}
}
3、PersonIntroductionAdvice类的源码
- public class PersonIntroductionAdvice extends DefaultIntroductionAdvisor {
- public PersonIntroductionAdvice(){
- super(new LockableImpl(), Lockable.class);
- }
- }
public class PersonIntroductionAdvice extends DefaultIntroductionAdvisor {
public PersonIntroductionAdvice(){
super(new LockableImpl(), Lockable.class);
}
}
4、bean配置
- <!-- Advice必须针对每个实例,所以scope要设为prototype -->
- <bean id="personIntroductionAdvice" class="com.cjm.aop.introduction.PersonIntroductionAdvice" scope="prototype"/>
- <bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
- <property name="proxyInterfaces" value="com.cjm.aop.Person"/>
- <property name="target" ref="personTarget"/>
- <property name="interceptorNames">
- <list>
- <value>personIntroductionAdvice</value>
- </list>
- </property>
- </bean>
5、测试代码
- ApplicationContext context = new FileSystemXmlApplicationContext("classpath:com/cjm/aop/beans.xml");
- //获得目标bean的代理bean
- Person p = (Person)context.getBean("person");
- //执行代理bean的方法,此时并未调用lock方法,可以执行
- p.info();
- Lockable lockable = (Lockable)p;
- lockable.lock();
- //目标bean已被锁定,此处将抛出异常
- p.info();