1、实现MethodBeforeAdvice等接口
pom.xml添加spring核心依赖:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
在Spring中,org.springframework.aop包下有四个接口,分别是MethodBeforeAdvice(前置通知)、AfterReturningAdvice(后置通知)、MethodInterceptor(环绕通知)、ThrowsAdvice(异常通知),其中,前三个接口都有对应的实现方法,分别实现后就可以在对应的通知方法中添加功能,但是ThrowsAdvice异常通知没有实现方法,所以需要自定义一个方法,不过对方法名有规定,必须写成afterThrowing,代码如下:
定义一个切面类UserServiceAdvice:
public class UserServiceAspect implements MethodBeforeAdvice, AfterReturningAdvice,
MethodInterceptor, ThrowsAdvice {
/**
* 后置通知
* @param o
* @param method
* @param objects
* @param o1
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("后置通知");
}
/**
* 前置通知
* @param method
* @param objects
* @param o
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知。。。");
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("环绕通知前。。。");
Object returnVal = methodInvocation.proceed();
System.out.println("环绕通知后。。。");
return returnVal;
}
/**
* 异常通知,参照MethodBeforeAdvice,
* 该方法的方法名必须叫做afterThrowing,
* method,args,target这三个参数可以省略,要么全部声明
* Exception必须保留
* @param e
*/
public void afterThrowing(Exception e){
System.out.println("产生了异常:"+e.getMessage());
}
}
在resources目录下创建一个Spring配置文件applicationContext.xml:
在这个配置文件中主要是做对bean的配置和对aop的配置。
<?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="userService" class="edu.nf.ch12.service.impl.UserServiceImpl" />
<!-- 切面 -->
<bean id="userServiceAspect" class="edu.nf.ch12.service.aspect.UserServiceAspect" />
<!-- aop配置, -->
<aop:config proxy-target-class="false">
<!-- 配置切入点,使用aspectJ的切入点表达式,
表达式语法:
1、execution(访问修饰符 方法返回值 包名.类名.方法名(参数类型))
execution是切入到方法级别的
2、within(访问修饰符 方法返回值 包名.类名)
within是切入到类级别的
说明:访问修饰符可以省略,
方法返回值、包名、类名、方法名、可以使用*号进行统配,
方法参数可以指定参数的类型,也可以使用".."来标识任意类型和个数的参数
例如:execution(* edu.nf.ch12.service.impl.*.*(..))
表示edu.nf.ch12.service.impl包下的所有类,以及任意返回值类和任意参数类型和个数的方法都会匹配-->
<aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch12.service.impl.UserServiceImpl.say(..))"/>
<!-- 配置通知器(Advisor),其实就是切面
advice-ref引用上面定义的切面的id
pointcut-ref引用上面定义的切入点的id-->
<aop:advisor advice-ref="userServiceAspect" pointcut-ref="myPointcut"/>
<!-- 当有多个切面,但是又不想共用一个切入点表达式的时候,那么使用pointcut属性来重新制定切入点表达式 -->
<aop:advisor advice-ref="demoAspect" pointcut="execution(* edu.nf.ch12.service.impl.UserServiceImpl.run())"/>
</aop:config>
</beans>
2、使用AspectJ配置,不实现任何接口
(1)使用配置文件配置切面
当使用AspectJ配置时,不需要实现任何接口,因为对这些通知的方法都是在配置文件中配置和绑定的。
创建一个切面类:
public class UserServiceAspect {
/**
* 前置通知
* @param jp 连接点,通过这个连接点可以获取目标方法
*/
public void before(JoinPoint jp){
System.out.println("前置通知,目标方法参数:" + jp.getArgs()[0]);
}
/**
* 环绕通知
* @param pjp 连接点,可以获取目标方法参数以及方法信息以及调用目标方法等等
*/
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知前。。。");
//获取目标方法的的Method对象
MethodSignature ms = (MethodSignature)pjp.getSignature();
Method method = ms.getMethod();
System.out.println("当前调用的目标方法:" + method.getName());
//调用目标方法
Object returnVal = pjp.proceed();
System.out.println("环绕通知后。。。");
return returnVal;
}
/**
* 后置通知
* @param returnVal 目标方法的返回值
*/
public void afterReturning(String returnVal){
System.out.println("后置通知,返回参数:" + returnVal);
}
/**
* 异常通知
* @param e 目标方法产生的异常对象
*/
public void afterThrowing(Throwable e){
System.out.println("异常通知,异常信息:" + e.getMessage());
}
/**
* 最终通知
*/
public void after(){
System.out.println("最终通知");
}
}
在resources目录中创建一个Spring配置文件applicationContext.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="userService" class="edu.nf.ch13.service.impl.UserServiceImpl"/>
<!-- 定义切面 -->
<bean id="userServiceAspect" class="edu.nf.ch13.service.aspect.UserServiceAspect"/>
<!-- 配置AOP,基于AspectJ -->
<aop:config>
<aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch13.service.impl.UserServiceImpl.*(..))"/>
<!-- 装配切面,ref引用上面配置的切面的id -->
<aop:aspect ref="userServiceAspect">
<!-- 装配通知,method对应通知的方法名,pointcut-ref引用上面定义的切入点的id
如果不同的通知想使用不同的切入点,那么使用pointcut属性进行自定义 -->
<!-- 前置通知 -->
<aop:before method="before" pointcut-ref="myPointcut"/>
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="myPointcut"/>
<!-- 后置通知,returning属性指定后置通知方法的参数名(参数名称要一致) -->
<aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
<!-- 异常通知,throwing属性指定异常通知方法的参数名(名称要一致) -->
<aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
(2)使用注解和配置类配置切面
在上面的applicationContext.xml配置文件中,对基于AspectJ的aop配置完全可以使用注解来配置,
在切面类中:
/**
* AspectJ提供了所有通知注解,这些注解都有一个value属性,
* 属性指定的是@Pointcut注解所标注的方法名,即pointcut(),
* 如:@Around("pointcut()")
* 如果不同通知想使用不同的切入点那么可以直接在value属性中自定义
* 如:@Before("execution(* edu.nf.ch14.service.impl.UserServiceImpl.say())")
*/
@Component
/**
* @Aspect注解标识当前类为一个切面类
*/
@Aspect
public class UserServiceAspect extends AbstractAsPect {
/**
* 声明一个切入点,@Pointcut声明在一个方法上,在这里将这个方法放在了AbstractAspect这个类中
*/
// @Pointcut("execution(* edu.nf.ch15.service.impl.UserServiceImpl.*(..))")
// public void pointcut(){
//
// }
/**
* 前置通知
* @param jp 连接点,通过这个连接点可以获取目标方法
* pointcut()指定的是切入点注解所标注的方法名
* @Before注解的value属性指定的是切入点注解所标注的方法名,
* 结果不同通知想使用不同的切入点
*/
@Before("execution(* edu.nf.ch15.service.impl.UserServiceImpl.say())")
public void before(JoinPoint jp){
System.out.println("前置通知,目标方法参数:" + jp.getArgs()[0]);
}
/**
* 环绕通知
* @param pjp 连接点,可以获取目标方法参数以及方法信息以及调用目标方法等等
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知前。。。");
//获取目标方法的的Method对象
MethodSignature ms = (MethodSignature)pjp.getSignature();
Method method = ms.getMethod();
System.out.println("当前调用的目标方法:" + method.getName());
//调用目标方法
Object returnVal = pjp.proceed();
System.out.println("环绕通知后。。。");
return returnVal;
}
/**
* 后置通知
* @param returnVal 目标方法的返回值
*/
@AfterReturning(value = "pointcut()", returning = "returnVal")
public void afterReturning(String returnVal){
System.out.println("后置通知,返回参数:" + returnVal);
}
/**
* 异常通知
* @param e 目标方法产生的异常对象
*/
@AfterThrowing(value = "pointcut()", throwing = "e")
public void afterThrowing(Throwable e){
System.out.println("异常通知,异常信息:" + e.getMessage());
}
/**
* 最终通知
*/
@After("pointcut()")
public void after(){
System.out.println("最终通知");
}
}
创建一个配置类,可以做到0配置,
SpringConfig:
@Configuration
@ComponentScan("edu.nf.ch15")
/**
* 启用AspectJ注解处理器,等同于xml中的<aop:aspectj-autoproxy/>
*/
@EnableAspectJAutoProxy
public class SpringConfig {
}