您的位置:首页 > 编程语言 > Java开发

SpringAOP之入门

2017-02-03 09:12 316 查看

AOP的3个关键概念

切入点(Pointcut)

在介绍Pointcut之前先介绍Join Point(连接点)的概念。Join Point指程序运行中的某个阶段点,比如一个方法调用,异常抛出等。Pointcut就是Join Point的集合,它是程序中需要注入Advice的位置的集合,指明Advice在什么样的条件下才能被触发。

Pointcut.java源码



接口ClassFilter:用来将切入点限定在给定的目标类中

接口MethodMatcher:用来判断切入点是否匹配目标类给定的方法

通知(Advice)

Advice是某个连接点所采用的处理逻辑,也就是向连接点注入的代码。 在spring AOP中,它主要描述Spring AOP围绕方法调用而注入的切面行为。

Advisor

Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码

Spring的3种切入点(Pointcut)实现

静态切入点

静态切入点只限于给定的方法和目标类,不考虑方法的参数。Spring在调用静态切入点时只在第一次的时候计算静态切入点的位置,然后把它缓存起来,以后不再需要再进行计算。使用org.springframework.aop.support.RegexpMethodPointcut实现静态切入点

<bean id="settersAndAbsquatulatePointcut"
class="org.springframework.aop.support.RegexpMethodPointcut">
<property name="patterns">
<!-- 设定切入点 -->
<list>
<value>.*save.*</value>
<value>.*do.*</value>
</list>
</property>
</bean>
</beans>


.* save.* : 表示所有以save开头的方法都是切入点

.* do.* : 表示所有以do开头的方法都是切入点

动态切入点

动态切入点不仅限定于给定的方法和类,还可以指定方法的参数。因为参数的变化性,动态切入点不能缓存,需要每次调用的时候都进行计算,因为使用动态切入点有很大的性能损耗。

当切入点需要在执行时根据参数值来调用通知,就需要使用动态切入点。Spring提供了一个内建的动态切入点:控制流切入点。该切入点匹配基于当前线程的调用的堆栈,只有在当前线程执行时找到特定的类和方法才能返回true

自定义切入点

因为Spring中的切入点是Java类,所以可以定义自定义切入点

Spring的通知(Advice)

Spring提供5种Advice类型:

① Interception Around :在Join Point前后调用

② Before :在Join Point前调用

③ after Returning :在Join Point后调用

④ Throw :在Join Point抛出异常调用

⑤ Introduction :在Join Point调用完毕后调用

Interception Around通知

Interception Around 在在Join Point前后调用,也是Spring中最基本的通知类型,实现Interception Around的类需要实现接口MethodInterception

public class LogInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throw Throwable
{
System.out.println("开始...");
Object ravl=invocation.proceed();
System.out.println("结束...");
return rval;
}
}


Before通知

Before 在Join Point前调用,需要实现接MethodBeforeAdvice

public class LogBeforeAdvice implements MethodBeforeAdvice {

@Override
public void before(Method m, Object[] arg1, Object target)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("开始...");
}
}


after Returning通知

after Returning 在在Join Point后调用,需要实现接口AfterReturningAdvice

public class LogAfterAdvice implements AfterReturningAdvice{

@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("结束...");
}
}


Throw通知

Throw**在Join Point抛出异常**调用,需要实现接口ThrowsAdvice

public class LogThrowAdvice implements ThrowsAdvice{
public void afterThrowing(RemoteException e) throws Throwable
{
System.out.println(" 抛出异常,请检查..."+e);
}
}


Introduction通知

Introduction 在Join Point调用完毕后调用,需要实现接口IntroductionAdvice和接口IntroductionInterceptor

用ProxyFactoryBean创建AOP代理

使用Spring提供的类org.springframework.aop.framework.ProxyFactoryBean是创建AOP的最基本方式

使用ProxyFactoryBean代理目标类的所有方法

在Spring中,ProxyFactoryBean是在xml里配置的

<bean id="log" class="spring.aop.LogAfterAdvice"></bean>
<bean id="timeBook" class="spring.aop.TimeBook"></bean>
<!-- 设定代理类 -->
<bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 这里代理的是接口 -->
<!-- 3个属性,proxyInterfaces ;target;interceptorNames-->
<property name="proxyInterfaces">
<value>spring.aop.TimeBookInterface</value>
</property>
<!-- 是ProxyFactoryBean要代理的目标类 -->
<property name="target">
<ref bean="timeBook"/>
</property>
<!-- 程序中的Advice -->
<property name="interceptorNames">
<list>
<value>log</value>
</list>
</property>
</bean>


id为log的Bean : 程序中的Advice

id为timeBook的Bean : ProxyFactoryBean要代理的目标类

id为logProxy的Bean : ProxyFactoryBean本身

ProxyFactoryBean的proxyInterfaces属性 : 指明要代理的接口

ProxyFactoryBean的target属性 : 指明要代理的目标类,这个目标类实现了上面proxyInterfaces属性指定的接口

