Вы действительно знаете Spring (самые полные заметки Spring во всей сети)

задняя часть Spring
Вы действительно знаете Spring (самые полные заметки Spring во всей сети)

Это 3-й день моего участия в августовском испытании обновлений, подробности о событии:Испытание августовского обновления

Восемь, АОП-программирование

8.1. Статический прокси

8.1.1 Введение в проблему

Зачем вам нужен шаблон проектирования прокси

В многоуровневой разработке JavaEEСервисный уровень (бизнес-уровень) является для нас наиболее важным.

Какой код входит в уровень службы

В Сервисе есть два типа кода:

  1. Основные функции: бизнес-операции, операции Дао.
  2. Дополнительные функции (небольшой объем кода и не часть основной функции, необязательные): транзакции, логирование, мониторинг производительности.

8.1.2 Обзор шаблонов проектирования прокси

Целевой класс (исходный класс): Подобно реальным арендодателям, относится к бизнес-классу, который содержит основные функции.

Целевой метод (исходный метод): метод в целевом классе (исходный класс).

Через прокси-класс к исходному классу (целевому классу) добавляются дополнительные функции, что полезно для обслуживания исходного класса (целевого класса).

8.1.3 Реализация статического прокси

Если статический прокси имеет исходный класс, он должен иметь написанный вручную класс прокси (исходный код), и каждый класс пишется вручную программистом.

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
  private String username;
  private String password;
}
public interface UserService {
   void register(User user);

   void login(String username,String password);
}
public class UserServiceImpl implements UserService {

  @Override
  public void register(User user) {
    System.out.println("注册了");
  }

  @Override
  public void login(String username, String password) {
    System.out.println("登录了");
  }
}
public class UserServiceProxy implements UserService {

  // 原始类对象
  UserServiceImpl userService = new UserServiceImpl();
  @Override
  public void register(User user) {
    System.out.println("增加了日志的额外功能");
    userService.register(user);
  }

  @Override
  public void login(String username, String password) {
    System.out.println("增加了登录日志的额外功能");
    userService.login(username,password);
  }
}

8.1.4 Проблемы со статическим прокси

  1. Исходный класс имеет прокси-класс, написанный вручную, что приведет к слишком большому количеству статических файлов классов, что не способствует управлению проектом.
  2. Дополнительные функции плохо поддерживаются и сложны для модификации.

8.2, динамический прокси Spring

Поскольку статический прокси такой сложный и хлопотный, Spring помог нам получить динамический прокси, который упрощает нашу разработку.

8.2.1 Реализация динамического прокси

  1. Создайте исходный объект (целевой объект)
public class UserServiceImpl implements UserService {

  @Override
  public void register(User user) {
    System.out.println("注册了");
  }

  @Override
  public void login(String username, String password) {
    System.out.println("登录了");
  }
}

 <bean id="UserServiceImpl" class="com.proxy.dynamicProxy.UserServiceImpl"/>
  1. писать дополнения

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

Нам нужно написать дополнительный класс метода и реализоватьMethodBeforeAdviceинтерфейс, который добавляет дополнительную функциональность в указанные им методы

public class Before implements MethodBeforeAdvice {

  // 额外功能书写在接口的实现中,运行在原始方法执行之前运行额外功能
  @Override
  public void before(Method method, Object[] objects, Object o) throws Throwable {
    System.out.println("---method before advice log---");
  }
}

  1. Перейдите в файл конфигурации для настройки
<bean id="before" class="com.proxy.dynamicProxy.Before"/>
  1. определить точку

Точка входа: место, где добавляются дополнительные функции, и программист решает, к какому исходному методу добавить дополнительные функции в соответствии со своими потребностями.

На этапе тестирования все методы используются в качестве точек входа для добавления дополнительных функций.

  <aop:config>
<!--    切入点,id属性是唯一标识符,expression属性是切入点表达式,这个切入点表达式的意思是所有方法都作为切入点都加入额外功能-->
    <aop:pointcut id="testaop" expression="execution(* *(..))"/>
  </aop:config>
  1. собрать

Целью сборки является интеграция точечных вырезов и дополнительных функций.

<!--    组装:将切入点和额外功能进行整合-->
    <aop:advisor advice-ref="before" pointcut-ref="testaop"/>
  1. контрольная работа

Использование оригинального объектаidзначение, чтобы получить прокси-объект, созданный фабрикой Spring. После получения прокси-объекта можно объявить тип интерфейса для хранилища объектов.

8.2.2. Метод перед советом

Если мы хотим реализовать динамический прокси, необходимо реализовать дополнительные функцииMethodBeforeAdviceинтерфейсbeforeметод,beforeОбъяснение параметра метода

