Spring AOP有两种使用模式:@AspectJ配置模式和xml配置模式。
@AspectJ配置模式
配置
1、加入依赖包:
<!--spring aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.2</version>
</dependency>
<!-- spring aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
2、配置文件加入:
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启aop注解方式,默认为false -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
示例
切面定义
@Component
// @Aspect切面注解,表示该类是切面类
@Aspect
public class AspectLog {
// before advice,括号中是切入点配置,省略了“pointcut”(execution是pointcut语法的固定开头) 注1
@Before("execution(* com.learnsf.demo.spring.aop.BzServiceImpl.*(..))")
public void before() {
System.out.println("方法执行advice-before");
}
// After advice,括号中是切入点配置,省略了“pointcut”
@After("execution(* com.learnsf.demo.spring.aop.BzServiceImpl.*(..))")
public void after() {
System.out.println("方法执行advice-after");
}
// AfterReturning advice,括号中是切入点配置,省略了“pointcut”
@AfterReturning("execution(* com.learnsf.demo.spring.aop.BzServiceImpl.*(..))")
public void afterReturning() {
System.out.println("方法执行advice-afterReturning");
}
// Around advice,括号中是切入点配置,省略了“pointcut” 注2
/**在使用环绕注解时,可以传入ProceedingJoinPoint的对象来获取业务方法的返回值*/
@Around("execution(* com.learnsf.demo.spring.aop.BzServiceImpl.around*(..))")
public void around(ProceedingJoinPoint pj) throws Throwable {
// 执行方法前处理
System.out.println("方法执行advice-@Around前");
// 执行方法本身
BzService bzService =(BzService) pj.proceed();
// 执行方法后处理
System.out.println("方法执行advice-@Around后,返回值-"+bzService);
}
// AfterThrowing advice,其中pointcut是切入点,throwing是传入切面异常命名为"ex" 注3
@AfterThrowing(pointcut="execution(* com.learnsf.demo.spring.aop.BzServiceImpl.throw*(..))",throwing="ex")
public void afterThrowing(JoinPoint jp, Throwable ex) {
System.out.println("method " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + " throw exception: "+ex.getMessage());
}
}
注1:如何定义切入点见后续章节。
注2: ProceedingJoinPoint 继承于 JoinPoint 。其中“pj.proceed()”是执行连接点方法本身。这是around advice的特点:需要切面程序自己执行连接点方法本身;其它advice是框架来执行的。
注3:JoinPoint接口是提供切面可以访问连接点方法的信息,有用的方法如下:
getArgs():返回方法参数。
getThis():返回代理对象。
getTarget():返回目标对象。
getSignature():返回当前Advice的方法描述。
toString():打印所当前Advice方法的描述。
例子类
public interface BzService {
public void add();
public void delete();
public void update();
public void query();
public BzService aroundTest();
public BzServiceImpl throwTest() throws Exception;
}
@Component
public class BzServiceImpl implements BzService{
@Override
public void add() {
System.out.println("业务增加!");
}
@Override
public void delete() {
System.out.println("业务删除!");
}
@Override
public void update() {
System.out.println("业务修改!");
}
@Override
public void query() {
System.out.println("业务查询!");
}
@Override
public BzServiceImpl aroundTest() {
System.out.println("该方法被@Around Annotation!");
return null;
}
@Override
public BzServiceImpl throwTest() throws Exception{
System.out.println("该方法被throw!");
throw new IllegalArgumentException("测试异常抛出");
}
}
运行类
@Component
public class DemoAop {
@Autowired
BzService bzService;
public void demo() {
bzService.add();
bzService.delete();
bzService.update();
bzService.aroundTest();
try {
bzService.throwTest();
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
}
// 主程序
public class Main {
public static void main(String[] args) {
//创建springframework容器,初始化文件为app.xml
ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
DemoAop demoAop = (RunDemo)context.getBean("DemoAop");
demoAop .demo();
}
}
XML配置模式
配置
配置文件加入:
<beans xmlns="http://www.springframework.org/schema/beans"
...
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
...
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
...
<!--AOP实现方式之一:xml配置模式-->
<aop:config>
<!-- 定义切入点:id切入点的id,expression:确定要切入的地方和其详细信息 -->
<aop:pointcut id="pointcut" expression="execution(* com.learnsf.demo.spring.aop.BzServiceImplForXml.*(..))"/>
<!-- 同切面关联:advice-ref定义切面 使用的切点pointcut-ref -->
<aop:advisor advice-ref="springAopLog" pointcut-ref="pointcut"/>
</aop:config>
...
示例
切面类
// 通过继承实现对方法的参数、返回值等的访问
@Component
public class SpringAopLog implements MethodBeforeAdvice,AfterReturningAdvice {
// implement MethodBeforeAdvice(BeforeAdvice只是个标记接口,MethodBeforeAdvice为实际接口)
@Override
public void before(Method method, Object[] args, @Nullable Object target) {
System.out.println("[前置Advice]: "+target.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
//implement AfterReturningAdvice(AfterAdvice只是个标记接口,AfterReturningAdvice为实际接口)
// returnValue:切入目标方法的返回值,切入的后置方法可以对返回值进行操作
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("[后置Advice]: 执行了"+method.getName()+"方法,返回值为"+returnValue);
}
}
例子类
@Component
public class BzServiceImplForXml {
public BzPojo add(BzPojo bzPojo) {
System.out.println("BzServiceImplForXml-业务增加!");
return bzPojo;
}
public BzPojo update(BzPojo bzPojo) {
System.out.println("BzServiceImplForXml-业务修改!");
return bzPojo;
}
public boolean delete(Integer id) {
System.out.println("BzServiceImplForXml-业务删除!");
return true;
}
public void query(Integer id) {
System.out.println("BzServiceImplForXml-业务查询!");
}
}
运行
@Component
public class DemoAop {
@Autowired
BzServiceImplForXml bzServiceImplForXml;
public void demo() {
BzPojo bzPojo=new BzPojo();
bzServiceImplForXml.add(bzPojo);
bzServiceImplForXml.update(bzPojo);
bzServiceImplForXml.delete(1);
}
}
// 主程序
public class Main {
public static void main(String[] args) {
//创建springframework容器,初始化文件为app.xml
ApplicationContext context = new ClassPathXmlApplicationContext("app.xml");
DemoAop demoAop = (RunDemo)context.getBean("DemoAop");
demoAop .demo();
}
}
应用如何选择
应用如何选择@AspectJ配置模式或XML配置模式,需了解两者区别:
1、用Spring AOP同时支持@AspectJ配置模式或XML配置模式。如果只用完整的AspectJ,则只能用@AspectJ模式。
2、XML配置模式是Spring一诞生就用的模式,也是很多程序员最熟悉的模式。因此,当使用AOP作为配置企业服务的工具时,XML可能是一个不错的选择。xml配置模式优点是:1、从配置中更清楚地看到系统中存在哪些方面;2、可独立于程序更改配置。同时也存在两个缺点:首先,它没有在单个位置完全封装它所解决的需求的实现,违反DRY原则(即系统中的任何职责都应该有一个单一的、明确的表示);其次,与@AspectJ配置模式相比,XML配置模式在其可以表达的内容方面稍有限制:仅支持“singleton”方面实例化模型,并且不可能组合在XML中声明的命名切入点。
实际上,两者可以混用。
Pointcut表达式语法
Pointcut(切入点)的语法(引用自:https://blog.51cto.com/panyujie/5604327)。