Introductions (在 AspectJ 中被称为类型间声明)使一个切面能够声明 advice 对象实现一个给定的接口,并代表这些对象提供该接口的实现。

你可以通过使用 @DeclareParents注解来做一个 Introductions 。这个注解被用来声明匹配的类型有一个新的父类(因此而得名)。例如,给定一个名为 UsageTracked 的接口和一个名为 DefaultUsageTracked 的接口的实现,下面这个切面声明所有服务接口的实现者也实现 UsageTracked 接口(例如,通过 JMX 进行统计):

  1. @Aspect
  2. public class UsageTracking {
  3. @DeclareParents(value="com.xzy.myapp.service.*+", defaultImpl=DefaultUsageTracked.class)
  4. public static UsageTracked mixin;
  5. @Before("com.xyz.myapp.CommonPointcuts.businessService() && this(usageTracked)")
  6. public void recordUsage(UsageTracked usageTracked) {
  7. usageTracked.incrementUseCount();
  8. }
  9. }

要实现的接口由被注解字段的类型决定。@DeclareParents 注解的值属性是 AspectJ 类型模式。任何匹配类型的 bean 都会实现 UsageTracked 接口。注意,在前面例子的 @Before,服务 Bean 可以直接作为 UsageTracked 接口的实现。如果以编程方式访问一个 Bean, 你会写下以下内容:

  1. UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

整个例子如下

声明一个接口

  1. package cn.mrcode.study.springdocsread.aspect;
  2. /**
  3. * @author mrcode
  4. */
  5. public interface UsageTracked {
  6. void incrementUseCount();
  7. }

实现这个接口

  1. package cn.mrcode.study.springdocsread.aspect;
  2. import java.util.concurrent.atomic.AtomicInteger;
  3. /**
  4. * @author mrcode
  5. */
  6. public class UsageTrackedImpl implements UsageTracked {
  7. final AtomicInteger is = new AtomicInteger();
  8. @Override
  9. public void incrementUseCount() {
  10. System.out.println("执行了一次方法,当前是第 :" + is.getAndIncrement());
  11. }
  12. }

编写切面和 advice

  1. package cn.mrcode.study.springdocsread.aspect;
  2. import org.aspectj.lang.annotation.Aspect;
  3. import org.aspectj.lang.annotation.Before;
  4. import org.aspectj.lang.annotation.DeclareParents;
  5. import org.springframework.stereotype.Component;
  6. /**
  7. * @author mrcode
  8. */
  9. @Aspect
  10. @Component
  11. public class NotVeryUsefulAspect {
  12. @DeclareParents(value = "cn.mrcode.study.springdocsread.web.DemoService", defaultImpl = UsageTrackedImpl.class)
  13. public static UsageTracked mixin;
  14. @Before("execution(* cn.mrcode.study.springdocsread.web.DemoService.*(..)) && this(usageTracked)")
  15. public void recordUsage(UsageTracked usageTracked) {
  16. usageTracked.incrementUseCount();
  17. }
  18. }

上面切点要切入的服务类

  1. package cn.mrcode.study.springdocsread.web;
  2. import org.springframework.stereotype.Component;
  3. /**
  4. * @author mrcode
  5. */
  6. @Component
  7. public class DemoService {
  8. public void test() {
  9. }
  10. public void test(String name) {
  11. }
  12. }

@DeclareParents的含义就是:让 DemoService 实现了一个 UsageTrackedImpl 类实现的接口,而这个接口的实现就是 UsageTrackedImpl; 所以下面这个强制转换就能转换了

  1. package cn.mrcode.study.springdocsread;
  2. import org.aspectj.lang.annotation.Aspect;
  3. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. import cn.mrcode.study.springdocsread.aspect.UsageTracked;
  6. import cn.mrcode.study.springdocsread.web.AppConfig;
  7. import cn.mrcode.study.springdocsread.web.DemoService;
  8. public class TestDemo {
  9. public static void main(String[] args) {
  10. final AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  11. final DemoService bean = ctx.getBean(DemoService.class);
  12. bean.test();
  13. UsageTracked usageTracked = ((UsageTracked) bean);
  14. System.out.println(bean);
  15. }
  16. }