имя параметра имея в виду
Method method Оригинальный метод, к которому добавляется дополнительная функция, кто добавляет дополнительную функцию, тот и тот, этот параметр меняется, в зависимости от того, к кому добавляется дополнительный метод
Object[] objects Параметр исходного метода, к которому добавлена ​​дополнительная функциональность. Добавьте дополнительные функции в метод login(String uername, Stirng password), тогда этот массив Object является списком параметров для метода login, тесно связанного с предыдущим параметром.
Object o Исходный объект, к которому была добавлена ​​дополнительная функциональность.

8.2.3, вопросы, требующие внимания

  1. Где динамические прокси-классы, созданные Spring?

    Во время выполнения среда Spring проходитТехнология динамического байт-кода, созданный в JVM, и исчезнет вместе с JVM после ожидания завершения программы.

  2. Что такое технология динамического байт-кода?

    Через стороннюю структуру динамического байт-кода JVM генерируется напрямую для генерации байт-кода, а затем создается объект.Когда JVM завершается, динамический байт-код исчезает.

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

  4. Без изменения функции при создании прокси-объекта другого целевого класса (исходного класса) нужно указать только исходный (целевой) объект.

8.2.4, Интерфейс MethodInterceptor

    MethodBeforeAdviceФункция метода интерфейса относительно проста, и перед выполнением исходного метода могут быть добавлены дополнительные функции. Spring также предоставляет другой интерфейс —MethodInterceptorИнтерфейс, он может не только добавлять дополнительную функциональность перед выполнением исходного метода, но и добавлять дополнительную функциональность после выполнения исходного метода, даже до и после выполнения.

public class Arround implements MethodInterceptor {

  /*
  书写额外功能的方法
  参数:MethodInvocation表示的是额外功能所增加给的那个原始方法。
  运行原始方法:methodInvocation.proceed(),在原始方法前面写的代码就运行在原始方法之前,反之。
  返回值:代表原始方法返回值
   */
  @Override
  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    System.out.println("前置增强");
    Object proceed = methodInvocation.proceed();
    System.out.println("后置增强");
    return proceed;
  }
}

8.2.5 Точка входа

Pointcut определяет, где будет добавлена ​​дополнительная функциональность. Он разделен на две части:

  1. execution(): функция точечного вырезания
  2. * *(..): выражение pointcut

8.2.5.1, выражение pointcut метода

    * *(..):Первый*Модификатор для соответствующего метода (*означает любой), второй*Имя метода соответствующего метода,(..)Соответствует любому списку параметров метода, поэтому это выражение pointcut представляет все методы.

Определите метод входа в систему как точку входа

* login(..)

Определите метод входа в систему, и метод имеет два параметра строкового типа в качестве точек входа.

* login(String,String)

У этого метода есть очень фатальный недостаток: метод врезки недостаточно точен. Нам нужно использовать точную квалификацию точки входа метода. Поэтому, когда мы указываем метод, если нам нужно быть более точным, нам нужно указать имя пакета + имя класса.

Spring-方法切入点

8.2.5.2, выражение класса pointcut

Укажите конкретный класс в качестве точки входа (где добавляются дополнительные функции), и все методы этого класса добавят соответствующие дополнительные функции.

* com.domain.UserService.*(..)

8.2.5.3, пакетное выражение pointcut

Укажите, куда добавляется пакет в качестве дополнительной функциональности, и все классы и их методы в пакете естественным образом добавят дополнительную функциональность. Он больше используется в реальном бою.

Spring-包切入点

# 切入点包中的所有类,必须在proxy中,不能在proxy包的子包中
* com.domain.proxy.*.*(..)

# 如果想要当前包及其当前包的子包都进行功能增强的话,必须要这样写
* com.domain.proxy..*.*(..)

8.2.5.4, функция pointcut

Функции Pointcut используются для выполнения выражений pointcut,executionЭто самая важная функция pointcut и имеет наиболее полные функции.Она может выполнять выражения pointcut методов, выражения pointcut классов и выражения pointcut пакетов.

Его недостатком является то, что его более хлопотно писать при выполнении выражения pointcut. Итак, Spring предоставляет другие функции pointcut для упрощенияexecutionСложность письма.

8.2.5.4.1, аргументы

Его основная функция — соответствие параметрам функций (методов).

# 方法参数必须是两个字符串类型的参数
args(String,String)
8.2.5.4.2, в пределах

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

# 切入点想选为某个类(UserServiceImpl这个类作为切入点)
whithin(*..UserServiceImpl)

# 切入点想选为某个包
within(com.poroxy..*)
8.2.5.4.3, @аннотация

Добавьте дополнительные функции к методам со специальными аннотациями, формат синтаксиса:@annotation(注解所在的包的全限定名)