ProxyFactoryBean的interceptorNames属性 : 指明要在代理的目标类中插入的Adivce

ProxyFactoryBean还有一个属性proxyTargetClass属性,如果属性设置为true,那proxyFactoryBean要使用CGLIB方式代理,而不是接口

使用ProxyFactoryBean代理目标类的指定方法

Advice会代理目标类的所有方法。使用Spring提供的org.springframework.aop.support.RegexpMethodPointcutAdvisor

<bean id="log" class="spring.aop.LogAfterAdvice"></bean>
<bean id="timeBook" class="spring.aop.TimeBook"></bean>
<!-- 代理目标类的指定方法 -->
<bean id="logAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="log" />
</property>
<property name="patterns">
<value>.*doAuditing.*</value>
</property>
</bean>
<!-- 设定代理类 -->
<bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>spring.aop.TimeBookInterface</value>
</property>
<property name="target">
<ref bean="timeBook" />
</property>
<property name="interceptorNames">
<list>
<value>logAdvisor</value>
</list>
</property>
</bean>


1、在id为logAdvisor的Bean中设定Advice和指定的方法

2、把id为logProxy的Bean的interceptorNames属性值改为logAdvisor

3、logAdvisor的advice属性指定Advice

4、logAdvice的patterns属性指定要代理的方法。“.doAuditing”表示只有doAuditing()方法才能使用指定的Advice,patterns属性使用的是正则表达式(注意:因为要使用正则表达式,要引入jakarta-oro-2.0.8.jar

正则表达式简介

正则表达式用于在一个文件中查找字符

1、“ . ” 可以用来匹配任何一个字符。比如:正则表达式为“ g.f ”,它就会匹配“ gaf ”、“ g*f ”、“ g&f ”等中间是任意字符的字符

2、“ [ ] ”只有[ ]里面指定的字符才能匹配。比如:正则表达式为“ g[ abc ]f ”,它只能匹配“ gaf ”、“ gbf ”、“ gcf ”,不会匹配“ g*f ”、“ g&f ”等

3、“ * ” 表示匹配次数,可以任意次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“ g.*f ”,它只能匹配“ gaaaaaf ”、“ gf ”、“ g *f ”等

4、“ ? ” 可以匹配 0 或者 1 次,用来确定紧靠该符号左边的符号出现的次数。比如:正则表达式为“ g.?f ”,它只能匹配“ gaf ”、“ g f ”等

5、“ \ ” 是正则表达式的连续符 , 比如:正则表达式为“ g.-f ”,它只能匹配“ g-f ”、“ ga-f ”和“ g*-f ”等

上面的示例中,只对TimeBook类的doAuditing( )方法有效,如果要对TimeBook中所有以do开头的方法有效,可以这样设定“ .do. ”

<!--对所有以do开头的方法有效-->
<property name="patterns">
<value>.*do.*</value>
</property>


如果要对TimeBook类中所有方法有效,可以这样设定“ .spring.aop.TimeBookInterface. ”

<property name="patterns">
<value> .*spring\.aop\.TimeBookInterface.*</value>
</property>


注意:包的路径要用“ \ ”连接符来表示 “ . ”不是正则表达式的“ . ”

把输出日志的实例用Spring的AOP来实现

采用Interception Around通知的形式实现

Interception Around通知会在JointPoint的前后执行,实现Interception Around通知的类需要实现接口MethodInterception。

实现思路:

① 实现接口MethodInterception

② 在Invoke()方法里编写负责输出日志信息的代码,具体的业务逻辑还使用前面的接口TimeBookInterface和它的实现类TimeBook

③ 在Spring的配置文档中定义Pointcut

④ 编写测试类,执行

(1)负责输出日志信息的类LogAround,该类实现了MethodInterception,重写了Invoke()方法

public class LogAround implements MethodInterceptor {
private Logger logger = Logger.getLogger(this.getClass().getName());

public Object invoke(MethodInvocation invocation) throws Throwable {
logger.log(Level.INFO, invocation.getArguments()[0] + " 开始...");
try {
Object result = invocation.proceed();
//返回值即是被调用的方法的返回值
return result;
} finally {
// TODO: handle exception
logger.log(Level.INFO, invocation.getArguments()[0] + " 结束...");
}
}
}


<bean id="log" class="spring.aop.LogAround"></bean>
<bean id="timeBook" class="spring.aop.TimeBook"></bean>
<bean id="logProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>spring.aop.TimeBookInterface</value>
</property>
<property name="target">
<ref bean="timeBook" />
</property>
<property name="interceptorNames">
<list>
<value>log</value>
</list>
</property>
</bean>


public class Test {
public static void main(String[] args) {
timeBookProxy.doAuditing("张三");

//通过ApplicationContext获取XML
ApplicationContext context=new
FileSystemXmlApplicationContext("classpath:aop-config.xml");
TimeBookInterface timeBookProxy=(TimeBookInterface) context.getBean("logProxy");
timeBookProxy.doAuditing("CSDN");
}
}


运行结果

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: