-
准备
Spring是方法级别的AOP框架,所以我们以某个类的某个方法作为连接点,说白了就是拦截哪个方法,来织入(也就是插入)对应AOP通知。
实体类 :Role
package com.cmb.test;
public class Role {
private Long id;
private String name;
private String note;
public Role(){
}
public Role(Long id, String name, String note){
this.id = id;
this.name = name;
this.note = note;
}
public Long getId(){
return id;
}
public void setId(Long id){
this.id = id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getNote(){
return note;
}
public void setNote(String note){
this.note = note;
}
}
给出一个接口:RoleService
package com.cmb.test;
public interface RoleService {
public void printRole(Role role);
}
再给出实现类:RoleServiceImpl
package com.cmb.test;
import org.springframework.stereotype.Component;
@Component
public class RoleServiceImpl implements RoleService {
public void printRole(Role role) {
System.out.println( "{id=" + role.getId()
+ ",roleName = " + role.getName()
+ ", note = " + role.getNote()
);
}
}
其中,RoleServiceImpl中的printRole方法会作为AOP的连接点,后续会为之生成动态代理对象,并且拦截这个方法,然后产生各种AOP通知方法。
2. 创建切面
在Spring中用@Aspect注解了一个类,那么SpringIoC就会认为这个类是一个切面了。
RoleAspect:
package com.cmb.test;
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;
@Aspect
public class RoleAspect {
@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")
public void print(){
}
@Before("print()")
public void before(){
System.out.println("before....");
}
@After("print()")
public void after(){
System.out.println("after....");
}
@AfterReturning("print()")
public void afterReturning(){
System.out.println("afterReturning....");
}
@AfterThrowing("print()")
public void afterThrowing(){
System.out.println("afterThrowing....");
}
/*@Around("print()")
public void around(ProceedingJoinPoint jp){
System.out.println("around before ...");
try{
jp.proceed();
}catch(Throwable e){
e.printStackTrace();
}
System.out.println("around after ...");
}*/
}
|
注解 |
通知 |
备注 |
|
@Before |
在被代理对象放入方法调用前调用 |
前置通知 |
|
@Around |
将被代理对象的方法封装起来,并用环绕通知取代它 |
环绕通知,它将覆盖原有方法,但是允许你通过反射调用原有方法 |
|
@After |
在被代理对象放入方法调用后调用 |
后置通知 |
|
@AfterReturning |
在被代理对象放入方法正常返回后调用 |
返回通知,要求被代理对象的方法执行过程中没有发生异常 |
|
@AfterThrowing |
在被代理对象放入方法抛出异常后调用 |
异常通知,要求被代理对象的方法执行过程中产生异常 |
此外,代码:
@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))")
- execution:代表执行方法的时候会触发;
- *:代表任意返回类型;
- com.cmb.test.RoleServiceImpl:代表类的全限定名
- printRole:被拦截方法
- (..):任意的参数;
Spring AOP切面有两种写法:
|
1 |
@Before("execution(* com.cmb.test.RoleServiceImpl.printRole(..))") @After("execution(* com.cmb.test.RoleServiceImpl.printRole(..))") @AfterReturning("execution(* com.cmb.test.RoleServiceImpl.printRole(..))") … |
|
2 |
@Pointcut("execution(* com.cmb.test.RoleServiceImpl.printRole(..))") @ Before(“print()”) @After(“print()”) … |
其中第一种比较麻烦,需要把类的限定,写多次,本例中采用的是第二种写法,因此引入注解@Pointcut.
3. 测试AOP
配置Spring bean :AopConfig
package com.cmb.test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.cmb.test")
public class AopConfig {
@Bean
public RoleAspect getRoleAspect(){
return new RoleAspect();
}
}
@EnableAspectJAutoProxy:这个注解代表启动AspectJ框架的自动代理,这个时候Spring才会生成动态代理对象,进而可以使用AOP, getRoleAspect()方法则会生成一个切面实例。
测试函数: GameMain2
package com.cmb.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class GameMain2 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
RoleService roleService = (RoleService) ctx.getBean(RoleService.class);
Role role = new Role(1L, "role_name_1", "note_1");
roleService.printRole(role);
System.out.println("#######################################");
//测试异常通知
role = null;
roleService.printRole(role);
}
}
结果如下:
八月 17, 2018 9:31:57 上午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@300ffa5d: startup date [Fri Aug 17 09:31:57 CST 2018]; root of context hierarchy
before....
{id=1,roleName = role_name_1, note = note_1
after....
afterReturning....
#######################################
before....
after....
afterThrowing....
Exception in thread "main" java.lang.NullPointerException
显然切面的通知已经通过AOP织入到约定的流程中了,这个时候就可以使用AOP去处理一些需要切面的场景了。
4. 环绕通知
环绕通知是Spring AOP中最强大的通知,它可以同时实现前置通知和后置通知。它还可以保留调度被代理对象原有方法的功能。在切面RoleAspect中把最后一段代码注释去掉,运行,就是含有环绕通知的方法。
其中参数ProceedingJoinPoint,是Spring提供,可以反射连接点方法。
结果:
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@300ffa5d: startup date [Fri Aug 17 10:09:45 CST 2018]; root of context hierarchy
around before ...
before....
{id=1,roleName = role_name_1, note = note_1
around after ...
after....
afterReturning....
#######################################
around before ...
before....
java.lang.NullPointerException
....
around after ...
after....
afterReturning....
可以看出环绕通知使用jp.proceed();后,会先调度前置通知,然后才是反射切点方法,最后才是后置通知和返回/异常通知。
参考文献:《javaEE互联网轻量级框架整合开发》
本文介绍如何使用Spring AOP框架实现方法级别的切面编程。通过具体案例,详细讲解了AOP的基本概念,如连接点、通知类型等,并演示了如何创建切面、配置AOP及测试AOP功能。
1092

被折叠的 条评论
为什么被折叠?



