AOP-Spring实现方式
AOP不是Spring框架特有的,Spring只是支持AOP编程的框架之一。而SpringAOP是一种基于方法拦截的AOP(有些AOP能够对方法的参数进行拦截)。
下文主要介绍使用注解方式@AspectJ实现AOP的拦截功能(还有不常用的XML配置方式)。
被拦截的对象接口:RoleService.java
public interface RoleService { void printRole(Role role); }
被拦截的对象实现类:RoleServiceImpl.java。printRole()为连接点。
@Component public class RoleServiceImpl implements RoleService { public void printRole(Role role) { System.out.println("打印用户信息:用户id:" + role.getId() + ";用户名称:" + role.getName() + ";用户备注:" + role.getNote()); } }
切面类:RoleAspect.java。
切面类对于动态代理概念而言,如同一个‘拦截器’。在Spring中只要使用@Aspect注解了一个类,那么SpringIOC容器就会认为这是一个切面了。
spring通过execution里的正则表达式判断是否需要拦截你的方法。
@Aspect public class RoleAspect { // execution:代表方法执行时会触发;*:代表任意返回类型;类的全限定名;(..):任意的参数 @Pointcut("execution(* com.xxx.service.impl.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 pjp) { System.out.println("around before......"); try { pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("around after......"); } }
测试AOP:
配置SpringBean:AopConfig.java
@Configuration @EnableAspectJAutoProxy // 启用AspectJ框架的自动代理,这个时候Spring才会自动生成动态代理对象 @ComponentScan("com.ssm.chapter11.game") public class AopConfig { @Bean public RoleAspect getRoleAspect() { return new RoleAspect(); // 生成一个切面实例 } }
main入口:Main.java
public class Main { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class); RoleService roleService = ctx.getBean(RoleService.class); Role role = new Role(1L, "role_name_1", "note_1"); roleService.printRole(role); } }
SpringAOP原则:
当类存在实现接口时,Spring提供JDK动态代理,从而织入各种通知。若不存在,采用CGLIB动态代理。
各概念:
目标对象:target。被通知/横切的对象。
代理:proxy。切面、通知、目标混合之后的对象。
连接点:joinpoint。目标对象上所定义的被pointcut过滤后的方法。通知要插入代码的具体位置。上述代码中的printRole()方法就是joinpoint。
切入点:pointcut。AOP通过pointcut定位到特定的joinpoint。例上述代码中的:execution(* com.xxx.service.impl.RoleServiceImpl.printRole(..))。
通知:advice。某个具体的joinpoint采取的行为。切面对象完成的工作(非业务代码),即对切入点增强的内容。例上述代码中的:System.out.println("xxx ....")。
通知器:advisor。由一个pointcut和一个advice组成。例上述代码中:@Before("print()") public void before(){...........}。一个方法表示一个通知器。
切面:aspect。pointcut+advice。上例中的:RoleAspect.java切面类。
织入:weaving。把增强代码应用到目标上,生成代理对象的过程。
通知advice分类:
before: 通知方法 在目标方法 调用前 执行;
after: 通知方法 在目标方法 返回或异常后 执行;
after-returning:通知方法 在目标方法 返回后 执行;
after-throwing:通知方法 在目标方法 抛出异常后 执行;
around: 通知方法 将目标方法 封装起来;
织入weaving时期:
编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器。它可以在目标类引入应用之前引入增强目标类的字节码。
运行期:切面在应用运行的某个时期被织入。一般情况下,在织入切面时,AOP容器就会为目标对象动态创建一个代理对象。SpringAOP采用的就是这种。