一,从代理机制初探AOP
1,使用静态代理
#代理对象和被代理对象必须实现共同的接口 package com.biaoflying; public interface IHello { public void hello(String name); } |
#代理对象HelloProxy package com.biaoflying; import java.util.logging.*; public class HelloProxy implements IHello { private IHello helloObject; private Logger logger= Logger.getLogger(this.getClass().getName()); public HelloProxy(IHello helloObject){ this.helloObject=helloObject; } public void hello(String name){ log("hello method starts...."); helloObject.hello(name); log("hello method ends...."); } private void log(String msg){ logger.log(Level.INFO,msg); } } |
#HelloSpeaker被代理对象 package com.biaoflying; public class HelloSpeaker implements IHello { public void hello(String name){ System.out.println("Hello "+name); } } |
#主程序 package com.biaoflying; public class ProxyDemo { public static void main(String[] args){ HelloProxy proxy= new HelloProxy(new HelloSpeaker()); proxy.hello("biaoflying"); } } |
#执行ProxyDemo的输出 信息: hello method starts.... Hello biaoflying 2008-3-21 9:05:40 com.biaoflying.HelloProxy log 信息: hello method ends.... |
TIP:使用静态代理,代理对象和被代理对象必须实现共同的接口。本质上就是由代理对象呼叫被代理对象并执行相关的逻辑。同时可以看到,静态代理在程序规模稍大时就无法胜任了。
-----------------------------------------------------------------------------------
2,动态代理
去掉HelloProxy.java,编写以下的LogHandler.java文件。
package com.biaoflying; import java.util.logging.*; import java.lang.reflect.*; public class LogHandler implements InvocationHandler{ private Logger logger= Logger.getLogger(this.getClass().getName()); private Object delegate; public Object bind(Object delegate){ this.delegate=delegate; return Proxy.newProxyInstance( delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this); } public Object invoke(Object proxy,Method method, Object[] args)throws Throwable{ Object result = null; try{ log("method starts..."+method); result=method.invoke(delegate,args); logger.log(Level.INFO,"method ends..."+method); }catch(Exception e){ log(e.toString()); } return result; } private void log(String msg){ logger.log(Level.INFO,msg); } } |
#主程序 package com.biaoflying; public class ProxyDemo2 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub LogHandler logHandler=new LogHandler(); IHello helloProxy= (IHello)logHandler.bind(new HelloSpeaker()); helloProxy.hello("biaoflying"); } } |
TIP:动态代理的执行流程是Proxy.newProxyInstance(..,..,this)方法返回一个代理对象,该代理对象每次执行相关的方法时都会委托给this.invoke方法。
-----------------------------------------------------------------------------------
二,Advices
由于织入Targets的时机不同,Advices被分为以下几种:Before advice,After advice,Around advice,Throw advice。
#MethodBeforeAdvice接口 package org.springframework.aop; public interface MethodBeforeAdvice extends BeforeAdvice{ void before(Method method,Object[] args,Object target) throws Throwable; } |
具体的例子,IHello接口和HelloSpeaker.java如上
package com.biaoflying; import java.lang.reflect.Method; import java.util.logging.*; import org.springframework.aop.MethodBeforeAdvice; public class LogBeforeAdvice implements MethodBeforeAdvice{ private Logger logger= Logger.getLogger(this.getClass().getName()); public void before(Method method,Object[] args, Object target)throws Throwable{ logger.log(Level.INFO,"Method starts..."+method); } } |
package com.biaoflying; import org.springframework.context.*; import org.springframework.context.support.*; public class SpringAOPDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext context= new FileSystemXmlApplicationContext( "beans-config.xml"); IHello helloProxy=(IHello)context.getBean( "helloProxy"); helloProxy.hello("biaoflying"); } } |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="logBeforeAdvice" class="com.biaoflying.LogBeforeAdvice"/> <bean id="helloSpeaker" class="com.biaoflying.HelloSpeaker"/> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.biaoflying.IHello</value> </property> <property name="target"> <ref bean="helloSpeaker"/> </property> <property name="interceptorNames"> <list> <value>logBeforeAdvice</value> </list> </property> </bean> </beans> |
执行SpringAOPDemo.java会在hello方法执行之前输出log消息。
另外对于After advice,Around advice是类似的。
-----------------------------------------------------------------------------------
三,Throw Advice
表示在目标对象调用方法发生异常时添加一个Aspect。
package com.biaoflying; public interface IHello { public void hello(String name)throws Throwable; } |
package com.biaoflying; public class HelloSpeaker implements IHello { public void hello(String name)throws Throwable{ System.out.println("Hello "+name); throw new Exception("发生异常..."); } } |
package com.biaoflying; import java.lang.reflect.Method; import java.util.logging.*; import org.springframework.aop.ThrowsAdvice; public class SomeThrowAdvice implements ThrowsAdvice{ private Logger logger= Logger.getLogger(this.getClass().getName()); public void afterThrowing(Method method,Object[] args,Object target,Throwable subclass){ logger.log(Level.INFO, "Logging that a "+subclass+ "Exception was thrown in "+method); } } |
package com.biaoflying; import org.springframework.context.ApplicationContext; import org.springframework. context.support.FileSystemXmlApplicationContext; public class SpringAOPDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext context= new FileSystemXmlApplicationContext( "beans-config.xml"); IHello helloProxy= (IHello)context.getBean("helloProxy"); try{ helloProxy.hello("biaoflying"); }catch(Throwable throwable){ System.err.println(throwable); } } } |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="someThrowAdvice" class="com.biaoflying.SomeThrowAdvice"/> <bean id="helloSpeaker" class="com.biaoflying.HelloSpeaker"/> <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.biaoflying.IHello</value> </property> <property name="target"> <ref bean="helloSpeaker"/> </property> <property name="interceptorNames"> <list> <value>someThrowAdvice</value> </list> </property> </bean> </beans> |
四,更为细化的AOP programming。
通过定义Ponitcut来实现更为细化的AOP编程,如可以指定在Target的某个方法执行时加入advice。
package com.biaoflying; public interface IHello { public void helloNewbie(String name); public void helloMaster(String name); } |
package com.biaoflying; public class HelloSpeaker implements IHello { public void helloNewbie(String name){ System.out.println("Hello "+name+" newbie"); } public void helloMaster(String name){ System.out.println("Hello "+name+" will be master"); } } |
package com.biaoflying; import java.lang.reflect.Method; import java.util.logging.*; import org.springframework.aop.MethodBeforeAdvice; public class LogBeforeAdvice implements MethodBeforeAdvice{ private Logger logger= Logger.getLogger(this.getClass().getName()); public void before(Method method,Object[] args, Object target)throws Throwable{ logger.log(Level.INFO,"Method starts..."+method); } } |
package com.biaoflying; import org.springframework.context.ApplicationContext; import org.springframework. context.support.FileSystemXmlApplicationContext; public class SpringAOPDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ApplicationContext context= new FileSystemXmlApplicationContext( "beans-config1.xml"); IHello helloProxy= (IHello)context.getBean("helloProxy"); helloProxy.helloNewbie("abio"); helloProxy.helloMaster("biaoflying"); } } |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="logBeforeAdvice" class="com.biaoflying.LogBeforeAdvice"/> <bean id="helloAdvisor" class="org.springframework.aop. support.NameMatchMethodPointcutAdvisor"> <property name="mappedName"> <value>hello*</value> </property> <property name="advice"> <ref bean="logBeforeAdvice"/> </property> </bean> <bean id="helloSpeaker" class="com. biaoflying.HelloSpeaker"/> <bean id="helloProxy" class="org.springframework. aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.biaoflying.IHello</value> </property> <property name="target"> <ref bean="helloSpeaker"/> </property> <property name="interceptorNames"> <list> <value>helloAdvisor</value> </list> </property> </bean> </beans> |
另外一个静态PointcutAdvisor为RegexpMethodPointcutAdvisor,更改以上的配置:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="logBeforeAdvice" class="com.biaoflying.LogBeforeAdvice"/> <bean id="helloAdvisor" class="org.springframework.aop. support.RegexpMethodPointcutAdvisor"> <property name="pattern"> <value>.*hello.*</value> </property> <property name="advice"> <ref bean="logBeforeAdvice"/> </property> </bean> <bean id="helloSpeaker" class="com.biaoflying. HelloSpeaker"/> <bean id="helloProxy" class="org.springframework. aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>com.biaoflying.IHello</value> </property> <property name="target"> <ref bean="helloSpeaker"/> </property> <property name="interceptorNames"> <list> <value>helloAdvisor</value> </list> </property> </bean> </beans> |
两者都可以得到一下的输出:
信息: Method starts...public abstract void com.biaoflying.IHello.helloNewbie(java.lang.String) 2008-3-22 18:38:22 com.biaoflying.LogBeforeAdvice before 信息: Method starts...public abstract void com.biaoflying.IHello.helloMaster(java.lang.String) Hello abio newbie Hello biaoflying will be master |