Spring [Spring AOP (тип уведомления, точечное выражение, многоаспектная конфигурация, конфигурация аннотаций AOP, собственная реализация Spring AOP)] (6) - исчерпывающее подробное объяснение (резюме обучения --- от входа до углубления)

Оглавление

 Тип SpringAOP_advice

Выражение точки SpringAOP_ 

SpringAOP_многоаспектная конфигурация 

Конфигурация аннотаций SpringAOP_ AOP

SpringAOP_Native Spring реализует АОП

SpringAOP_SchemaBased реализует АОП 


 Тип SpringAOP_advice

 В AOP есть следующие часто используемые типы уведомлений:

 1. Напишите метод уведомления

// 通知类
public class MyAspectAdvice {
    // 后置通知
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("切点方法名:" + joinPoint.getSignature().getName());
        System.out.println("目标对象:" + joinPoint.getTarget());
        System.out.println("打印日志" + joinPoint.getSignature().getName() + "方法被执行了!");
   }
    // 前置通知
    public void myBefore() {
        System.out.println("前置通知...");
   }
    // 异常通知
    public void myAfterThrowing(Exception ex) {
        System.out.println("异常通知...");
        System.err.println(ex.getMessage());
   }
    // 最终通知
    public void myAfter() {
        System.out.println("最终通知");
   }
    // 环绕通知
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前");
        Object obj = proceedingJoinPoint.proceed(); // 执行方法
        System.out.println("环绕后");
        return obj;
   }
}

2. Раздел конфигурации

<!-- 配置AOP -->
<aop:config>
    <!-- 配置切面 -->
    <aop:aspect ref="myAspectJAdvice">
        <!-- 配置切点 -->
        <aop:pointcut id="myPointcut" expression="execution(* com.tong.dao.UserDao.*(..))"/>
        <!-- 前置通知 -->
        <aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>
        <!-- 后置通知 -->
        <aop:after-returning method="myAfterReturning" pointcutref="myPointcut"/>
        <!-- 异常通知 -->
        <aop:after-throwing method="myAfterThrowing" pointcutref="myPointcut" throwing="ex"/>
        <!-- 最终通知 -->
        <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>
        <!-- 环绕通知 -->
        <aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>
    </aop:aspect>
</aop:config>

Выражение точки SpringAOP_ 

Чтобы использовать AspectJ, вам нужно использовать выражение pointcut для настройки местоположения pointcut, которое записывается следующим образом:

1. Стандартное написание: возвращаемое значение модификатора доступа имя пакета имя класса имя метода (список параметров)

2. Модификатор доступа можно опустить.

3. Возвращаемое значение использует * для представления любого типа.

4. Имя пакета использует * для представления любого пакета, многоуровневая структура пакета должна писать несколько *, использовать *.. для представления любой структуры пакета

5. Имена классов, и имена методов можно использовать с подстановочными знаками *.

6. Список параметров

基本数据类型直接写类型
引用类型写 包名.类名
* 表示匹配一个任意类型参数
.. 表示匹配任意类型任意个数的参数

7. Все подстановочные знаки: * *..*.*(..)

SpringAOP_многоаспектная конфигурация 

 Мы можем настроить несколько уведомлений для точки среза, чтобы сформировать несколько аспектов.Например, мы надеемся, что каждый метод слоя dao может печатать журналы и отправлять электронные письма после окончания:

1. Напишите уведомление для отправки по электронной почте:

public class MyAspectJAdvice2 {
    // 后置通知
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("发送邮件");
   }
}

2. Аспект конфигурации:

<!-- 通知对象 -->
<bean id="myAspectJAdvice" class="com.tong.advice.MyAspectAdvice"></bean>
<bean id="myAspectJAdvice2" class="com.tong.advice.MyAspectAdvice2"></bean>
<!-- 配置AOP -->
<aop:config>
    <!-- 配置切面 -->
    <aop:aspect ref="myAspectJAdvice">
        <!-- 配置切点 -->
        <aop:pointcut id="myPointcut" expression="execution(* *..*.*(..))"/>
        <!-- 后置通知 -->
        <aop:after-returning method="myAfterReturning" pointcutref="myPointcut"/>
    </aop:aspect>
    
    <aop:aspect ref="myAspectJAdvice2">
        <aop:pointcut id="myPointcut2" expression="execution(*com.tong.dao.UserDao.*(..))"/>
        <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut2"/>
    </aop:aspect>