// 先写一个自定义注解
@Target(ElementType.METHOD) // 表示可以加在哪里
@Retention(RetentionPolicy.RUNTIME) // 表示什么时候起作用
public @interface Log {
}
    <aop:pointcut id="testaop" expression="@annotation(com.anno.Log)"/>
  @Log
  @Override
  public void register(User user) {
    System.out.println("注册了");
  }

8.2.5.5 Логическая работа функции pointcut

Логическая работа функции pointcut заключается в объединении нескольких функций pointcut для совместной работы для выполнения более сложных требований.

8.2.5.5.1, и эксплуатация (и)
# 案例一:满足方法名为login且参数为两个字符串
execution (* login(..) and args(String,String))

    Примечание. Операции И нельзя использовать с функциями pointcut того же типа.

# 案例二:满足方法名为login和register作为切入点
# 这是错误的,不可能一个方法同时叫login和register
execution (* login(..)) and execution(* register(..))
8.2.5.5.2, или операция (или)
# 案例一:满足方法名为login或register作为切入点
execution(* login(..)) or execution(* register(..))

8.2.6 Резюме

Spring-总结

8.3, обзор АОП

АОП — это аббревиатура Aspect Oriented Programming, что означает аспектно-ориентированное программирование.Это технология, которая реализует унифицированное обслуживание программных функций с помощью прекомпиляции и динамических агентов во время выполнения. Аспекты = точечные разрезы + дополнительные функции.

АОП является продолжением ООП, горячей точки в разработке программного обеспечения и важной частью среды Spring, производной парадигмы функционального программирования. Использование АОП может изолировать каждую часть бизнес-логики, тем самым уменьшая связь между каждой частью бизнес-логики, улучшая возможность повторного использования программы и повышая эффективность разработки.

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

8.4. Объяснение терминов

Нижний уровень реализации Spring AOP заключается в инкапсуляции кода динамического прокси-сервера выше.После инкапсуляции нам нужно только закодировать те части, которые требуют внимания, и завершить усовершенствование метода указанной цели посредством конфигурации.

  • Цель: целевой объект прокси.
  • Прокси: когда класс расширяется с помощью АОП, он создает результирующий прокси-класс.
  • Точка соединения (connection point): Так называемая точка соединения относится к тем точкам, которые перехватываются. В Spring эти точки относятся к методам, потому что Spring поддерживает только точки соединения типа метода.
  • Pointcut (точка входа): так называемая точка входа относится к тому, что мы хотим сделать.
  • Точка присоединения: определение перехвата.
  • Совет (уведомление/улучшение): Так называемый совет относится к уведомлению после перехвата точки присоединения.
  • Аспект (аспект): это комбинация pointcut и уведомления (введения), короче говоря, pointcut + метод улучшения.
  • Плетение: относится к процессу применения улучшений к целевым объектам для создания новых прокси-объектов. Spring использует динамическое переплетение прокси, в то время как AspectJ использует переплетение во время компиляции и во время загрузки классов.

8.5 Базовая реализация АОП (динамический прокси)

8.5.1 Динамический прокси JDK

Spring-JDK动态代理

public class TestJDKProxy {

  public static void main(String[] args) {
    // 1. 创建原始对象
    UserService userService = new UserServiceImpl();

    // 2. 创建JDK动态代理
    InvocationHandler handler = new InvocationHandler() {
       
      @Override
                     // 参数一:表示代理对象   参数二:额外功能所增加给的原始方法  参数三: 表示原始方法的参数
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       
        // 原始对象的方法方法运行
        Object ret = method.invoke(userService,args);
        System.out.println("========after proxy log========");
        return ret;
      }
    };
    UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),handler);
    userServiceProxy.login("admin","123456");
    userServiceProxy.register(null);
  }
}

8.5.2 Динамический прокси CGlib

Динамический прокси JDK должен гарантировать, что метод прокси-класса согласуется с исходным классом путем реализации интерфейса.Однако, если нет интерфейса, необходимо использовать динамический прокси CGlib.Самая большая разница между ним и JDK динамический прокси заключается в том, что динамический прокси CGlib реализует тот же метод, что и исходный класс, посредством наследования родитель-потомок.

Принцип CGlib для создания динамических прокси: отношение наследования родитель-потомок создает прокси-объекты, исходный класс используется в качестве родительского класса, а прокси-класс используется в качестве подкласса.

Spring-JDK动态代理、CGlib动态代理

package cn.linstudy.cglibProxy.service;

import cn.linstudy.cglibProxy.domain.User;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/25 18:44
 */
public interface UserServiceImpl {

  void login(String username,String password);

  void register(User user);
}

package cn.linstudy.cglibProxy.proxy;

import cn.linstudy.cglibProxy.domain.User;
import cn.linstudy.jdkProxy.service.impl.UserServiceImpl;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/25 18:45
 */
public class UserServiceProxy extends UserServiceImpl {

