Javassm - AOP2
AOP面向切片2
使用接口实现AOP
我们来看看如何使用Advice接口实现AOP。
它与我们之前学习的动态代理更接近一些,比如在方法开始执行之前或是执行之后会去调用我们实现的接口,首先我们需要将一个类实现Advice接口,只有实现此接口,才可以被通知
比如我们这里使用MethodBeforeAdvice表示是一个在方法执行之前的动作:
public class StudentAOP implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("通过Advice实现AOP");
}
}
我们发现,方法中包括了很多的参数,其中args代表的是方法执行前得到的实参列表,还有target表示执行此方法的实例对象。
运行之后,效果和之前是一样的,但是在这里我们就可以快速获取到更多信息。
还是以简单的study方法为例:
public class Student {
public void study(){
System.out.println("我是学习方法!");
}
}
xml不需要配置 <aop:aspect> 只需要 <aop:advisor> 来指明对应的bean和切入点
<bean id="student" class="org.example.entity.Student"/>
<bean id="studentAOP" class="org.example.entity.StudentAOP"/>
<aop:config>
<aop:pointcut id="test" expression="execution(* org.example.entity.Student.study())"/>
<!-- 这里只需要添加我们刚刚写好的advisor就可以了,注意是Bean的名字 -->
<aop:advisor advice-ref="studentAOP" pointcut-ref="test"/>
</aop:config>

除了此接口以外,还有其他的接口,比如AfterReturningAdvice就需要实现一个方法执行之后的操作:
public class StudentAOP implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("通过Advice实现AOP");
}
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("我是方法执行之后的结果,方法返回值为:"+returnValue);
}
}
因为使用的是接口,就非常方便,直接写一起,配置文件都不需要改了
我们也可以使用MethodInterceptor(同样也是Advice的子接口)进行更加环绕那样的自定义的增强,它用起来就真的像代理一样,例子如下:
public class Student {
public String study(){
System.out.println("我是学习方法!");
return "lbwnb";
}
}
public class StudentAOP implements MethodInterceptor { //实现MethodInterceptor接口
@Override
public Object invoke(MethodInvocation invocation) throws Throwable { //invoke方法就是代理方法
Object value = invocation.proceed(); //跟之前一样,需要手动proceed()才能调用原方法
return value+"增强";
}
}
使用起来还是挺简单的。
使用注解实现AOP
首先我们需要在主类添加@EnableAspectJAutoProxy注解,开启AOP注解支持:
@EnableAspectJAutoProxy
@ComponentScan("org.example.entity")
@Configuration
public class MainConfiguration {
}
还是熟悉的玩法,类上直接添加@Component快速注册Bean:
@Component
public class Student {
public void study(){
System.out.println("我是学习方法!");
}
}
定义增强方法 (@Aspect @Before)
接着我们需要在定义AOP增强操作的类上添加@Aspect注解和@Component将其注册为Bean即可,就像我们之前在配置文件中也要将其注册为Bean那样:
@Aspect
@Component
public class StudentAOP {
}
接着,我们可以在里面编写增强方法,并将此方法添加到一个切点中
比如我们希望在Student的study方法执行之前执行我们的before方法:
那么只需要添加@Before注解即可:
@Before("execution(* org.example.entity.Student.study())") //execution写法跟之前一样
public void before(){
System.out.println("我是之前执行的内容!");
}
这样,这个方法就会在指定方法执行之前执行了:
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
Student bean = context.getBean(Student.class);
bean.study();
}
添加JoinPoint参数
同样的,我们可以为其添加JoinPoint参数来获取切入点信息,使用方法跟之前一样:
@Before("execution(* org.example.entity.Student.study())")
public void before(JoinPoint point){
System.out.println("参数列表:"+ Arrays.toString(point.getArgs()));
System.out.println("我是之前执行的内容!");
}
命名绑定模式
为了更方便,我们还可以直接将参数放入,比如:
public void study(String str){
System.out.println("我是学习方法!");
}
使用命名绑定模式,可以快速得到原方法的参数:
@Before(value = "execution(* org.example.entity.Student.study(..)) && args(str)", argNames = "str")
// 命名绑定模式就是根据下面的方法参数列表进行匹配
// 这里args指明参数,注意需要跟原方法保持一致,然后在argNames中指明
public void before(String str){
System.out.println(str);
//可以快速得到传入的参数
System.out.println("我是之前执行的内容!");
}
其他注解
除了@Before,还有很多可以直接使用的注解,比如@AfterReturning、@AfterThrowing等,比如@AfterReturning:
public String study(){
System.out.println("我是学习方法!");
return "lbwnb";
}
@AfterReturning(value = "execution(* org.example.entity.Student.study())", argNames = "returnVal", returning = "returnVal") //使用returning指定接收方法返回值的参数returnVal
public void afterReturn(Object returnVal){
System.out.println("返回值是:"+returnVal);
}
环绕
同样的,环绕也可以直接通过注解声明:
@Around("execution(* com.test.bean.Student.test(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("方法执行之前!");
Object val = point.proceed();
System.out.println("方法执行之后!");
return val;
}
实际上,无论是使用注解或是XML配置,我们要做的流程都是一样的,在之后的学习中,我们还会遇到更多需要使用AOP的地方。
