目录
1、切入点表达式
配置:
Spring基于xml的aop配置:
1)把通知bean交给spring来管理
2)使用 aop:config 标签开始aop配置
3)使用 aop: aspect标签,开始切面配置 id:切面的id ref:引用通知bean的id
4)使用 aop:before 配置前置通知
method:指定通知类中哪个方法是前置通知
pointcut:用于指定切入点表达式
写法:execution(访问修饰符 返回值 包名.类名.方法名( 参数列表 ))
访问修饰符可以省略
返回值用 * ,可以用来表示任意返回值
execution(* anli.AccountServiceImpl.save())
包名用 * ,可以表示任意包。几级包就用几个 *
包名也可以用 .. 表示当前包及其子包
类名和方法名也可以使用 *,意思同上
参数基本类型:execution(* anli.AccountServiceImpl.*(int))
参数引用类型:execution(* anli.AccountServiceImpl.*(java.lang.String))
参数列表可以使用 * ,但是必须有参数:execution(* anli.AccountServiceImpl.*(*))
参数列表可以用 .. ,表示有无参数均可:execution(* anli.AccountServiceImpl.*(..))
实际开发中:execution(* anli.*.*(..)) 可以根据实际需求进行修改
bean.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置业务层 -->
<bean id="accountService" class="anli.AccountServiceImpl"></bean>
<bean id="logger" class="utils.Logger"></bean>
<aop:config>
<aop:aspect id="LogAdvice" ref="logger">
<aop:before method="beforeLog" pointcut="execution(public void anli.AccountServiceImpl.save())"/>
</aop:aspect>
</aop:config>
</beans>
AccountServiceImpl:
package anli;
public class AccountServiceImpl implements IAccountService{
@Override
public void save() {
System.out.println("保存了账户");
}
@Override
public void update(int id) {
System.out.println("修改了账户");
}
@Override
public int delete() {
System.out.println("删除了账户");
return 0;
}
}
Client:
package anli;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("anli/bean.xml");
IAccountService as = ac.getBean("accountService", IAccountService.class);
as.save();
}
}
Logger:
package utils;
//一个通知类,对业务层方法进行增强,模拟记录日志
public class Logger {
public void beforeLog() {
System.out.println("Logger开始记录日志");
}
}
2、常见的四种通知类型
前置通知:它永远在切入点方法执行之前执行
后置通知:当切入点方法正常执行后,再执行
异常通知:切入点方法执行出现异常后执行
最终通知:无论切入点是否正常执行,它都会在最后执行
后置通知和异常通知只能执行一个
<aop:config>
<aop:aspect id="LogAdvice" ref="logger">
<!-- 前置通知 -->
<aop:before method="beforeLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
<!-- 后置通知 -->
<aop:after-returning method="afterLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
<!-- 异常通知 -->
<aop:after-throwing method="exceptionLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
<!-- 最终通知 -->
<aop:after method="finalLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
</aop:aspect>
</aop:config>
Logger
package utils;
//一个通知类,对业务层方法进行增强,模拟记录日志
public class Logger {
public void beforeLog() {
System.out.println("Logger开始记录日志");
}
public void afterLog() {
System.out.println("后置");
}
public void exceptionLog() {
System.out.println("异常");
}
public void finalLog() {
System.out.println("最终");
}
}
3、环绕通知
<!-- 环绕通知 -->
<aop:around method="aroundLog" pointcut="execution(* anli.AccountServiceImpl.*(..))"/>
问题:
为什么切入点save()没有执行?
解决:
分析:环绕通知没有调用切入点方法
public Object aroundLog(ProceedingJoinPoint pjp) {
Object obj = null;
try {
System.out.println("前置");
//获取方法所需的参数
Object[] args = pjp.getArgs();
//process明确调用切入点方法
obj = pjp.proceed(args);
System.out.println("后置");
} catch (Throwable e) {
System.out.println("异常");
e.printStackTrace();
}finally {
System.out.println("最终");
}
return obj;
}
它是spring为我们提供的一种可以在代码中手动控制通知何时执行的方式,上四种是通过配置的方式。
4、通用化切入点表达式
使用aop:pointcut标签可以配置通用切入点表达式,写在aspect内部,只能当前切面使用。如果要想所要切面使用,写到aop:aspect外面
<aop:config>
<aop:pointcut expression="execution(* anli.AccountServiceImpl.*(..))" id="pc"/>
<aop:aspect id="LogAdvice" ref="logger">
<!-- 环绕通知 -->
<aop:around method="aroundLog" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
5、spring基于注解的aop配置
bean.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="anli"></context:component-scan>
<!-- 开启spring注解对aop的支持 -->
<aop:aspectj-autoproxy/>
</beans>
package anli;
import org.springframework.stereotype.Service;
@Service("accountService")
public class AccountServiceImpl implements IAccountService{
@Override
public void save() {
System.out.println("保存了账户");
}
@Override
public void update(int id) {
System.out.println("修改了账户");
}
@Override
public int delete() {
System.out.println("删除了账户");
return 0;
}
}
package anli;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
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("logger")
@Aspect
public class Logger {
@Pointcut("execution(* anli.AccountServiceImpl.*(..))")
private void pt1() {}
@Before("pt1()")
public void beforeLog() {
System.out.println("Logger开始记录日志");
}
@AfterReturning("pt1()")
public void afterLog() {
System.out.println("后置");
}
@AfterThrowing("pt1()")
public void exceptionLog() {
System.out.println("异常");
}
@After("pt1()")
public void finalLog() {
System.out.println("最终");
}
@Around("pt1()")
public Object aroundLog(ProceedingJoinPoint pjp) {
Object obj = null;
try {
System.out.println("前置");
//获取方法所需的参数
Object[] args = pjp.getArgs();
//process明确调用切入点方法
obj = pjp.proceed(args);
System.out.println("后置");
} catch (Throwable e) {
System.out.println("异常");
e.printStackTrace();
}finally {
System.out.println("最终");
}
return obj;
}
}
纯注解
package anli;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Client {
public static void main(String[] args) {
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
IAccountService as = ac.getBean("accountService", IAccountService.class);
as.save();
}
}
package anli;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("anli")
@EnableAspectJAutoProxy
public class SpringConfig {
}