Продвинутые навыки использования АОП основ SpringBoot

Spring

Для получения дополнительной связанной информации, проверьте:spring.hhui.top/

предыдущий пост в блоге190301-SpringBoot Basics AOP Основные сведения об использовании положенияВводится простое использование АОП. В конце статьи предлагается решить несколько проблем. В этом сообщении блога основное внимание будет уделено предыдущим проблемам и приведены дополнительные инструкции по использованию АОП.

I. Продвинутые навыки

1. Метод перехвата аннотаций

Предыдущая статья в основном знакомит с перехватом соответствующего метода на основе регулярных выражений, далее я продемонстрирую, как перехватить целевой метод через аннотации, и реализация относительно проста.

Сначала создайте аннотацию

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnoDot {
}

Затем добавьте аннотации к целевому методу, вот объяснение с помощью проекта в предыдущем сообщении в блоге, создайте новыйcom.git.hui.boot.aop.demo2.AnoDemoBean, обратите внимание, что этот путь к пакету не будет использоваться предыдущимAnoAspectОпределенные советы перехватывают, цель создания нового пути пакета здесь состоит в том, чтобы максимально уменьшить количество помех.

@Component
public class AnoDemoBean {
    @AnoDot
    public String genUUID(long time) {
        try {
            System.out.println("in genUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in genUUID finally!");
        }
    }
}

Далее определите соответствующий совет, непосредственно передAnoAspect(Не беда, если вы не знаете предыдущий текст, соответствующие классы кода размещены ниже, и содержание предыдущего класса не имеет ничего общего с содержанием этого раздела)

@Aspect
@Component
public class AnoAspect {
    @Before("@annotation(AnoDot)")
    public void anoBefore() {
        System.out.println("AnoAspect ");
    }
}

тестовый код

@SpringBootApplication
public class Application {
    private AnoDemoBean anoDemoBean;

    public Application(AnoDemoBean anoDemoBean) {
        this.anoDemoBean = anoDemoBean;
        this.anoDemoBean();
    }

    private void anoDemoBean() {
        System.out.println(">>>>>>>" + anoDemoBean.genUUID(System.currentTimeMillis()));
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Вывод выглядит следующим образом: перед выполнением целевого метода сначала будет выполнена логика в совете до.

AnoAspect 
in genUUID before process!
in genUUID finally!
>>>>>>>3a5d749d-d94c-4fc0-a7a3-12fd97f3e1fa|1551513443644

2. Перехват нескольких советов

При выполнении метода, если есть несколько советов, удовлетворяющих правилу перехвата, будут ли они все активированы? Из предыдущего поста в блоге мы знаем, что можно перехватывать разные типы советов, но что, если есть несколько советов одного типа?

Основываясь на предыдущем сообщении в блоге, давайте расширимcom.git.hui.boot.aop.demo.DemoBean

@Component
public class DemoBean {
    @AnoDot
    public String genUUID(long time) {
        try {
            System.out.println("in genUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in genUUID finally!");
        }
    }
}

Соответствующее содержание тестового аспекта выглядит следующим образом

@Aspect
@Component
public class AnoAspect {

    @Before("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("do in Aspect before method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
    }

    @Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))")
    public void point() {
    }

    @After("point()")
    public void doAfter(JoinPoint joinPoint) {
        System.out.println("do in Aspect after method called! args: " + JSON.toJSONString(joinPoint.getArgs()));
    }

    /**
     * 执行完毕之后,通过 args指定参数;通过 returning 指定返回的结果,要求返回值类型匹配
     *
     * @param time
     * @param result
     */
    @AfterReturning(value = "point() && args(time)", returning = "result")
    public void doAfterReturning(long time, String result) {
        System.out.println("do in Aspect after method return! args: " + time + " ans: " + result);
    }

    @Around("point()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("do in Aspect around ------ before");
        Object ans = joinPoint.proceed();
        System.out.println("do in Aspect around ------- over! ans: " + ans);
        return ans;
    }

    @Before("point()")
    public void sameBefore() {
        System.out.println("SameAspect");
    }

    @Before("@annotation(AnoDot)")
    public void anoBefore() {
        System.out.println("AnoAspect");
    }
}

Код теста выглядит следующим образом

@SpringBootApplication
public class Application {
    private DemoBean demoBean;

    public Application(DemoBean demoBean) {
        this.demoBean = demoBean;
        this.demoBean();
    }