</aop:config>

Конфигурация аннотаций SpringAOP_ AOP

Spring может использовать аннотации вместо файлов конфигурации для настройки аспектов:

1. Включить поддержку аннотаций АОП в xml

<!-- 开启注解配置Aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

2. Добавьте аннотацию @Aspect над классом уведомлений.

3. Добавьте комментарий над методом уведомления

@Before/@AfterReturning/@AfterThrowing/@After/@Around
@Aspect
@Component
public class MyAspectAdvice {
    // 后置通知
    @AfterReturning("execution(* com.tong.dao.UserDao.*(..))")
    public void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("切点方法名:" + joinPoint.getSignature().getName());
        System.out.println("目标对象:" + joinPoint.getTarget());
        System.out.println("打印日志" + joinPoint.getSignature().getName() + "方法被执行了!");
   }
    // 前置通知
    @Before("execution(* com.tong.dao.UserDao.*(..))")
    public void myBefore() {
        System.out.println("前置通知...");
   }
    // 异常通知
    @AfterThrowing(value = "execution(* com.tong.dao.UserDao.*(..))",throwing = "ex")
    public void myAfterThrowing(Exception ex) {
        System.out.println("异常通知...");
        System.err.println(ex.getMessage());
   }
    // 最终通知
    @After("execution(* com.tong.dao.UserDao.*(..))")
    public void myAfter() {
        System.out.println("最终通知");
   }
    // 环绕通知
    @Around("execution(* com.tong.dao.UserDao.*(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前");
        Object obj = proceedingJoinPoint.proceed(); // 执行方法
        System.out.println("环绕后");
        return obj;
   }
}

 4. Тест:

@Test
public void testAdd2(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
    UserDao userDao = (UserDao) ac.getBean("userDao");
    userDao.update();
}

Как единообразно настроить pointcut для всех методов класса:

1. Добавить точку конфигурации метода, вырезанную в классе уведомлений.

@Pointcut("execution(* com.tong.dao.UserDao.*(..))")
public void pointCut(){}

2. Используйте определенный pointcut в методе уведомления

@Before("pointCut()")
public void myBefore(JoinPoint joinPoint) {
    System.out.println("前置通知...");
}
@AfterReturning("pointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
    System.out.println("后置通知...");
}

Как класс конфигурации заменяет поддержку аннотаций АОП в XML?

Просто добавьте @EnableAspectJAutoProxy над классом конфигурации.

@Configuration
@ComponentScan("com.tong")
@EnableAspectJAutoProxy
public class SpringConfig {
}

SpringAOP_Native Spring реализует АОП

 Помимо AspectJ, Spring изначально поддерживает АОП.

1. Введите зависимости

<!-- AOP -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.13</version>
</dependency>

2. Напишите класс уведомлений

// Spring原生Aop的通知类
public class SpringAop implements MethodBeforeAdvice, AfterReturningAdvice,
ThrowsAdvice, MethodInterceptor {
    /**
     * 前置通知
     * @param method 目标方法
     * @param args 目标方法的参数列表
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
     }
    /**
     * 后置通知
     * @param returnValue 目标方法的返回值
     * @param method 目标方法
     * @param args 目标方法的参数列表
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知");
   }
    /**
     * 环绕通知
     * @param invocation 目标方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前");
        Object proceed = invocation.proceed();
        System.out.println("环绕后");
        return proceed;
   }
    /**
     * 异常通知
     * @param ex 异常对象
     */
    public void afterThrowing(Exception ex){
        System.out.println("发生异常了!");
    }
}

Когда Spring изначально реализует АОП, поддерживаются только четыре типа уведомлений:

3. Напишите класс конфигурации

<!-- 通知对象 -->
<bean id="springAop" class="com.tong.advice.SpringAop"></bean>
<!-- 配置代理对象 -->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!-- 配置目标对象 -->
    <property name="target" ref="userDao"></property>
    <!-- 配置通知 -->
    <property name="interceptorNames">
        <list>
            <value>springAop</value>
        </list>
    </property>
    <!-- 代理对象的生成方式 true:使用CGLib false:使用原生JDK生成-->
    <property name="proxyTargetClass" value="true"></property>
</bean>

4. Напишите тестовые классы

public class UserDaoTest2 {
    @Test
    public void testAdd(){
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
        UserDao userDao = (UserDao) ac.getBean("userDaoProxy"); // 获取的是代理对象
        userDao.update();
   }
}

SpringAOP_SchemaBased реализует АОП 

Метод конфигурации SchemaBased (базовый режим) относится к использованию собственного метода Spring для определения уведомлений и использованию платформы AspectJ для настройки аспектов.

1. Напишите класс уведомлений

public class SpringAop implements MethodBeforeAdvice, AfterReturningAdvice,ThrowsAdvice, MethodInterceptor {
    /**
     * 前置通知
     * @param method 目标方法
     * @param args 目标方法的参数列表
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
     }
    /**
     * 后置通知
     * @param returnValue 目标方法的返回值
     * @param method 目标方法
     * @param args 目标方法的参数列表
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知");
   }
    /**
     * 环绕通知
     * @param invocation 目标方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前");
        Object proceed = invocation.proceed();
        System.out.println("环绕后");
        return proceed;
   }
    /**
     * 异常通知
     * @param ex 异常对象
     */
    public void afterThrowing(Exception ex){
        System.out.println("发生异常了!");
   }
}

2. Раздел конфигурации

<!-- 通知对象 -->
<bean id="springAop2" class="com.tong.aop.SpringAop2"/>
<!-- 配置切面 -->
<aop:config>
    <!-- 配置切点-->
    <aop:pointcut id="myPointcut" expression="execution(* com.tong.dao.UserDao.*(..))"/>
    <!-- 配置切面:advice-ref:通知对象 pointcut-ref:切点 -->
    <aop:advisor advice-ref="springAop2" pointcut-ref="myPointcut"/>
</aop:config>

3. Тест

@Test
public void t6(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("aop3.xml");
    UserDao userDao = (UserDao) ac.getBean("userDao");
    userDao.add();
}

обзор:

 Как создаются IOC_objects

 Spring поможет нам создать bean-компоненты, так какой метод он вызывает внизу для их создания?

использовать конструктор

По умолчанию Spring использует пустой конструктор параметров класса для создания bean-компонентов:

// 假如类没有空参构造方法,将无法完成bean的创建
public class StudentDaoImpl implements StudentDao{
    public StudentDaoImpl(int a){}    
    @Override
    public Student findById(int id) {
        // 模拟根据id查询学生
        return new Student(1,"程序员","北京");
   }
}

Использование метода фабричного класса

Spring может вызывать метод фабричного класса для создания bean-компонентов:

1. Создайте фабричный класс, который предоставляет методы для создания объектов:

public class StudentDaoFactory {
    public StudentDao getStudentDao(){
        return new StudentDaoImpl(1);
   }
}

2 Настройте метод создания bean-компонентов в файле конфигурации как фабричный метод.

<!-- id:工厂对象的id,class:工厂类 -->
<bean id="studentDaoFactory" class="com.tong.dao.StudentDaoFactory"></bean>
<!-- id:bean对象的id,factory-bean:工厂对象 的id,factory-method:工厂方法 -->
<bean id="studentDao" factorybean="studentDaoFactory" factory-method="getStudentDao"></bean>

3 теста

Используйте статический метод фабричного класса

Spring может вызвать статический метод фабричного класса для создания bean-компонентов:

1 Создайте фабричный класс, который предоставляет статические методы для создания объектов.

public class StudentDaoFactory2 {
    public static StudentDao getStudentDao2() {
        return new StudentDaoImpl();
   }
}

2 Настройте метод создания bean-компонентов в файле конфигурации как заводской статический метод.