  @Override
  public void login(String username, String password) {
    System.out.println("登录了"+username+password);
  }

  @Override
  public void register(User user) {
    System.out.println("注册了"+user);
  }
}
public class TestCGlib {

  public static void main(String[] args) {
    // 创建原始对象
    UserService userService = new UserService();

    // 通过CGlib方式创建代理对象
    Enhancer enhancer = new Enhancer();
    enhancer.setClassLoader(userService.getClass().getClassLoader()); // 设置类加载器
    enhancer.setSuperclass(userService.getClass()); // 设置父类
    MethodInterceptor interceptor = new MethodInterceptor() {
      // 等同于InvocationHandler 的 invoke 方法
      @Override
      public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
          throws Throwable {
        System.out.println("=====cglib log=====");
        Object ret = method.invoke(userService, args);
        return  ret;
      }
    };
    enhancer.setCallback(interceptor);
    UserService serviceProxy = (UserService) enhancer.create();
    serviceProxy.login();
    serviceProxy.register();
  }
}

8.5.3. Резюме

  1. Динамический прокси JDK: Proxy.newProxyInstance(), который создает класс реализации прокси через интерфейс.
  2. Динамический прокси CGlib: Enhancer, который создает прокси-классы, наследуя родительские классы.

8.6 Реализация АОП с аннотациями

public interface UserService {
   void register(User user);
   void login(String username, String password);
}

public class UserServiceImpl implements UserService {
  
  @Override
  public void register(User user) {
    System.out.println("注册了");
  }

  @Override
  public void login(String username, String password) {
    System.out.println("登录了");
  }
}
@Aspect
public class MyAspect {

  @Around("execution(* * (..))") // 写切入点表达式
    // joinPoint 表示原始方法
  public Object Around(ProceedingJoinPoint joinPoint) throws Throwable { 
    System.out.println("====aspect前置增强====");
    Object ret = joinPoint.proceed(); // 代表原始方法执行
    System.out.println("====aspect后置增强====");
    return ret;
  }
}

8.6.1, @точечный разрез

Если мы хотим настроить одно и то же выражение pointcut для нескольких методов, будет избыточность.В настоящее время все, о чем мы можем думать, это извлечь общее выражение pointcut, тогда нам нужно использовать аннотацию:@Pointcut,Обратите внимание, что метод, в котором находится аннотация, должен быть public void и не иметь тела метода.

Повторное использование Pointcut заключается в определении функции в классе аспектов с использованием@PointcutТаким образом, аннотация определяет выражение pointcut, что более удобно для повторного использования pointcut.

@Aspect
public class MyAspect {

  // 将公共的切入点表达式提取出来,注意的是,注解所在的方法必须是public void修饰,且没有方法体
  @Pointcut("execution(* * (..))")  
@Around(value = "MyPoint()") // 引入切入点表达式
  public Object Around(ProceedingJoinPoint joinPoint) throws Throwable { // joinPoint 表示原始方法
    System.out.println("====aspect前置增强====");
    Object ret = joinPoint.proceed(); // 代表原始方法执行
    System.out.println("====aspect后置增强====");
    return ret;
  }


  @Around(value = "MyPoint()") // 引入切入点表达式
  public Object Around1(ProceedingJoinPoint joinPoint) throws Throwable { // joinPoint 表示原始方法
    System.out.println("====aspect tx====");
    Object ret = joinPoint.proceed(); // 代表原始方法执行
    System.out.println("====aspect tx====");
    return ret;
  }
}

8.6.2 Как создать динамический прокси

Существуют две основные реализации АОП:

  1. Динамический прокси JDK: через реализацию интерфейса создайте прокси-объект, создав новый метод реализации.
  2. Динамический прокси CGlib: создайте прокси-объект, унаследовав родительский класс и создав новый подкласс.

По умолчанию мы можем узнать с помощью dubug, что метод динамического прокси JDK используется по умолчанию для АОП-программирования.

image-20210209112410685

В некоторых случаях мы хотим изменить метод динамического прокси JDK по умолчанию на метод динамического прокси CGlib, так как же его реализовать?

В конфигурационном файле мы прописали конфигурацию перед<aop:aspectj-autoproxy />, функция этой конфигурации — сообщить Spring, что мы собираемся начать АОП-программирование на основе аннотаций, в этой конфигурации есть атрибутproxy-target-class="false", его значение по умолчанию равноfalse, указывающее, что динамический прокси-сервер JDK используется по умолчанию, если для китайского языка установлено значениеtrue, то это означает использование динамического прокси CGlib.

    Этот тег применяется только к АОП-разработке на основе аннотаций. Если он основан на традиционной АОП-разработке, а не на аннотациях, то он должен быть в<aop-config>отметить, добавитьproxy-target-class="false"Свойства можно настроить. По сравнению с методом аннотации атрибуты те же, но расположение другое.