    private void demoBean() {
        System.out.println(">>>>> " + demoBean.genUUID(System.currentTimeMillis()));
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Вывод следующий, все аспекты выполняются, то есть до тех пор, пока совет соответствует условиям, он будет перехвачен

do in Aspect around ------ before
AnoAspect
do in Aspect before method called! args: [1551520547268]
SameAspect
in genUUID before process!
in genUUID finally!
do in Aspect around ------- over! ans: 5f6a5616-f558-4ac9-ba4b-b4360d7dc238|1551520547268
do in Aspect after method called! args: [1551520547268]
do in Aspect after method return! args: 1551520547268 ans: 5f6a5616-f558-4ac9-ba4b-b4360d7dc238|1551520547268
>>>>> 5f6a5616-f558-4ac9-ba4b-b4360d7dc238|1551520547268

3. Вложенный перехват

Есть несколько случаев вложенности, давайте рассмотрим первый

А. Если вызывающий метод не соответствует правилам перехвата, вызовите другие методы в этом классе, соответствующие условиям перехвата.

Здесь мы продолжаем симуляцию с помощью бобов в первой секции, вAnoDemoBeanДобавьте метод в класс

@Component
public class AnoDemoBean {

    public String randUUID(long time) {
        try {
            System.out.println("in randUUID start!");
            return genUUID(time);
        } finally {
            System.out.println("in randUUID finally!");
        }
    }

    @AnoDot
    public String genUUID(long time) {
        try {
            System.out.println("in genUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in genUUID finally!");
        }
    }
}

Соответствующий фрагмент

@Aspect
@Component
public class NetAspect {

    @Around("@annotation(AnoDot)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("In NetAspect doAround before!");
        Object ans = joinPoint.proceed();
        System.out.println("In NetAspect doAround over! ans: " + ans);
        return ans;
    }
}

Затем тестовый пример необходимо изменить для прямого вызоваAnoDemoBean#randUUID, вам нужно увидеть внутренний вызов этого методаgenUUIDБудет ли он перехвачен поверхностью разреза?

@SpringBootApplication
public class Application {
    private AnoDemoBean anoDemoBean;

    public Application(AnoDemoBean anoDemoBean) {
        this.anoDemoBean = anoDemoBean;
        this.anoDemoBean();
    }

    private void anoDemoBean() {
        System.out.println(">>>>>>>" + anoDemoBean.randUUID(System.currentTimeMillis()));
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Вывод следующий, лог аспекта отсутствует, что указывает на то, что в этом сценарии он не будет перехвачен.

in randUUID start!
in genUUID before process!
in genUUID finally!
in randUUID finally!
>>>>>>>0c6a5ccf-30c0-4ac0-97f2-3dc063580f3d|1551522176035

б) Вызывающий метод не соответствует правилам перехвата и вызывает методы других классов, которые удовлетворяют условиям перехвата.

Предыдущий пример по-прежнему используется для иллюстрации, но он немного изменен.AnoDemoBean, вызовите метод DemoBean во втором разделе

Код DemoBean выглядит следующим образом

@AnoDot
public String genUUID(long time) {
    try {
        System.out.println("in DemoBean genUUID before process!");
        return UUID.randomUUID() + "|" + time;
    } finally {
        System.out.println("in DemoBean genUUID finally!");
    }
}

Тогда код AnoDemoBean выглядит следующим образом

@Component
public class AnoDemoBean {
    @Autowired
    private DemoBean demoBean;

    public String randUUID(long time) {
        try {
            System.out.println("in AnoDemoBean randUUID start!");
            return genUUID(time) + "<<<>>>" + demoBean.genUUID(time);
        } finally {
            System.out.println("in AnoDemoBean randUUID finally!");
        }
    }

    @AnoDot
    public String genUUID(long time) {
        try {
            System.out.println("in AnoDemoBean genUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in AnoDemoBean genUUID finally!");
        }
    }
}

Код теста точно такой же, как и раньше, а затем посмотрите на вывод

in AnoDemoBean randUUID start!
in AnoDemoBean genUUID before process!
in AnoDemoBean genUUID finally!
### 上面三行为 anoDemoBean#randUUID方法调用 anoDemoBean#genUUID方法的输出结果,可以看到没有切面执行的日志输出
### 下面的为调用 demoBean#genUUID 方法,可以看到切面(NetAspect#doAround)执行的日志
In NetAspect doAround before!
in DemoBean genUUID before process!
in DemoBean genUUID finally!
In NetAspect doAround over! ans: f35b8878-fbd0-4840-8fbe-5fef8eda5e31|1551522532092
### 最后是收尾
in AnoDemoBean randUUID finally!
>>>>>>>e516a35f-b85a-4cbd-aae0-fa97cdecab47|1551522532092<<<>>>f35b8878-fbd0-4840-8fbe-5fef8eda5e31|1551522532092

Из приведенного выше анализа журнала мы можем ясно видеть сравнение, вызов метода в этом классе, который удовлетворяет перехвату, не будет следовать логике аспекта; вызов метода в других классах, который удовлетворяет перехвату аспекта, будет следовать логике аспекта.

c. Вызывающий метод удовлетворяет условию перехвата аспекта и вызывает другие методы, удовлетворяющие условию перехвата аспекта.

Это немного похоже на два случая. Разница в том, что непосредственно вызываемый метод также удовлетворяет условиям перехвата аспектом. Наша главная забота заключается в том, войдет ли вложенный вызывающий метод в логику аспекта. быть изменен здесь. , прямо сказаноAnoDemoBean#randUUIDДобавьте аннотацию к методу и выполните его

@Component
public class AnoDemoBean {
    @Autowired
    private DemoBean demoBean;

    @AnoDot
    public String randUUID(long time) {
        try {
            System.out.println("in AnoDemoBean randUUID start!");
            return genUUID(time) + "<<<>>>" + demoBean.genUUID(time);
        } finally {
            System.out.println("in AnoDemoBean randUUID finally!");
        }
    }

    @AnoDot
    public String genUUID(long time) {
        try {
            System.out.println("in AnoDemoBean genUUID before process!");
            return UUID.randomUUID() + "|" + time;
        } finally {
            System.out.println("in AnoDemoBean genUUID finally!");
        }
    }
}

Вывод выглядит следующим образом

## 最外层的切面拦截的是 AnoDemoBean#randUUID 方法的执行
In NetAspect doAround before!
in AnoDemoBean randUUID start!
in AnoDemoBean genUUID before process!
in AnoDemoBean genUUID finally!
### 从跟上面三行的输出,可以知道内部调用的 AnoDemoBean#genUUID 即便满足切面拦截规则,也不会再次走切面逻辑
### 下面4行,表明其他类的方法,如果满足切面拦截规则,会进入到切面逻辑
In NetAspect doAround before!
in DemoBean genUUID before process!
in DemoBean genUUID finally!
In NetAspect doAround over! ans: d9df7388-2ef8-4b1a-acb5-6639c47f36ca|1551522969801

in AnoDemoBean randUUID finally!
In NetAspect doAround over! ans: cf350bc2-9a9a-4ef6-b496-c913d297c960|1551522969801<<<>>>d9df7388-2ef8-4b1a-acb5-6639c47f36ca|1551522969801
>>>>>>>cf350bc2-9a9a-4ef6-b496-c913d297c960|1551522969801<<<>>>d9df7388-2ef8-4b1a-acb5-6639c47f36ca|1551522969801

Вывод из результатов вывода, вывод

  • Целевой метод, который должен быть выполнен, если вызывается метод A в этом классе, который удовлетворяет правилам аспекта, логика аспекта не будет запущена во время выполнения метода A.
  • Целевой метод, который должен быть выполнен, если вызывается метод B в другом классе, который удовлетворяет правилам аспекта, логика аспекта будет запущена во время выполнения метода B.

4. Область действия метода перехвата АОП

Все перехваченные методы, протестированные выше, являются общедоступными, значит ли это, что можно перехватывать только общедоступные методы?

Из третьего раздела мы можем в основном увидеть, что приватный метод исключается первым, почему? Поскольку частный метод может быть вызван только внутри, а внутренний вызов не будет следовать логике аспекта, поэтому следующее, на что вам нужно обратить внимание, это область действия по умолчанию и защищенная область.

@Component
public class ScopeDemoBean {

    @AnoDot
    String defaultRandUUID(long time) {
        try {
            System.out.println(" in ScopeDemoBean defaultRandUUID before!");
            return UUID.randomUUID() + " | default | " + time;
        } finally {
            System.out.println(" in ScopeDemoBean defaultRandUUID finally!");
        }
    }

    @AnoDot
    protected String protectedRandUUID(long time) {
        try {
            System.out.println(" in ScopeDemoBean protectedRandUUID before!");
            return UUID.randomUUID() + " | protected | " + time;
        } finally {
            System.out.println(" in ScopeDemoBean protectedRandUUID finally!");
        }
    }

    @AnoDot
    private String privateRandUUID(long time) {
        try {
            System.out.println(" in ScopeDemoBean privateRandUUID before!");
            return UUID.randomUUID() + " | private | " + time;
        } finally {
            System.out.println(" in ScopeDemoBean privateRandUUID finally!");
        }
    }

}

Мы не используем методы этого класса напрямую, а используем предыдущийAnoDemoBean, ниже приводится случай вызова закрытого метода путем отражения

@Component
public class AnoDemoBean {
    @Autowired
    private ScopeDemoBean scopeDemoBean;

    public void scopeUUID(long time) {
        try {
            System.out.println("-------- default --------");
            String defaultAns = scopeDemoBean.defaultRandUUID(time);
            System.out.println("-------- default: " + defaultAns + " --------\n");


            System.out.println("-------- protected --------");
            String protectedAns = scopeDemoBean.protectedRandUUID(time);
            System.out.println("-------- protected: " + protectedAns + " --------\n");


            System.out.println("-------- private --------");
            Method method = ScopeDemoBean.class.getDeclaredMethod("privateRandUUID", long.class);
            method.setAccessible(true);
            String privateAns = (String) method.invoke(scopeDemoBean, time);
            System.out.println("-------- private: " + privateAns + " --------\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

прецедент

@SpringBootApplication
public class Application {
    private AnoDemoBean anoDemoBean;

    public Application(AnoDemoBean anoDemoBean) {
        this.anoDemoBean = anoDemoBean;
        this.anoDemoBean();
    }

    private void anoDemoBean() {
        anoDemoBean.scopeUUID(System.currentTimeMillis());
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

Результат выглядит следующим образом: с точки зрения печати журнала аспекты защищенных методов и методов по умолчанию пришли к выводу.

-------- default --------
In NetAspect doAround before!
 in ScopeDemoBean defaultRandUUID before!
 in ScopeDemoBean defaultRandUUID finally!
In NetAspect doAround over! ans: 2ad7e509-c62c-4f25-b68f-eb5e0b53196d | default | 1551524311537
-------- default: 2ad7e509-c62c-4f25-b68f-eb5e0b53196d | default | 1551524311537 --------

-------- protected --------
In NetAspect doAround before!
 in ScopeDemoBean protectedRandUUID before!
 in ScopeDemoBean protectedRandUUID finally!
In NetAspect doAround over! ans: 9eb339f8-9e71-4321-ab83-a8953d1b8ff8 | protected | 1551524311537
-------- protected: 9eb339f8-9e71-4321-ab83-a8953d1b8ff8 | protected | 1551524311537 --------

-------- private --------
 in ScopeDemoBean privateRandUUID before!
 in ScopeDemoBean privateRandUUID finally!
-------- private: 1826afac-6eca-4dc3-8edc-b4ca7146ce28 | private | 1551524311537 --------

5. Резюме

Этот пост в блоге относительно длинный, в основном из-за того, что тестовый код занимает много места, поэтому необходимо кратко подвести итоги и сделать четкую сводку, что удобно для тех, кто не хочет читать подробности и просто хочет получить окончательный вывод.

Метод перехвата аннотации:

  • Сначала объявите аннотацию
  • Аннотировать целевой метод
  • В разделе содержание совета выглядит так@Around("@annotation(AnoDot)")

Несколько ситуаций с советами:

  • Когда несколько советов удовлетворяют сценарию перехвата, все они будут выполнены.

Вложенная сцена

  • Целевой метод, который должен быть выполнен, если вызывается метод A в этом классе, который удовлетворяет правилам аспекта, логика аспекта не будет запущена во время выполнения метода A.
  • Целевой метод, который должен быть выполнен, если вызывается метод B в другом классе, который удовлетворяет правилам аспекта, логика аспекта будет запущена во время выполнения метода B.

объем

  • Методы в общедоступной, защищенной области действия по умолчанию могут быть перехвачены.

приоритет

Поскольку этот контент очень большой, его необходимо проводить отдельно.Основные категории следующие.

  • Тот же аспект, порядок выполнения разных советов
  • Порядок выполнения различных аспектов и советы
  • Тот же аспект, порядок исполнения того же совета

II. Другое

0. Проект

1. Серый блог

  • One Grey BlogПерсональный блогblog.hhui.top
  • Серый блог - специальный весенний блогspring.hhui.top

Серый личный блог, записывающий все посты блога по учебе и работе, приглашаю всех в гости

2. Заявление

Это не так хорошо, как письмо. Вышеупомянутое содержание чисто из семьи. Из-за ограниченных личных способностей неизбежно будут упущения и ошибки. Если вы обнаружите ошибки или у вас есть лучшие предложения, вы можете критиковать и поправьте их.

3. Сканируйте внимание

серый блог

QrCode

планета знаний

goals