<!-- id:bean的id class:工厂全类名 factory-method:工厂静态方法   -->
<bean id="studentDao" class="com.tong.dao.StudentDaoFactory2" factory-method="getStudentDao2"></bean>

3 теста

Стратегия создания объекта IOC_object

 Spring задает стратегию создания объектов через атрибут scope в конфигурации, существует пять стратегий создания

1. singleton: синглтон, стратегия по умолчанию. Весь проект будет создавать только один объект, а время создания одноэлементного объекта можно установить с помощью атрибута lazy-init в:

 lazy-init="false" (по умолчанию): Создать немедленно, все объекты Bean в файле конфигурации будут созданы при запуске контейнера. lazy-init="true": отложенное создание, объект Bean будет создан только при первом использовании объекта Bean.

 Настройте одноэлементную стратегию:

<!--   <bean id="studentDao" class="com.tong.dao.StudentDaoImpl2" scope="singleton" lazy-init="true"> </bean>-->
<bean id="studentDao" class="com.tong.dao.StudentDaoImpl2" scope="singleton" lazy-init="false">
</bean>

Протестируйте одноэлементную стратегию:

// 为Bean对象的类添加构造方法
public StudentDaoImpl2(){
    System.out.println("创建 StudentDao!!!");
}
@Test
public void t2(){
    // 创建Spring容器
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
    // 从容器获取对象
    StudentDao studentDao1 = (StudentDao)ac.getBean("studentDao");
    StudentDao studentDao2 = (StudentDao)ac.getBean("studentDao");
    StudentDao studentDao3 = (StudentDao)ac.getBean("studentDao");
    System.out.println(studentDao1.hashCode());
    System.out.println(studentDao2.hashCode());
    System.out.println(studentDao3.hashCode());
}

2. прототип: несколько экземпляров, объекты будут создаваться каждый раз, когда они будут получены из контейнера.

<!-- 配置多例策略 -->
<bean id="studentDao"  class="com.tong.dao.StudentDaoImpl2" scope="prototype"></bean>

3. запрос: каждый запрос создает объект, который действителен только в веб-среде.

4. сеанс: для каждого сеанса создается объект, который действителен только в веб-среде.

5. глобальный сеанс: сеанс в кластерной среде создает объект, который действителен только в веб-среде.

 Время уничтожения объектов IOC_

 Стратегия создания объекта разная, и сроки уничтожения тоже разные:

singleton: объект уничтожается при уничтожении контейнера.

прототип: уничтожить объект, используя механизм сборки мусора JAVA.

request: Когда запрос на обработку завершится, экземпляр bean-компонента будет уничтожен.

session: когда сеанс HTTP окончательно отбрасывается, bean-компонент также будет уничтожен.

gloabal-session: при уничтожении сеанса в среде кластера экземпляр компонента также будет уничтожен.

 методы IOC_lifecycle

 Жизненный цикл объекта Bean включает создание-использование-уничтожение, и Spring может настроить метод, который объект Bean выполняет автоматически при его создании и уничтожении:

1 Определение методов жизненного цикла

public class StudentDaoImpl2 implements StudentDao{
     // 创建时自动执行的方法
    public void init(){
        System.out.println("创建StudentDao!!!");
   }
    // 销毁时自动执行的方法
    public void destory(){
        System.out.println("销毁StudentDao!!!");
   }
}

2 Методы жизненного цикла конфигурации

<!-- init-method:创建对象时执行的方法  destroy-method:销毁对象时执行的方法 -->
<bean id="studentDao" class="com.tong.dao.StudentDaoImpl2" scope="singleton"
      init-method="init" destroy-method="destory"></bean>

3 теста

@Test
public void t3(){
    // 创建Spring容器
    ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml");
    // 销毁Spring容器,ClassPathXmlApplicationContext才有销毁容器的方法
    ac.close();
}

IOC_ способ получить объект Bean

 Spring имеет несколько способов получить объекты в контейнере:

Получить по идентификатору/имени

Файл конфигурации

<bean name="studentDao" class="com.tong.dao.StudentDaoImpl2"></bean>
<bean id="studentDao" class="com.tong.dao.StudentDaoImpl2"></bean>

получить объект

StudentDao studentDao = (StudentDao) ac.getBean("studentDao");

Получить по типу

Файл конфигурации

<bean name="studentDao" class="com.tong.dao.StudentDaoImpl2"></bean>

получить объект

StudentDao studentDao2 = ac.getBean(StudentDao.class);

Получить по типу + id/имя

Хотя нет необходимости принудительно получать тип, если в контейнере есть несколько объектов класса реализации интерфейса, при получении будет сообщено об ошибке, и вам нужно использовать type + id/name для получения

Файл конфигурации

<bean name="studentDao" class="com.tong.dao.StudentDaoImpl2"></bean>
<bean name="studentDao1" class="com.tong.dao.StudentDaoImpl"></bean>

получить объект

StudentDao studentDao2 = ac.getBean("studentDao",StudentDao.class);

DI_Что такое внедрение зависимостей

 Dependency Injection (сокращенно DI), которая является конкретной реализацией идеи Spring об инверсии управления. Инверсия управления передает создание объектов Spring, но объекты могут зависеть от других объектов. Например, в классе сервиса должны быть атрибуты класса дао, и мы называем сервис зависимым от дао. Раньше нужно было вручную вводить значения атрибутов, код такой:

public interface StudentDao {
    Student findById(int id);
}
public class StudentDaoImpl implements StudentDao{
    @Override
    public Student findById(int id) {
        // 模拟根据id查询学生
        return new Student(1,"程序员","北京");
   }
}
public class StudentService {
  // service依赖dao,手动注入属性值,即手动维护依赖关系
    private StudentDao studentDao = new StudentDaoImpl();
    public Student findStudentById(int id){
        return studentDao.findById(id);
   }
}

В настоящее время, когда StudentService хочет использовать другой класс реализации StudentDao, такой как StudentDaoImpl2, необходимо изменить исходный код Java, что снижает удобство сопровождения кода. После использования среды Spring Spring управляет объектами службы и объектами Dao.В это время он может вводить зависимые значения атрибутов Dao в объекты службы. Это инъекция зависимостей Spring. Проще говоря, инверсия управления заключается в создании объектов, а внедрение зависимостей — в присвоении значений свойствам объектов.

Внедрение DI_зависимости

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

// setter方法设置属性
StudentService studentService = new StudentService();
StudentDao studentDao = new StudentDao();
studentService.setStudentDao(studentDao);
// 构造方法设置属性
StudentDao studentDao = new StudentDao();
StudentService studentService = new StudentService(studentDao);

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

Инъекция сеттера

Метод установки внедренного класса записывает свойство

public class StudentService {
    private StudentDao studentDao;
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
   }
}

В файле конфигурации установите <property> в <bean>, который должен вводить значения свойств.

<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
<bean id="studentService" class="com.itbaizhan.service.StudentService">
    <!--依赖注入-->
    <!--name:对象的属性名 ref:容器中对象的id值-->
    <property name="studentDao" ref="studentDao"></property>
</bean>

Проверьте, успешна ли инъекция

@Test
public void t2(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    StudentService studentService = (StudentService) ac.getBean("studentService");
    System.out.println(studentService.findStudentById(1));
}

инъекция конструктора

Внедренный класс пишет конструктор с параметрами

public class StudentService {
    private StudentDao studentDao;
    public StudentService(StudentDao studentDao) {
        this.studentDao = studentDao;
   }
}

Установите <constructor-arg> в <bean>, который должен вводить значения атрибутов

<bean id="studentDao" class="com.tong.dao.StudentDaoImpl"></bean>
<bean id="studentService" class="com.tong.service.StudentService">
    <!-- 依赖注入 -->
    <!-- name:对象的属性名 ref:配置文件中注入对象的id值 -->
    <constructor-arg name="studentDao" ref="studentDao"></constructor-arg>
</bean>

Проверьте, успешна ли инъекция

@Test
public void t2(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    StudentService studentService =(StudentService) ac.getBean("studentService");
  System.out.println(studentService.findStudentById(1));
}

рекомендация

отblog.csdn.net/m0_58719994/article/details/131744395