image-20210209112817522

8.7. Резюме

Spring-Spring总结

9. Программирование аннотаций

9.1, Обзор программирования аннотаций

Программирование аннотаций относится к добавлению определенных аннотаций к классам или методам для завершения разработки определенных функций. Преимущества использования аннотаций:

  1. Разработка аннотированного программирования более удобна, код лаконичен, а скорость разработки значительно улучшена.
  2. Аннотированное программирование — это новая тенденция в разработке Spring.

9.2, роль аннотаций

  1. Упростите сложную конфигурацию в XML-файле.

  2. Замените интерфейс, чтобы реализовать контракт между вызывающими сторонами. С помощью аннотаций достигается соглашение между вызывающим функцию и поставщиком функции, а затем вызывается функция. Форма аннотации является основной.

9.3 Аннотации, связанные с созданием объекта

Аннотации на этом этапе существуют только для упрощения конфигурации XML и не могут полностью заменить XML.

9.3.1, @компонент

9.3.1.1, пример кода

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

<context:component-scan base-package:"com.lin" />

Spring注解编程

package cn.lin;

import org.springframework.stereotype.Component;

@Component
@Data
public class User {
  private String id;
  private String username;
  private String password;
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="cn.lin"/>
</beans>
package cn.lin;

import static org.junit.Assert.*;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/24 21:58
 */
public class UserTest {
  /**
  * 用于测试注解@Component
  */
  @Test
  public void testComponent(){
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = app.getBean("user", User.class);
    System.out.println(user);
  }
}

9.3.1.2 Внимание

  1. использовать@Componentзаметьте, онЗначение id созданного объекта по умолчанию соответствует первой букве имени класса в нижнем регистре.
  2. мы можем пройти@Component("自定义id值")способ присвоить значение идентификатора созданному объекту.
  3. Файлы конфигурации Spring — это то, что может переопределить конфигурацию аннотаций.Значение id и значение class должны быть согласованы.Если они несовместимы, Spring будет думать, что это новый объект, и не перезапишет его.

9.3.1.3, производные аннотации @Component

@Component имеет три производные аннотации:

  1. @Repository
  2. @Service
  3. @Controller

Эти аннотации по сути являются аннотациями @Component.Использование (альтернативные теги) и детали ничем не отличаются от @Component, и они по сути одинаковы. Мы можем доказать этот вывод, взглянув на исходный код @Repository (то же самое можно доказать и для других).

image-20210225210144605

Spring предоставляет эти аннотации для более точного выражения роли типа.

  1. @Repository: в основном используется для интерфейса Dao.
  2. @Service: в основном используется для классов реализации службы.
  3. @Controller: в основном используется для контроллеров.

9.3.2, @сфера

9.3.2.1 Обзор

@ScopeАннотации используются для управления количеством создаваемых простых объектов. если мы не добавим@ScopeЕсли он аннотирован, он имеет значение по умолчаниюsingleton.

9.3.2.2, пример кода

// 单例模式,只会创建一次
@Scope("singleton")
public class User(){
    
}

9.3.3, @ленивый

9.3.3.1 Обзор

    @LazyЦель аннотации — отсрочить создание одноэкземплярных объектов.

Если не настроено, по умолчанию объекты создаются синхронно при создании контейнера.

Если добавлена ​​аннотация, объект не будет создаваться синхронно при создании контейнера, а объект будет создаваться тогда, когда вам нужно его использовать.

9.4 Аннотации, относящиеся к методу цикла объявления

9.4.1, конструкция @post

    @PostConstructАннотация используется для метода, чтобы указать, что метод является методом инициализации. При введении этой аннотации нам нужно сначала добавить набор зависимостей.

<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>

@Component
@Data
public class User {

  // 表示这个是一个初始化方法
  @PostConstruct
  public void MyInit(){
    System.out.printf("User Init");
  }

9.4.2, @до уничтожения

    @PreDestroyАннотация используется для метода, чтобы указать, что метод является методом уничтожения. Метод destroy автоматически вызывается при закрытии фабрики.

  @PreDestroy
  public void MyDestory(){
    System.out.printf("User Destory");
  }

9.4.3 Внимание

Эта аннотация не предоставляется Spring, но предоставляется JSR (спецификация JavaEE) 520. Итак, вам нужно импортировать зависимости.

9.5, добавить связанные аннотации

9.5.1 Пользовательский тип: @Autowired

9.5.1.1 Обзор

    @AutowiredЭто аннотация, которая может аннотировать переменные-члены, методы и конструкторы для завершения работы автоматической сборки.

    @AutowiredАннотацию можно поместить в переменную-член, в метод set переменной-члена или в любой метод, чтобы указать, что текущий метод выполняется автоматически.Если метод имеет параметры, он автоматически найдет параметры того же типа в Контейнер IOC в качестве его значения передачи.

9.5.1.2, пример кода

package cn.lin.dao;

import cn.lin.User;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/26 9:17
 */

public interface UserMapper {
  public void login(User user);
}

package cn.lin.dao;

import cn.lin.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/26 9:18
 */

@Repository
public class UserMapperImpl implements UserMapper{

  @Override
  public void login(User user) {
    System.out.printf("登录了");
  }
}

package cn.lin.service;

import cn.lin.User;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/26 9:19
 */

public interface UserService {
  void login(User user);
}

package cn.lin.service.impl;

/**
 * @Description
 * @Author XiaoLin
 * @Date 2021/2/26 9:19
 */
@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;


  @Override
  public void login(User user) {
    userMapper.login(new User());
  }
}
 /**
  * 用于测试
  */
  @Test
  public void test(){
    ApplicationContext app = new ClassPathXmlApplicationContext("/applicationContext.xml");
    UserService userService = (UserService)app.getBean("userServiceImpl");
    userService.login(new User());
  }

9.5.1.3 Внимание

  1. @AutowiredПо умолчанию аннотация вводится на основе типа.Тип внедряемого объекта должен совпадать с типом целевой переменной-члена или ее подкласса или класса реализации.

  2. Если вы хотите ввести на основе имени, вам нужно@AutowiredВ сочетании с другой аннотацией:@Qualifier("需要注入的类的id值"). Внедрение на основе имени требует, чтобы значение ud внедряемого объекта совпадало с@QualifierИмена, заданные в аннотациях, совпадают.

  3. @AutowiredГде можно размещать аннотации:

    • Может быть помещен в метод set соответствующей переменной-члена. Переменные Spring будут вызывать метод set для инъекции.
    • Поместите его непосредственно над переменной-членом, которую необходимо внедрить. Spring будет напрямую вводить переменные-члены через отражение.(рекомендовать)

10. Spring интегрирует Mybatis

10.1 Обзор интеграции

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

10.2. Проблемы, существующие в традиционном кодировании Mybatis

Обзор этапов разработки MyBatis:

  1. Напишите классы сущностей.
  2. Настройте псевдонимы объектов.
  3. Создайте таблицу.
  4. Создайте интерфейс DAO.
  5. Реализуйте файл Mapper.
  6. Зарегистрируйте файл Mapper.
  7. Вызов API MyBatis.

Когда мы разрабатываем MyBatis, мы можем обнаружить, что традиционная разработка MyBatis имеет фатальный недостаток: громоздкая конфигурация и избыточный код. Поэтому нам нужен Spring, суперфабрика, для интеграции.

10.3 Объединение идей

Spring整合

10.4, реализация кода

Важным изменением в MyBatis, интегрирующем Spring, является изменение файла конфигурации и кода тестового класса.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--  连接池-->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///javaweb?characterEncoding=utf-8&amp;useSSL=false"/>
    <property name="username" value="root"/>
    <property name="password" value="1101121833"/>
  </bean>

<!--  创建SqlSessionFactory、创建SqlSessionFactoryBean-->
  <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--    数据源-->
    <property name="dataSource" ref="dataSource"/>
<!--    别名-->
    <property name="typeAliasesPackage" value="com.lin.domain"/>
<!--    mapper配置文件路径-->
    <property name="mapperLocations">
      <list>
        <value>classpath:com/lin/mapper/*Mapper.xml</value>
      </list>
    </property>
  </bean>

<!--  Dao对象的创建-->
  <bean id="scanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--    sqlSessionFactoryBean对象-->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
<!--    Dao接口的位置-->
    <property name="basePackage" value="com.lin.mapper"/>
  </bean>
</beans>
 /**
  * 用于测试Spring与Mybatis整合
  */
  @Test
  public void testSpringMybatis(){
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext(
        "/applicationContext.xml");
    UserMapper userMapper = (UserMapper) app.getBean("userMapper");
    List<User> users = userMapper.queryAll();
    System.out.println(users);

  }

10.5 Внимание

  1. Когда Spring интегрирован с Mybatis, почему бы не отправлять вещи, но мы все равно можем вставлять данные, когда мы вставляем данные?
分析:本质上谁控制了链接对象,谁就可以提交事物。连接对象是由连接池来获取的:
1. MyBatis提供的连接池对象:他将Connection.setAutoCommit的值从默认值true设置成为了false,所以导致无法自动提交,手动控制事物,需要在操作完成后,手工提交事务。
2. Druid(C3P0、DBCP)提供的连接池对象:Connection.setAutoCommit的值为默认值true,保持自动控制事物,自动提交。
  1. В реальной разработке в будущем, как правило, несколько SQL-запросов завершаются успешно или неудачно одновременно, и нам все еще нужно отправлять их вручную. Но в будущем мы передадим вещи Spring, чтобы решать их через управление транзакциями.

10.6, Обработка транзакций Spring

10.6.1. Обзор вещей

Механизм базы данных для обеспечения целостности бизнес-операций. Имеет четыре характеристики: А (атомарность), С (постоянство), I (обособленность), D (постоянство).

Управление транзакциями двух распространенных технологий сохраняемости:

