本文介绍介绍spring aop 的用法:注解和xml配置,最简洁的配置,最简单的介绍。
注解
使用注解去配置aop非常的简单,只需要在spring的xml配置文件里激活component-scan
功能指定需要扫描的包,将组件都初始化到spring的容器中。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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<context:component-scan base-package="com.springinaction"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
很简单,除了引入spring的一些命名空间外还有两行,一个是注册扫描功能并指定目标包、第二个是注册自动代理功能,这一点很重要,如果你不加入的话,spring就会抛出异常,我小白,没有能搞定其中的原理,在网上找了一个链接
动态代理
编写接口
这里声明一个接口Performance
,里面有perform()方法,如下
package com.springinaction.service;
public interface Performance {
public void perform();
}
编写Actor实现接口
这里需要加上conponent
的注解,是让spring初始化的时候被扫描到
package com.springinaction.service;
import org.springframework.stereotype.Component;
@Component
public class Actor implements Performance {
@Override
public void perform() {
System.out.println("表演中...");
}
}
编写一个切面
这个bean除了需要加上component
的注解外,还需要加上aspectj
的注解,让spring知道他不仅仅是一个组件,而且是一个切面。代码如下
package com.springinaction.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AudienceAnnotation {
@Pointcut("execution(** com.springinaction.service.Performance.perform(..))")
public void performance(){}
@Before("performance()")
public void takeSeats(){
System.out.println("找座位");
}
@AfterReturning("performance()")
public void applause(){
System.out.println("再来一个");
}
@AfterThrowing("performance()")
public void demandRefund(){
System.out.println("垃圾,炒鸡垃圾");
}
@Around("performance()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出环绕通知");
return object;
}
}
被Pointcut
注解的performance
方法并没有实质性作用,这是为了扩展切点表达式语言,这样就可以在任何切点表达式中使用performance
,当然不使用Pointcut
你就要这样写
package com.springinaction.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AudienceAnnotation {
@Before("execution(** com.springinaction.service.Performance.perform(..))")
public void takeSeats(){
System.out.println("找座位");
}
@AfterReturning("execution(** com.springinaction.service.Performance.perform(..))")
public void applause(){
System.out.println("再来一个");
}
@AfterThrowing("execution(** com.springinaction.service.Performance.perform(..))")
public void demandRefund(){
System.out.println("垃圾,炒鸡垃圾");
}
@Around("execution(** com.springinaction.service.Performance.perform(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出环绕通知");
return object;
}
}
不嫌麻烦,这也是可以的。下面是切点表达式的解释(照着springinaction上绘制的)
只要实现Performance
的perform
的方法,就会依次执行@Before
,@AfterReturning
,@AfterThrowing
。当然使用@Around
方法可以代替以上三个注解。
下面看一下目录结构(先不用管红线划去的地方)
编写测试类
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.springinaction.service.Actor;
public class ShowTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-annotation.xml");
Actor actor = (Actor)context.getBean("actor");
actor.perform();
}
}
运行结果为:
进入环绕通知
找座位
表演中...
退出环绕通知
再来一个
XML配置
使用xml,你就要注册每一个bean,也包括aspectJ,如下
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<bean id="oldClown" class="com.springinaction.service.OldClown" />
<bean id="audienceXml" class="com.springinaction.aop.AudienceXml" />
<aop:aspectj-autoproxy proxy-target-class="true" />
<aop:config>
<aop:aspect id="aspect" ref="audienceXml">
<aop:pointcut
expression="execution(** com.springinaction.service.Performance.perform(..))"
id="performance" />
<aop:before method="takeSeats" pointcut-ref="performance" />
<aop:after-returning method="applause"
pointcut-ref="performance" />
<aop:after-throwing method="demandRefund"
pointcut-ref="performance" />
<aop:around method="doBasicProfiling" pointcut-ref="performance" />
</aop:aspect>
</aop:config>
</beans>
上面没有注册spring扫描的功能,所有的bean都要自己去声明。aspect的配置要和下面的AudienceXml
一一对应。
编写OldClown实现接口
同样实现Performance
接口
package com.springinaction.service;
public class OldClown implements Performance {
@Override
public void perform() {
System.out.println("老小丑演技更加佳");
}
}
编写一个切面
这里的切面没有任何注解,所有的功能都在xml里配置了
package com.springinaction.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class AudienceXml {
public void performance(){}
public void takeSeats(){
System.out.println("找座位");
}
public void applause(){
System.out.println("再来一个");
}
public void demandRefund(){
System.out.println("垃圾,炒鸡垃圾");
}
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("进入环绕通知");
Object object = pjp.proceed();//执行该方法
System.out.println("退出环绕通知");
return object;
}
}
编写测试类
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.springinaction.service.OldClown;
public class ShowTestXml {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-aop-xml.xml");
OldClown oldClown = (OldClown)context.getBean("oldClown");// TODO
oldClown.perform();
}
}
运行的结果为:
找座位
进入环绕通知
老小丑演技更加佳
退出环绕通知
再来一个
到此spring切点的注解与xml配置已经都已经完成,这些是springaop的最基本实现方式,具体的还要看所属的项目结构。
下一篇将会介绍springaop处理通知中的参数,还有本节所有的代码下载地址