spring共提供了五种类型的通知:
通知类型 | 接口 | 描述 |
Around 环绕通知 | org.aopalliance.intercept.MethodInterceptor | 拦截对目标方法调用 |
Before 前置通知 | org.springframework.aop.MethodBeforeAdvice | 在目标方法调用前调用 |
After 后置通知 | org.springframework.aop.AfterReturningAdvice | 在目标方法调用后调用 |
Throws 异常通知 | org.springframework.aop.ThrowsAdvice | 当目标方法抛出异常时调用 |
上一篇里面已经介绍了前置通知,就是在方法被调用前先执行前置通知
现在介绍一下其他的通知:
1、后置通知:
后置通知类的写法:
package com.cdtax.aop;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturningAdvice implements AfterReturningAdvice
{
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable
{
System.out.println("后置通知被:关闭资源,");
}
}
在beans.xml中的配置:
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 配置被代理的对象 --> <bean id="test1Service" class="com.cdtax.aop.Test1Service"> <property name="name" value="小明"></property> </bean> <!-- 配置前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cdtax.aop.MyMethodBeforeAdvice"></bean> <!-- 配置后置通知 --> <bean id="myAfterReturningAdvice" class="com.cdtax.aop.MyAfterReturningAdvice"></bean> <!-- 配置代理对象 --> <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口集 --> <property name="proxyInterfaces"> <list> <value>com.cdtax.aop.TestServiceInter</value> <value>com.cdtax.aop.TestServiceInter2</value> </list> </property> <!-- 把通知织入到代理对象 | 拦截器名集--> <property name="interceptorNames"> <list> <!-- 相当于把 MyMethodBeforeAdvice前置通知和代理对象关联起来,我们也可以把通知看成拦截器,struts2核心就是拦截器 --> <value>myMethodBeforeAdvice</value> <!-- 织入后置通知 --> <value>myAfterReturningAdvice</value> </list> </property> <!-- 配置被代理对象,可以指定 --> <property name="target" ref="test1Service"> </property> </bean> </beans>
package com.cdtax.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App1 { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("com/cdtax/aop/beans.xml"); // Test1Service ts = (Test1Service) ac.getBean("test1Service"); // ts.sayHello(); TestServiceInter ts1 = (TestServiceInter) ac.getBean("proxyFactoryBean"); ts1.sayHello(); ((TestServiceInter2)ts1).sayBye(); } }
-----------------------------
前置通知 :记录日志...sayHello
hai 小明
后置通知被调用:关闭资源,
-----------------------------
前置通知 :记录日志...sayBye
bye 小明
后置通知被调用:关闭资源,
2、环绕通知
package com.cdtax.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor
{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable
{
System.out.println("环绕通知被调用:调用方法前执行 ");
Object obj = arg0.proceed();
System.out.println("环绕通知被调用:调用方法后执行 ");
return obj;
}
}
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 配置被代理的对象 --> <bean id="test1Service" class="com.cdtax.aop.Test1Service"> <property name="name" value="小明"></property> </bean> <!-- 配置前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cdtax.aop.MyMethodBeforeAdvice"></bean> <!-- 配置后置通知 --> <bean id="myAfterReturningAdvice" class="com.cdtax.aop.MyAfterReturningAdvice"></bean> <!-- 配置环绕通知 --> <bean id="myMethodInterceptor" class="com.cdtax.aop.MyMethodInterceptor"></bean> <!-- 配置代理对象 --> <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口集 --> <property name="proxyInterfaces"> <list> <value>com.cdtax.aop.TestServiceInter</value> <value>com.cdtax.aop.TestServiceInter2</value> </list> </property> <!-- 把通知织入到代理对象 | 拦截器名集--> <property name="interceptorNames"> <list> <!-- 相当于把 MyMethodBeforeAdvice前置通知和代理对象关联起来,我们也可以把通知看成拦截器,struts2核心就是拦截器 --> <value>myMethodBeforeAdvice</value> <!-- 织入后置通知 --> <value>myAfterReturningAdvice</value> <!-- 织入环绕通知 --> <value>myMethodInterceptor</value> </list> </property> <!-- 配置被代理对象,可以指定 --> <property name="target" ref="test1Service"> </property> </bean> </beans>
-----------------------------
前置通知 :记录日志...sayHello
环绕通知被调用:调用方法前执行
hai 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
-----------------------------
前置通知 :记录日志...sayBye
环绕通知被调用:调用方法前执行
bye 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
3、异常通知:
修改Test1Service类,
package com.cdtax.aop;
public class Test1Service implements TestServiceInter,TestServiceInter2
{
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override
public void sayHello()
{
System.out.println("hai " + name);
}
@Override
public void sayBye()
{
System.out.println("bye " + name);
int i = 9 / 0;
}
}
编写异常通知:
package com.cdtax.aop;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice
{
public void afterThrowing(Method m,Object[] os,Object target,Exception throwable)
{
System.out.println("异常通知:出大事了" + throwable.getMessage());
}
}
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 配置被代理的对象 --> <bean id="test1Service" class="com.cdtax.aop.Test1Service"> <property name="name" value="小明"></property> </bean> <!-- 配置前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cdtax.aop.MyMethodBeforeAdvice"></bean> <!-- 配置后置通知 --> <bean id="myAfterReturningAdvice" class="com.cdtax.aop.MyAfterReturningAdvice"></bean> <!-- 配置环绕通知 --> <bean id="myMethodInterceptor" class="com.cdtax.aop.MyMethodInterceptor"></bean> <!-- 配置异常通知 --> <bean id="myThrowsAdvice" class="com.cdtax.aop.MyThrowsAdvice"></bean> <!-- 配置代理对象 --> <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口集 --> <property name="proxyInterfaces"> <list> <value>com.cdtax.aop.TestServiceInter</value> <value>com.cdtax.aop.TestServiceInter2</value> </list> </property> <!-- 把通知织入到代理对象 | 拦截器名集--> <property name="interceptorNames"> <list> <!-- 相当于把 MyMethodBeforeAdvice前置通知和代理对象关联起来,我们也可以把通知看成拦截器,struts2核心就是拦截器 --> <!-- 织入前置通知 --> <value>myMethodBeforeAdvice</value> <!-- 织入后置通知 --> <value>myAfterReturningAdvice</value> <!-- 织入环绕通知 --> <value>myMethodInterceptor</value> <!-- 织入异常通知 --> <value>myThrowsAdvice</value> </list> </property> <!-- 配置被代理对象,可以指定 --> <property name="target" ref="test1Service"> </property> </bean> </beans>
-----------------------------
前置通知 :记录日志...sayHello
环绕通知被调用:调用方法前执行
hai 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
-----------------------------
前置通知 :记录日志...sayBye
环绕通知被调用:调用方法前执行
bye 小明
异常通知:出大事了/ by zero
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.cdtax.aop.Test1Service.sayBye(Test1Service.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:307)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:182)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:149)
at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:126)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at com.cdtax.aop.MyMethodInterceptor.invoke(MyMethodInterceptor.java:13)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at $Proxy0.sayBye(Unknown Source)
at com.cdtax.aop.App1.main(App1.java:17)
4、最后一个是引入通知,引入通知不需要编写相应的类,只需要进行配置,目的是用来指定哪些方法需要执行相应的通知,如,我们想指定只有sayHello()方法执行前置通知,
在beans.xml中如下配置:
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 配置被代理的对象 --> <bean id="test1Service" class="com.cdtax.aop.Test1Service"> <property name="name" value="小明"></property> </bean> <!-- 配置前置通知 --> <bean id="myMethodBeforeAdvice" class="com.cdtax.aop.MyMethodBeforeAdvice"></bean> <!-- 配置后置通知 --> <bean id="myAfterReturningAdvice" class="com.cdtax.aop.MyAfterReturningAdvice"></bean> <!-- 配置环绕通知 --> <bean id="myMethodInterceptor" class="com.cdtax.aop.MyMethodInterceptor"></bean> <!-- 配置异常通知 --> <bean id="myThrowsAdvice" class="com.cdtax.aop.MyThrowsAdvice"></bean> <!-- 定义前置通知的切入点 (引用通知) --> <bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="advice" ref="myMethodBeforeAdvice"></property> <property name="mappedNames"> <list> <value>sayHello</value> </list> </property> </bean> <!-- 配置代理对象 --> <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口集 --> <property name="proxyInterfaces"> <list> <value>com.cdtax.aop.TestServiceInter</value> <value>com.cdtax.aop.TestServiceInter2</value> </list> </property> <!-- 把通知织入到代理对象 | 拦截器名集--> <property name="interceptorNames"> <list> <!-- 相当于把 MyMethodBeforeAdvice前置通知和代理对象关联起来,我们也可以把通知看成拦截器,struts2核心就是拦截器 --> <!-- <value>myMethodBeforeAdvice</value> --> <!-- 使用自定义切入点的前置通知 --> <value>myMethodBeforeAdviceFilter</value> <!-- 织入后置通知 --> <value>myAfterReturningAdvice</value> <!-- 织入环绕通知 --> <value>myMethodInterceptor</value> <!-- 织入异常通知 --> <value>myThrowsAdvice</value> </list> </property> <!-- 配置被代理对象,可以指定 --> <property name="target" ref="test1Service"> </property> </bean> </beans>更改Test1Service的sayBye()方法,去掉除零异常这一步,执行App1测试类结果:
-----------------------------
前置通知 :记录日志...sayHello
环绕通知被调用:调用方法前执行
hai 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
环绕通知被调用:调用方法前执行
bye 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
可以看到,只有sayHello()方法执行了前置通知,sayBye()方法没有执行
这就是引用通知,用来定义切入点的。
5、关于ts1的类型,
package com.cdtax.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App1
{
public static void main(String[] args)
{
ApplicationContext ac = new ClassPathXmlApplicationContext("com/cdtax/aop/beans.xml");
// Test1Service ts = (Test1Service) ac.getBean("test1Service");
// ts.sayHello();
TestServiceInter ts1 = (TestServiceInter) ac.getBean("proxyFactoryBean");
ts1.sayHello();
System.out.println("ts1的类型:" + ts1);
((TestServiceInter2)ts1).sayBye();
}
}
我们获得ts1是通过getBean("proxyFactoryBean");获得的,proxyFactoryBean的class是org.springframework.aop.framework.ProxyFactoryBean,那么ts1的类型是不是也是它呢?运行上述程序,结果:
-----------------------------
前置通知 :记录日志...sayHello
环绕通知被调用:调用方法前执行
hai 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
环绕通知被调用:调用方法前执行
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
ts1的类型:com.cdtax.aop.Test1Service@1982fc1
环绕通知被调用:调用方法前执行
bye 小明
环绕通知被调用:调用方法后执行
后置通知被调用:关闭资源,
可以看出,ts1的类型是com.cdtax.aop.Test1Service,它是proxyFactoryBean动态代理生成的。org.springframework.aop.framework.ProxyFactoryBean内部实现了动态代理方式。
spring在运行期创建代理,不需要特殊的编译器,spring有两种代理方式:
(1)若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理该类让spring动态产生一个新类,他实现了所需的接口,织入了通知,并且代理对目标对象的所有请求。
(2)若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。使用该方式时需要注意:
1)对接口创建代理优先于对类创建代理,因为会产生更加松耦合的系统。对类代理是让遗留系统或无法实现接口的第三方类库同样可以得到通知,这种方式应该是beiyongfang'an。
2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写。将通知织入。final方法是不允许重写的。
spring实现了aop联盟接口。
spring只支持方法连接点:不提供属性接入点,spring的观点是属性拦截破坏了封装。面向对象的概念是对象自己处理工作,其他对象只能通过方法调用来得到结果。
6、对于引入通知,定义切面时可以使用正则表达式,如在定义切入点时使用say*,表示以say开头的所有方法。