上一篇博客中已经简单介绍了,这里通过案例详细说明一下aop中各种通知的用法。
先列出后面介绍的JDK动态代理通知(主要区别于cglib代理而言,下面会具体介绍)、静态切入点、正则切入点等都依赖使用的抽象主题(一个接口,可以是多个)、是、真实主题(改接口的实现类)
接口:SayService.java
package www.csdn.spring.proxy.advice;
public interface SayService {
public void say(String content);
public void sayHi();
public void sayHello();
public void byebye();
}
实现类:SayServiceImpl.java
package www.csdn.spring.proxy.advice;
public interface SayService {
public void say(String content);
public void sayHi();
public void sayHello();
public void byebye();
}
实现类:SayServiceImpl.java
package www.csdn.spring.proxy.advice;
public class SayServiceImpl implements SayService{
@Override
public void say(String content) {
System.out.println("say:"+content);
//int i=1/0;
}
@Override
public void sayHi() {
System.out.println("===sayHi()方法执行了===");
}
@Override
public void sayHello() {
System.out.println("===sayHello()方法执行了===");
}
@Override
public void byebye() {
System.out.println("===byebye()方法执行了===");
}
}
1.各种通知的使用,JDK动态代理
前置通知、后置通知、环绕通知、异常通知、引入通知的案例分析,下面注释都有详细解释,这里不再赘述。
spring-advice.xml 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.xsd">
<!-- 创建前置通知 -->
<bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
<!-- 创建后置通知 -->
<bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
<!-- 创建环绕通知 -->
<bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
<!-- 创建异常通知 -->
<bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
<!-- 引入通知 -->
<bean id="auditableAdvice" class="www.csdn.spring.proxy.advice.AuditableImpl"/>
<!-- 真实主题 目标对象 -->
<bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
<!-- 配置代理操作 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--抽象主题 实现接口 -->
<property name="proxyInterfaces">
<array>
<value>www.csdn.spring.proxy.advice.SayService</value>
<value>www.csdn.spring.proxy.advice.Auditable</value>
</array>
</property>
<!-- 目标对象 -->
<property name="target">
<ref bean="sayServiceImpl"/>
</property>
<!-- 织入的通知的名称 -->
<property name="interceptorNames">
<array>
<value>beforeAdvice</value>
<value>afterAdvice</value>
<value>aroundAdvice</value>
<value>throwAdvice</value>
<value>auditableAdvice</value>
</array>
</property>
</bean>
</beans>
下面是这几个通知具体反映到的类,最关键的是这个几个通知代理类所要继承的接口,不可写错,更不可不写,否则会出错,这些错误将在后期本人一篇spring错误总结的博客中涉及到,敬请期待。
前者通知类:BeforeAdvice.java
package www.csdn.spring.proxy.advice;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("在目标方法执行之前执行........................");
// 执行完之后执行exit方法,退出程序
//System.exit(0);
// 暴露的参数的含义
System.out.println("目标方法:" + method.getName());
if (args != null && args.length > 0) {
for (Object arg : args) {
System.out.println("传递的参数值:" + arg);
}
}
System.out.println("目标对象:" + target.getClass());
}
}
后置通知类:AfterAdvice.java
package www.csdn.spring.proxy.advice;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class AfterAdvice implements AfterReturningAdvice{
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("在目标方法执行之后执行..............................");
}
}
环绕通知类:AroundAdvice.java
package www.csdn.spring.proxy.advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class AroundAdvice implements MethodInterceptor {
public void beforeMethod() {
System.out.println("around---------------------目标方法之前执行");
}
public void afterMethod() {
System.out.println("around---------------------目标方法之后执行");
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object returnValue = null;
beforeMethod();
returnValue = invocation.proceed();
afterMethod();
return returnValue;
}
}
异常通知类:ThrowsAdvice.java
package www.csdn.spring.proxy.advice;
import java.lang.reflect.Method;
import org.springframework.aop.ThrowsAdvice;
public class ThrowAdvice implements ThrowsAdvice{
public void afterThrowing(Method m,Object[] os,Object
target,Throwable throwable){
System.out.println("出现异常了:"+throwable.getLocalizedMessage());
}
}
引用通知需要注意是,因为这里spring使用的是jdk动态代理,所以使用引用通知的时候一定先创建一个引用通知的抽象接口,再创建一个引用通知类。
抽象主题 Auditable.java
package www.csdn.spring.proxy.advice;
import java.util.Date;
public interface Auditable {
public void setDate(Date date);
public Date getDate();
}
引用通知类 AuditableService.java
package www.csdn.spring.proxy.advice;
import java.util.Date;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class AuditableImpl extends DelegatingIntroductionInterceptor implements Auditable {
private Date date;
@Override
public void setDate(Date date) {
this.date = date;
}
@Override
public Date getDate() {
return date;
}
}
测试类 AdviceTest.java
package www.csdn.spring.proxy.advice;
import java.util.Date;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdviceTest {
@Test
public void testAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:spring-advice.xml");
// 不使用代理的时候
// context.getBean("sayServiceImpl",SayServiceImpl.class).say("嗨!杨凯!");
// 使用代理的时候
SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
sayService.say("嗨!杨凯!");
//引入通知测试
Auditable auditable = (Auditable) sayService;
auditable.setDate(new Date());
System.out.println(auditable.getDate());
}
}
2.各种通知的使用,cglib代理
spring中的JDK动态代理和cglib代理区别就是:1)在配置文件中的区别,即在配置文件中抽象主键,接口对象配置不同,具体不同详见下面红色部分;主要通过property属性的proxyTargetClass值设置。2)即JDK动态代理和cglib代理的不同,前者基于接口代理,后者基于类代理,所以这里就有了真实主题的不同,即实现类的不同,cglib的真实主题没有实现任何接口。
spring-advices.xml 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.xsd">
<!-- 创建前置通知 -->
<bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
<!-- 创建后置通知 -->
<bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
<!-- 创建环绕通知 -->
<bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
<!-- 创建异常通知 -->
<bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
<!-- 真实主题 目标对象 -->
<bean id="sayServiceImpls" class="www.csdn.spring.proxy.advice.SayServiceImpls"/>
<!-- 配置代理操作 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--抽象主题 实现接口 -->
<!-- 写法一:直接使用代理类,不再去指明是哪个代理类 ;这个时候value的值不管是true或false都其作用-->
<property name="proxyTargetClass" value="true"/>
<!-- 指明代理类,这时候value必须是true的时候才起作用 ;
使用false会报错:Bean named 'proxyFactoryBean' must be of type [www.csdn.spring.advice.SayServiceImpls],
but was actually of type [$Proxy4]
<property name="proxyInterfaces">
<array>
<value>www.csdn.spring.proxy.advice.SayService</value>
</array>
</property>
<property name="proxyTargetClass">
<value>true</value>
</property>
-->
<!-- 目标对象 -->
<property name="target">
<ref bean="sayServiceImpls"/>
</property>
<!-- 织入的通知的名称 -->
<property name="interceptorNames">
<array>
<value>beforeAdvice</value>
<value>afterAdvice</value>
<value>aroundAdvice</value>
<value>throwAdvice</value>
</array>
</property>
</bean>
</beans>
真实主题:SayServiceImpls.java
package www.csdn.spring.proxy.advice;
public class SayServiceImpls{
public void say(String content) {
System.out.println("say:"+content);
}
}
测试类 AdviceTests.java
package www.csdn.spring.proxy.advice;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdviceTests {
@Test
public void testAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:spring-advices.xml");
// 不使用代理的时候
// context.getBean("sayServiceImpls",SayServiceImpls.class).say("嗨!你好!");
// 使用代理的时候
context.getBean("proxyFactoryBean", SayServiceImpls.class).say("嗨!你好!");
}
}
3.静态切入点
为什么会有静态切入点,上一篇看博客已经介绍过,为了使你编写的通知,即代理在指定切入点起作用,也就是所谓的是aop的通知有实用价值。这里通过案例详细分析静态切入点。
spring-staticAdvisor.xml 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.xsd">
<!-- 创建前置通知 -->
<bean id="beforeAdvice" class="www.csdn.spring.proxy.advice.BeforeAdvice"/>
<!-- 创建后置通知 -->
<bean id="afterAdvice" class="www.csdn.spring.proxy.advice.AfterAdvice"/>
<!-- 创建环绕通知 -->
<bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
<!-- 创建异常通知 -->
<bean id="throwAdvice" class="www.csdn.spring.proxy.advice.ThrowAdvice"/>
<!-- 静态切入点 -->
<bean id="nameMatchMethodPointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<!-- 织入通知,比如环绕通知 -->
<property name="advice">
<ref bean="aroundAdvice"/>
</property>
<!-- 指明切入点 -->
<property name="mappedName">
<value>say</value>
</property>
</bean>
<!-- 真实主题 目标对象 -->
<bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
<!-- 配置代理操作 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--抽象主题 实现接口 -->
<property name="proxyInterfaces">
<array>
<value>www.csdn.spring.proxy.advice.SayService</value>
</array>
</property>
<!-- 目标对象 -->
<property name="target">
<ref bean="sayServiceImpl"/>
</property>
<!-- 织入的通知的名称 -->
<property name="interceptorNames">
<array>
<value>nameMatchMethodPointcutAdvisor</value>
</array>
</property>
</bean>
</beans>
测试类 AdvisorTest.java
package www.csdn.spring.proxy.advice;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdvisorTest {
@Test
public void testAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:spring-s*.xml");
// 使用代理的时候
SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
sayService.say("嗨!杨凯,你好!");
sayService.sayHi();
sayService.sayHello();
sayService.byebye();
}
}
4.正则切入点
闲话少说,与静态切入点不同的就是正则切入点匹配正则表达式,重点代码见下面红色部分.
spring-regAdvisor.xml 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.xsd">
<!-- 创建环绕通知 -->
<bean id="aroundAdvice" class="www.csdn.spring.proxy.advice.AroundAdvice"/>
<!-- 静态切入点 -->
<bean id="regexpMethodPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 织入通知,比如环绕通知 -->
<property name="advice">
<ref bean="aroundAdvice"/>
</property>
<!-- 指明切入点 -->
<property name="patterns">
<!-- .是通配符的意思;第一个.*代表匹配任何包名和类名 ;第二个.*代表匹配以say开头的任意方法-->
<array>
<value>.*sayH.</value>
<value>.*bye.*</value>
<value>www.*\.SayService\.sayHell.</value>
</array>
</property>
</bean>
<!-- 真实主题 目标对象 -->
<bean id="sayServiceImpl" class="www.csdn.spring.proxy.advice.SayServiceImpl"/>
<!-- 配置代理操作 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--抽象主题 实现接口 -->
<property name="proxyInterfaces">
<array>
<value>www.csdn.spring.proxy.advice.SayService</value>
</array>
</property>
<!-- 目标对象 -->
<property name="target">
<ref bean="sayServiceImpl"/>
</property>
<!-- 织入的通知的名称 -->
<property name="interceptorNames">
<array>
<value>regexpMethodPointcutAdvisor</value>
</array>
</property>
</bean>
</beans>
测试类 AdvisorTest.java
package www.csdn.spring.proxy.advice;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdvisorTest {
@Test
public void testAdvice() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:spring-r*.xml");
// 使用代理的时候
SayService sayService = context.getBean("proxyFactoryBean", SayService.class);
sayService.say("嗨!杨凯,你好!");
sayService.sayHi();
sayService.sayHello();
sayService.byebye();
}
}