业务接口IHello.java
public interface IHello { public void toHello(String name); }
实现HelloImp.java
public class HelloImp implements IHello { public void toHello(String name) { System.out.println("hello:" + name); } }
以下是几种advices的实现:
1. 1. before advice 会在目标对象被调用之前执行的.before advice的实现代码:
LogBeforeAdvice.java
public class LogBeforeAdvice implements MethodBeforeAdvice { public void before(Method method, Object[] param, Object target) throws Throwable { System.out.println("method start..." + method.getName()); } }
接口MethodBeforeAdvice只有一个方法before需要实现,它定义了advice的实现.
before方法有三个参数,参数Method是advice开始后执行的方法.Object[]是传给被调用的参数数组,Object是执行方法m对象的引用.
2. after advice 会在目标对象被调用之后执行的.after advice的实现代码:
public class LogAfterAdvice implements AfterReturningAdvice { public void afterReturning(Object arg0, Method method, Object[] arg2, Object arg3) throws Throwable { System.out.println("method end..." + method.getName() + arg2[0]); } }
spring的配置:
<bean id="logBeforeAdvice" class="com.spring.advices.LogBeforeAdvice"></bean> <bean id="hello" class="com.dynamic.proxy.HelloImp"></bean> <bean id="logAfterAdvice" class="com.spring.advices.LogAfterAdvice"></bean> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.dynamic.proxy.IHello</value> </property> <property name="target"> <ref bean="hello"/> </property> <property name="interceptorNames"> <list> <value>logBeforeAdvice</value> <value>logAfterAdvice</value> </list> </property> </bean>
属性proxyInterface定义了接口类。
属性target指向本地配置的一个bean
属性interceptorNames是唯一允许定义一个值列表的属性.这个列表包含所有需要在beanTarget上执行的advice.
编写主方法的Java代码:
public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); IHello hello = (IHello)context.getBean("helloProxy"); hello.toHello("callan"); } }
每次toHell方法调用时,都会执行advice.也就是说调用toHello会先执行LogAfterAdvice,调用完后会执行LogAfterAdvice
以上是before advice和after advice的实现,也可以单独用MethodInterceptor来代替它们的功能.不同的是,在MethodInterceptor的invoke()方法中你要决定是否使用 proceed()方法来调用目标方法.
3. around advice的实现:
public class LogInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation method) throws Throwable { // TODO Auto-generated method stub System.out.println("start..."); Object obj = null; // method.proceed()会调用目示方法,在调用目录方法之前打印了start,之后打印了end,实现与before,after的组合功能 obj = method.proceed(); System.out.println("end..."); return obj; } }
配置与before,after相似
<bean id="helloProxy2" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.dynamic.proxy.IHello</value> </property> <property name="target"> <ref bean="hello"/> </property> <property name="interceptorNames"> <list> <value>logInterceptore</value> </list> </property> </bean>
before advice,after advice,around advice三种只定义了切入在代理接口执行前后执行,其实可以还可以更细的切入日志等
4. 有一个方便的类叫做NameMatchMethodPointcutAdvisor,它允许通过名称选择方法,只有匹配的方法才会加入日志.要想使用NameMatchMethodPointcutAdvisor,只需要修改配置.
<bean id="helloAdvice" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName"> <value>toHello</value> <!-- 方法名 --> </property> <property name="advice"> <ref bean="logBeforeAdvice"/> </property> </bean> <bean id="helloProxy3" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.dynamic.proxy.IHello</value> </property> <property name="target"> <ref bean="hello"/> </property> <property name="interceptorNames"> <list> <value>helloAdvice</value> </list> </property> </bean>
IHello接口中,只有toHello方法才能切入日志.
mappedName是定义匹配的方法名,还可以使用mappedNames定义方法列表
<property name="mappedNames">
<value>toHello</value> <!-- 方法名 -->
<value>toHello2</value> <!-- 方法名 -->
<value>toHello3</value> <!-- 方法名 -->
</property>
5. RegExpMethodPointcutAdvisor与NameMatchMethodPointcutAdvisor类例,只需要修改配置.
<bean id="reg" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="pattern"> <value>.*hello</value> </property> <property name="advice"> <list> <value>logInterceptore</value> </list> </property> </bean>
pattern属性符合完整类名加方法名称.比如IHello下的toHello方法,就要编写com.dynamic.proxy.IHello.toHello.
. 符合任何单一字符
+ 符合前一个字符一次或多次
* 符合前一个字符零次或多次
6.如果要为目标对象提供advice,必须要为其建立代理对象,如果程序规模很大时,一个个代理会很麻烦,spring提供了自动代理BeanNameAutoProxyCreator与 DefaultAdvisorAutoProxyCreator
BeanNameAutoProxyCreator:根据beanName进行自动代理.
spring配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="logBeforeAdvice" class="com.spring.advices.LogBeforeAdvice"></bean> <bean id="helloService" class="com.dynamic.proxy.HelloImp"> </bean> <bean id="beanNameAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Service</value> </list> </property> <property name="interceptorNames"> <value>logBeforeAdvice</value> </property> </bean> </beans>
为每个beanName为Service结属的bean提供自动的代理.
public class SpringDemo { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext("classpath:applicationContext.xml"); IHello hello = (IHello)context.getBean("helloService"); hello.toHello("callan"); } }
这样在toHello方法调用前也会执行logBeforeAdvice,以后只要想要为目标对象使用log advice时,只要取名为***Service就可以了