  1. JDBC (полагаться на объект Connection для управления транзакциями)

    Connection.setAutoCommit(false);

    Connection.commit();

    Connection.rollback();

  2. MyBatis (автоматическая фиксация транзакций)

    sqlSession.commit();

    sqlSession.rollback();

  3. Вывод: Нижний уровень управления вещами контролируется объектом Connection (нижний уровень sqlSession также является Connection).

10.6.2 Реализация кодирования

<!--  配置原始对象-->
  <bean id="userService" class="com.lin.service.impl.UserServiceImpl">
    <property name="userMapper" ref="userMapper"/>
  </bean>

<!--  额外功能,Spring帮我们封装好了一个对象DataSourceTransactionManager,简化我们的开发-->
  <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--    先注入数据源,获取链接对象,进而才可以控制事物-->
    <property name="dataSource" ref="dataSource" />
  </bean>

<!--  组装切面-->
  <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
@Transactional // 切入点,给谁加入事物
public class UserServiceImpl implements UserService {

  private UserMapper userMapper;

  public UserMapper getUserMapper() {
    return userMapper;
  }

  public void setUserMapper(UserMapper userMapper) {
    this.userMapper = userMapper;
  }
  @Override
  public List<User> queryAll() {
    return userMapper.queryAll();
  }
}

11. Дела

11.1 Атрибуты транзакций

Атрибуты — это ряд значений, описывающих характеристики объекта.Транзакции имеют пять атрибутов:

  1. Свойство изоляции
  2. свойства распространения
  3. свойство только для чтения
  4. свойство тайм-аута
  5. свойство исключения

мы можем использовать@Transactional()Аннотация добавляет атрибуты, а атрибуты добавляются к транзакции в круглых скобках.

11.2 Изоляционные свойства

Атрибут изоляции описывает характеристики транзакции для решения проблем параллелизма. Транзакционный одновременный доступ создает три проблемы:

  1. грязное чтение
  2. неповторяемое чтение,
  3. галлюцинации

Мы можем решить проблему параллелизма транзакций через атрибут изоляции, и установить разные значения в атрибуте изоляции.

11.2.1 Грязные чтения

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

Решение: дать@Transactional()Добавлена ​​аннотацияisolation=Isolation.READ_COMMITTED.

11.2.2, неповторяемое чтение

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

Решение: дать@Transactional()Добавлена ​​аннотацияisolation=Isolation.REPEATABLE.READ. Суть его в том, чтобы добавить к данным блокировку на уровне строки.

11.2.3 Фантомное чтение

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

Решение: дать@Transactional()Добавлена ​​аннотацияisolation=Isolation.SERIALIZABLE. Суть его в том, чтобы добавить табличную блокировку на всю таблицу.

11.2.4. Резюме

Параллельная сортировка: Isolation.SERIALIZABLE>Isolation.REPEATABLE.READ>Isolation.READ_COMMITTED

Сортировка по производительности: Isolation.SERIALIZABLE

# 我们可以用命令来查询MySQL的默认属性。
select @@tx_isolation
Значение свойства изоляции MySQL Oracle
ISOLATION_READ_COMMITTED
IOSLATION_REPEATABLE_READ
ISOLATION_SERIALIZABLE

11.3 Свойства распространения

Свойство распространения описывает характеристики транзакций для решения проблем вложенности.

Вложение транзакций: большая транзакция содержит несколько небольших транзакций.

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

11.3.1 Подробное объяснение атрибутов распространения транзакций

Слияние: ваши собственные дела ушли, и внешние дела возьмут верх.

Приостановить: эквивалентно паузе.

Распределите стоимость имущества Внешняя транзакция не существует Транзакция внешнего существования Применение сцены, которые будут использоваться
ТРЕБУЕТСЯ** (по умолчанию)** начать новую транзакцию Интегрироваться во внешние дела @Transactional(propagation = Propagation.REQUIRED) Добавить, удалить и изменить метод
SUPPORTS Не начинать транзакцию Интегрироваться во внешние дела @Transactional(propagation = Propagation.SUPPORTS) метод запроса
REQUIRES_NEW начать новую транзакцию Приостановите внешнюю транзакцию и создайте новую транзакцию. @Transactional(propagation = Propagation.REQUIRES_NEW) в методе регистрации
NOT_SUPPORTED Не начинать транзакцию приостановить внешнюю транзакцию @Transactional(propagation = Propagation.NOT_SUPPORTED) и не часто используется
NEVER Не начинать транзакцию Выбросить исключение @Transactional(propagation = Propagation.NEVER) и не часто используется
MANDATORY Выбросить исключение Интегрироваться во внешние дела @Transactional(propagation = Propagation.MANDATORY) и не часто используется

11.4, свойства только для чтения

Для бизнес-методов, выполняющих только операции запроса, можно добавить атрибуты только для чтения (readlyonly = true) для повышения эффективности работы. Его значение по умолчанию равно false.

11.5 Свойство тайм-аута

Когда текущая транзакция обращается к данным, возможно, данные, к которым осуществляется доступ, заблокированы другими транзакциями, тогда этой транзакции необходимо подождать. Свойство timeout указывает максимальное время ожидания транзакции в секундах. Его значение по умолчанию равно -1, а его окончательное время определяется соответствующей базой данных.

Демонстрация приложения:@Transaction(timeout=秒数);

11.6 Свойства исключений

Во время обработки транзакций Spring стратегия отката принимается по умолчанию для RuntimeException и его подклассов, а стратегия фиксации принимается для Exception и его подклассов.

// 异常属性有两个值
// 1. rollbackFor();表示回滚的异常类型,里面写异常的类型的字节码对象,他里面的值的类型是数组类型,所以可以写多个异常类型的字节码对象,用逗号隔开
rollbackFor({java.lang.Exception.class});

// noRollbackFor();表示不会滚的异常类型的字节码对象
noRollbackFor({java.lang.RuntimeException.class})

11.7 Сводка атрибутов транзакций и их общих конфигураций

