深入浅出 spring AOP 刑红瑞
来源:互联网 发布:mac终端查看当前路径 编辑:程序博客网 时间:2024/06/07 09:55
深入浅出 springAOP (一) 源出处
先不讨论AOP的各种名词,也不作各种AOP的比较,我将在例子中介绍各种名词。
1。先写一个javabean,就是target object。
packageorg.tatan.test;
publicclass MessageBean {
public void write() {
System.out.print("AOP example");
}
2。写一个AOP的advice类MethodInterceptor是AOP联盟的标准接口,它是最简单最实用的连接点(joinpoint),实现了aroundadvice ,你可以在他返回前调用target的方法。
packageorg.tatan.test;
importorg.aopalliance.intercept.MethodInterceptor;
importorg.aopalliance.intercept.MethodInvocation;
publicclass MessageCode implements MethodInterceptor {
publicObject invoke(MethodInvocation invocation) throws Throwable {
System.out.print("this is a ");
Object returnValue = invocation.proceed();
return returnValue ;
}
3。把MessageCode advice weave 到proxy factory,proxy factory是整个架构的核心
先创建instance of MessageBean,然后创建代理的instance ,MessageCode advice 传递给的
addAdvice()方法,设置Target Object,调用getProxy()产生代理对象。
importorg.springframework.aop.framework.ProxyFactory;
publicclass AOPExample {
public static void main(String[] args) {
MessageBean target = new MessageBean();
ProxyFactory pf = new ProxyFactory();
pf.addAdvice(new essageCode());
MessageBean proxy = (MessageBean) pf.getProxy();
//输出原始信息
target.write();
proxy.write();
4。classpath中加入cglib-nodep-2.1_2.jar ,spring.jar,aopalliance.jar,commons-logging.jar
结果
AOP example
this is a AOP example
有人问我,为什末使用CGLIBproxy而不使用JDKDynamic Proxies,这和spring aop使用的原则相关。
packageorg.tatan.test;
publicinterface Worker {
void doSomeWork(int numOfTimes);
}
目标类
package org.tatan.test;
public void doSomeWork(int numOfTimes) {
for (int i = 0; i < numOfTimes; i++) {
}
}
Advice执行流程
package org.tatan.test;
importjava.lang.reflect.Method;
importorg.aopalliance.intercept.MethodInterceptor;
importorg.aopalliance.intercept.MethodInvocation;
publicclass AroundAdvice implements MethodInterceptor {
publicObject invoke(MethodInvocation invocation) throws Throwable {
Object returnValue = invocation.proceed();
Method m = invocation.getMethod();
Object target = invocation.getThis();
Object[] args = invocation.getArguments();
System.out.println("Executed method: " + m.getName());
System.out.println("On object of type: " + target.getClass().getName());
System.out.println("With arguments:");
for (inti=0;i<args.length;i++) {
System.out.println("---->" + args[i]);
}
System.out.println();
return returnValue;
}
packageorg.tatan.test;
importorg.springframework.aop.framework.ProxyFactory;
publicclass AOPExample2 {
public static void main(String[] args) {
Worker bean = getWorkerBean();
bean.doSomeWork(100000000);
}
private static Worker getWorkerBean() {
WorkerBean target = newWorkerBean();
ProxyFactory pf = newProxyFactory();
pf.setTarget(target);
pf.addAdvice(new AroundAdvice());
pf.setInterfaces(newClass[]{Worker.class});
return (Worker) pf.getProxy();
}
}
如果调用了setInterfaces();就不要cglib了,使用JDKDynamic Proxies,只是使用JDKDynamic Proxies程序执行的效率比较低。
使用CGLIB的Frozen效率比标准的CGLIB效率高。
packageorg.tatan.test;
importorg.springframework.aop.framework.ProxyFactory;
publicclass AOPExample2 {
public static void main(String[] args) {
Worker bean = getWorkerBean();
bean.doSomeWork(100000000);
}
private static Worker getWorkerBean() {
WorkerBean target = new WorkerBean();
ProxyFactory pf = new ProxyFactory();
pf.setTarget(target);
pf.addAdvice(new AroundAdvice());
// pf.setInterfaces(new Class[]{Worker.class});
pf.setFrozen(true);
return (Worker) pf.getProxy();
}
原则上使用CGLIB,因为既可以使用类,还可以使用接口,JDK proxy 只能代理口。
2.目标类的方法不能是final的,因为spring要生成目标类的子类,任何要advised的方法都要overide,所以不允许final method。
spring AOP使用,使用CGLIB应该使用接口不是类,这点务必注意。
使用BeanNameAutoProxyCreator声明事务,
<!--define transaction interceptor -->
<beanid="txInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor">
<propertyname="transactionManager"><refbean="transactionManager"/></property>
<propertyname="transactionAttributeSource"><refbean="txAttributes"/></property>
<!--Define transactional methods (NameMatchTransactionAttributeSource
appliesspecific attributes to methods that match to a pattern) -->
<beanid="txAttributes"class="org.springframework.transaction.interceptor.
NameMatchTransactionAttributeSource">
<property name="properties">
<!--使用Autoproxy定义事务的beans,应用于Controllers-->
<beanid="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<propertyname="interceptorNames"><value>txInterceptor</value></property>
<propertyname="beanNames"><value>*Controller</value></property>
<!--事务管理的beans-->
<beanid="userManager" class="com.tatan.domain.user.UserManager"singleton="true" dependency-check="objects">
<constructor-argindex="0"><refbean="userController"/></constructor-arg>
spring文档说明“when BeanNameAutoProxyCreator postprocesses the targetobject and create the proxy, it causes the proxy to be inserted into theApplication context under the name of the original bean”,
UserController 应该是个接口,而不是类,默认情况下,spring使用dynamicproxies,它只使用接口。如果使用CGLIB,最好proxyTargetClass设为true。
使用TransactionProxyFactoryBean也是如此
<beanid="MimeTarget" class="com。tatan.task.MimeTarget">
<propertyname="sessionFactory"><refbean="sessionFactory"/></property>
<propertyname="MimeDao"><refbean="MimeDao"/></property>
<beanid="MimeTargetProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<refbean="transactionManager"/></property>
<property name="target"><reflocal="MimeTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
</props>
</property>
<beanid="MimeTrigger"class="org.springframework.scheduling.timer.ScheduledTimerTask">
<propertyname="timerTask"><refbean="MimeTargetProxyFactoryBean"/></property>
<propertyname="delay"><value>1000</value></property>
<propertyname="period"><value>500000</value></property>
这样会抛出异常,exceptionorg.springframework.beans.TypeMismatchException: Failed to convert propertyvalue of type [$Proxy0] to required type [java.util.TimerTask] for property'timerTask'; nested exception is java.lang.IllegalArgumentException: argumenttype mismatch
TimerTask是个类,只能用CGLIB产生代理,TransactionProxyFactoryBean默认使用接口来产生target object,将proxyTargetClass = true即可使用CGLIBproxy代理。
spring的Advice分为5种,Before,After returning,Around,Throws,Introduction。使用这些Advice可以完成AOP相关部分90%的编码,剩余的10%只好依靠AspectJ了。在大多数情况下,around advice可以完成Before,After returning,Throws的所有功能。Beforeadvice是比较有用的advice,它可以修改传递给method的参数,可以通过异常中断method的执行,通常用于检测用户的权限。Servlet过滤器是Beforeadvice的一种方式,提供了在servlet调用前执行其他处理的能力。
packageorg.tatan.test;
publicinterface Worker {
voiddoSomeWork(int numOfTimes);
packageorg.tatan.test;
publicclass WorkerBean implements Worker {
public void doSomeWork(int numOfTimes) {
for (int i = 0; i < numOfTimes; i++) {
System.out.print(i);
}
}
packageorg.tatan.test;
importjava.lang.reflect.Method;
importorg.springframework.aop.MethodBeforeAdvice;
importorg.springframework.aop.framework.ProxyFactory;
importorg.springframework.beans.factory.support.AbstractBeanFactory;
importorg.springframework.beans.factory.xml.XmlBeanFactory;
importorg.springframework.core.io.ClassPathResource;
importorg.springframework.core.io.Resource;
publicclass SimpleBeforeAdvice implements MethodBeforeAdvice {
public static void main(String[] args) {
Resource res = newClassPathResource("org/tatan/test/bean2.xml");
AbstractBeanFactory ft = new XmlBeanFactory(res);
//Instantiate an object
Worker testObject = (Worker) ft.getBean("businesslogicbean");
testObject.doSomeWork(100);
publicvoid before(Method method, Object[] args, Object target)
System.out.println("Beforemethod: " + method.getName());
}
<?xmlversion="1.0" encoding="UTF-8"?>
<!DOCTYPEbeans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beanid="businesslogicbean"
<propertyname="proxyTargetClass">
<propertyname="interceptorNames">
<bean id="BeforeAdvice"class="org.tatan.test.SimpleBeforeAdvice" />
class="org.springframework.aop.interceptor.TraceInterceptor"/>
<bean id="beanTarget"class="org.tatan.test.WorkerBean" />
Spring使用TraceInterceptor,你可以把她添加到你的代理bean中当作一个拦截器。TransactionProxyFactoryBean有"preInterceptors"和"postInterceptors"属性,ProxyFactoryBean只有"interceptorNames"属性,这和springlive第九章有点出入,log4j配置文件,详见我的blog(http://blogger.org.cn/blog/more.asp?name=hongrui&id=7968#11881)
log4j.properties
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] -<%m>%n
<propertyname="password">
<beanclass="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<propertyname="targetClass" >
<value>com.tatan.util.XxxUtil</value>
<propertyname="targetMethod">
</property>
<propertyname="arguments">
<value>saflj</value>
return newString("Iamstupid");
深入浅出 springAOP (六)
前面的几个例子都是拦截所有类的所有方法,但是我们主要是拦截某些类的某些方法,使用Pointcut可以做到。pointcut是一系Joinpoint的集合,它定义了需要注入advice的位置。AOP框架必须允许开发者指定切入点,advisor是pointcut和advice的装配器,是将advice织入预定义位置的代码中。
ClassFilter getClassFilter();
Pointcut interface只有两个方法,返回ClassFilterand MethodMatcher的实例。ClassFilter接口被用来将切入点限制到一个给定的目标类的集合。 如果matches()永远返回true,所有的目标类都将被匹配。
matches(Method, Class) 方法被用来测试这个切入点是否匹配目标类的给定方法。这个测试可以在AOP代理创建的时候执行,避免在所有方法调用时都需要进行 测试。如果2个参数的匹配方法对某个方法返回true,并且MethodMatcher的isRuntime()也返回true,那么3个参数的匹配方法将在每次方法调用的时候被调用。这使切入点能够在目标通知被执行之前立即查看传递给方法调用的参数。
大部分MethodMatcher都是静态的,意味着isRuntime()方法 返回false。这种情况下3个参数的匹配方法永远不会被调用。
如果可能,尽量使用静态切入点。spring提供Pointcut interface的7种实现,详见它的文档。
在使用Pointcut之前,必须先创建Advisor或指定PointcutAdvisor。一个advisor就是一个aspect的完整的模块化表示,一个advisor应该包括通知和切入点。Spring中很多内建的切入点都有对应的PointcutAdvisor,DefaultPointcutAdvisor是最通用的advisor类,它可以和ethodInterceptor、 BeforeAdvice或者ThrowsAdvice一起使用。在应用较小时,只有很少类需要被切入时,ProxyFactoryBean可以使用。当有许多类需要被切入时,为每个代理创建ProxyFactoryBean就显得很繁琐。可以通过容器来创建代理。Spring提供了两个类实现自动代理:BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator。BeanNameAutoProxyCreator为匹配名字的Bean产生代理,它可以使用在将一个或者多个aspect应用在命名相似的Bean中。
public Object invoke(MethodInvocation invocation)throws Throwable {
Object returnValue =invocation.proceed();
Method m =invocation.getMethod();
Object target =invocation.getThis();
Object[] args =invocation.getArguments();
System.out.println("Executed method: " + m.getName());
System.out.println("On object of type: " +target.getClass().getName());
System.out.println("---->" + args[i]);
<bean id="myAdvice"class="com.tatan.MyAdvice"/>
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<propertyname="advice">
<ref local="myAdvice"/>
<propertyname="mappedName">
<value>getConnection</value>
<propertyname="interceptorNames">
<list>
<value>myAdvisor</value>
</list>
<propertyname="beanNames">
<list>
<value>dataSource</value>
</list>
- 深入浅出 spring AOP 刑红瑞
- 深入浅出Spring AOP
- 深入浅出Spring Aop
- 深入浅出Spring(三) AOP详解
- 深入浅出Spring(三) AOP详解
- 深入浅出Spring之第三章AOP
- 深入浅出Spring(二) AOP详解
- Spring源码分析-深入浅出AOP(图文分析)
- 深入浅出AOP(一)
- 深入浅出Spring
- 【免费】深入浅出解读 Spring 源码:IOC/AOP 篇 | Chat · 预告
- AOP、Spring的AOP
- AOP--Spring AOP
- Spring AOP 嵌套AOP
- spring AOP
- Spring AOP
- Spring AOP
- spring aop
- no resultSet set producted得问题
- 构建软件的永恒方式 -- Web Design Patterns
- Apache2+Tomcat4的配置
- CString 的 Bug(MFC42)
- Pando -- 使用Email传送任何大小的文件给任何人
- 深入浅出 spring AOP 刑红瑞
- linux下的一些设备标识及支持的文件格式对应的操作系统
- FREEBSD5.1上用IPFILTER做NAT做网关
- 网络常用端口号
- 真正的Java学习从入门到精通
- 使用IPFILTER设置小型企业防火墙系统
- JBoss Eclipse IDE 开发小组宣布 JBoss Eclipse IDE 1.5 发布了。
- 精美壁画
- 利用 ASP.NET 的内置功能抵御 Web 攻击(转自msdn)