  1. Свойство изоляции: обычно используйте значение по умолчанию.

  2. Атрибуты распространения: как правило, используйте Required (значение по умолчанию) для добавления и удаления и Supports для операций запроса.

  3. Атрибут только для чтения: как правило, значение readOnly имеет значение false для добавлений, удалений и изменений, а значение readOnly — значение true для параметров запроса.

  4. Свойство тайм-аута: обычно используйте значение по умолчанию -1.

  5. Свойство исключения: обычно используйте значение по умолчанию.   Суммировать:

  6. Операции добавления, удаления и модификации:@Transactional.

  7. Операция запроса:@Transactional(propagation=Propagation.SUPPORTS,readOnly=true)

12. Spring интегрирует MVC

12.1 Подготовка зависимости

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.13</version>
    </dependency>


    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.8.8</version>
    </dependency>

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.3</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.25</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.1</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

12.2 Зачем интегрировать инфраструктуру MVC

Поскольку есть много требований и функций, которые не могут быть решены с помощью собственной среды Spring, для упрощения нашей разработки необходима среда MVC. Наиболее часто используемые фреймворки MVC:SpringMVC, Struts2, Struts1, jsf, webwork (исключаются все, кроме первого).

  1. Платформа MVC предоставляет контроллер (Controller) для вызова уровня службы.
  2. Обработка ответов на запросы.
  3. Принять параметры запроса.
  4. Контролируйте запущенный процесс программы.
  5. Просмотр синтаксического анализа (JSP, JSON, Freemarker, Thyemeleaf)

12.3 Основная идея интеграции Spring с MVC framework

подготовительный завод

1. Web开发过程中如何创建工厂
	ApplicationContext ctx = new WebXmlApplication("/applicationContext.xml");

2. 如何保证工厂被共用同时保证唯一性
	被共用:在Web中有四个作用于:request、session、ServletContext(application),我们可以把工厂存储到ServletContext这个作用域中。
	唯一性:我们可以利用ServletContextListener这个监听器,因为在ServletContext对象创建的同时,ServletContextListener会被调用且只被调用一次。所以我们可以把工厂创建的代码写在ServletContextListener中,这样就保证了唯一性。
	
3. 总结
	我们首先在ServletContextListener这个监听器中书写创建工厂的代码,其次将这个工厂放在ServletContext对象中。
	
4. Spring的封装
	既然这些代码如此繁琐,那么Spring肯定会帮我们封装好了。

5. ContextLoaderListener类
	Spring封装了一个ContextLoaderListener类,他的作用是创建工厂和将工厂放进ServletContext对象中。

	
	

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

Поскольку Spring инкапсулировал для нас класс ContextLoaderListener, мы можем настроить его в web.xml напрямую в соответствии с слушателем.

	<!---我们只需要在web.xml中进行配置即可-->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
        <!--告诉Spring我们的配置文件在哪里-->
	<context-param>
		<param-name>contextConfigLocation</param-name>
        	<param-value>classpath:applicationContext.xml</param-value>
	</context-param>

интеграция кода

Ядром интеграции кода является внедрение зависимостей, которое внедряет объект службы, требуемый контроллером, в объект контроллера.