Автопроводка SpringBoot
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Почему после использования SpringBoot мы можем построить веб-сервер всего несколькими строками вышеприведенного кода, что ненамного проще, чем использование SpringMVC до этого.
Секрет этого в том, что@SpringBootApplication
Среди заметок:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
он унаследовал@EnableAutoConfiguration
аннотация:
/**
* ...
* Auto-configuration classes are regular Spring {@link Configuration} beans. They are
* located using the {@link SpringFactoriesLoader} ..
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...
}
Глядя на комментарии, мы видим, что класс автоконфигурации также существует в виде обычного Spring Bean. они естьSpringFactoriesLoader
позиция:
public final class SpringFactoriesLoader {
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
...
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
}
Класс будет загружен из пути к классам"META-INF/spring.factories"
Ознакомьтесь со списком классов с автоматическим подключением вspring-boot-autoconfigure.jar
Он содержит классы конфигурации со встроенными Tomcat, SpringMVC, транзакциями и другими функциями:
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
Политика распространения транзакций
предисловие
метод как измерение
Поскольку транзакции Spring основаны на АОП, транзакции существуют в коде Java с точки зрения методов. Распространение транзакций основано на взаимодействии между несколькими транзакциями, поэтому оно проявляется в коде как один метод транзакции, вызывающий другой метод транзакции (как в следующем кодеsavePersons
вызов методаsaveChildren
):
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
@Transactional
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
saveChildren();
}
@Transactional
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
public void saveChild1() {
Person person = new Person();
person.setUsername("child1");
person.setPassword("456");
personMapper.insertSelective(person);
}
public void saveChild2() {
Person person = new Person();
person.setUsername("child2");
person.setPassword("789");
personMapper.insertSelective(person);
}
}
боб это вход
Однако Spring AOP основан на расширении компонента, что означает, что когда вы вызываете метод транзакции компонента (метод, модифицированный аннотацией транзакции), аннотация транзакции может вступить в силу обычным образом. Но если вы вызовете метод транзакции в этом классе, это эквивалентно встраиванию кода метода в текущий метод, то есть аннотация транзакции метода будет проигнорирована.
Например:
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
saveChildren();
}
@Transactional
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
}
Приведенный выше код эквивалентен следующему коду (saveChildren
Аннотации транзакции метода игнорируются):
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
saveChild1();
saveChild2();
int i = 1 / 0;
}
}
Таким образом, один транзакционный/нетранзакционный метод, который мы обсуждаем далее, вызывает другой транзакционный/нетранзакционный метод, оба из которых вызываются на основе ссылки на bean-компонент как на метод, а не черезthis
Вызов методов в этом классе. Таким образом, мы могли бы также объединить два метода написания библиотек.saveChildren
а такжеsavePersons
Переместитесь в два компонента для тестирования:
@Service
public class PersonService2 {
@Autowired
private PersonMapper personMapper;
public void saveChildren() {
saveChild1();
saveChild2();
}
public void saveChild1() {
Person person = new Person();
person.setUsername("child1");
person.setPassword("456");
personMapper.insertSelective(person);
}
public void saveChild2() {
Person person = new Person();
person.setUsername("child2");
person.setPassword("789");
personMapper.insertSelective(person);
}
}
@Service
public class PersonService {
@Autowired
private PersonMapper personMapper;
@Autowired
private PersonService2 personService2;
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class TransTest {
@Autowired
private PersonService personService;
@Test
public void test() {
personService.savePersons();
}
}
Текущая транзакция и транзакция родительского метода
В этой статье вопрос о том, создает ли родительский метод транзакцию, относится не только к вызывающему объекту, который вызывает этот метод, но и к вышестоящему методу в цепочке вызовов методов. текущий метод находится внутри транзакции, то есть транзакция существует, когда выполняется текущий метод.
Перечисление политик
Классы перечисления, связанные со стратегией распространения транзакций Spring, следующие:
package org.springframework.transaction.annotation;
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
private final int value;
private Propagation(int value) {
this.value = value;
}
public int value() {
return this.value;
}
}
ТРЕБУЕТСЯ - ешьте, если у вас есть еда, покупайте сами, если у вас ее нет
@Transactional
Стратегия распространения аннотаций по умолчанию:REQUIRED
:
public @interface Transactional {
Propagation propagation() default Propagation.REQUIRED;
}
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
На это указывает такая аннотация: если текущий метод имеет транзакцию, текущая транзакция поддерживается, если текущий метод не имеет транзакции, создается новая транзакция для собственного использования.
Нет отца, сын самостоятельный
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1/0;
}
Поскольку родительский метод не аннотирован, при выполнении строки 7 вызывается метод транзакции со стратегией распространения REQUIRED, и он создает новую транзакцию для собственного использования, поэтомуchild1, child2
потому что1/0
Исключение не будет вставлено, исключение брошено в родительский метод, родительский метод не будет откатывать ранее вставленное, потому что нет делаparent
, результат выполнения следующий:
----+----------+----------+
| id | username | password |
+----+----------+----------+
| 23 | parent | 123 |
+----+----------+----------+
Отец владеет, сын наследует
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.REQUIRED)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
@Autowired
private PersonService personService;
@Test
public void test() {
personService.savePersons();
}
потому чтоtest
Вызов метода транзакции savePersons бина personService и его стратегия распространения ОБЯЗАТЕЛЬНЫ, поэтому он создает новую транзакцию для собственного использования.При вызове ОБЯЗАТЕЛЬНОГО метода транзакции бина personService2 он обнаруживает, что существует текущая транзакция, и поэтому поддерживает текущую транзакцию. , поэтому вставляются parent, child1, child2. Поскольку они находятся в одной транзакции, все они откатываются после возникновения исключений 1/0:
mysql> select * from person;
Empty set (0.00 sec)
сленг
ОБЯЗАТЕЛЬНО, у начальника (родительский метод) есть еда (иметь дела), я (дочерний метод) ем с начальником (поддерживать текущие дела); у начальника нет еды, я покупаю себе еду
ПОДДЕРЖКА - ешьте, когда есть, голодайте без еды
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* {@code SUPPORTS} is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
Если есть текущая транзакция, текущая транзакция поддерживается, в противном случае текущий метод выполняется нетранзакционным способом.
поддержка отца и сына
Когда родительский метод создаст транзакцию, эффект от использования SUPPORTS и REQUIRED в дочернем методе будет таким же.
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
mysql> select * from person;
Empty set (0.00 sec)
отец без сына
Когда родительский метод не создает транзакцию, то и дочерний метод не будет создавать новую транзакцию по собственной инициативе:
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.SUPPORTS)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
mysql> select * from person;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 29 | parent | 123 |
| 30 | child1 | 456 |
| 31 | child2 | 789 |
+----+----------+----------+
сленг
ПОДДЕРЖКА: Если у босса есть еда, я ем с ним, если у босса нет еды, я могу только голодать.
ОБЯЗАТЕЛЬНО - должна быть еда
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
Заставить текущий метод выполняться транзакционным образом: если есть текущая транзакция, то поддерживать текущую транзакцию, в противном случае выдать исключение
поддержка отца и сына
То же самое для НЕОБХОДИМОГО, ПОДДЕРЖКИ, ОБЯЗАТЕЛЬНОГО
забастовка без отца
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.MANDATORY)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
Когда выполняется ОБЯЗАТЕЛЬНЫЙ метод транзакции personService2, обнаруживается, что транзакции нет, поэтому он напрямую генерирует исключение:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
mysql> select * from person;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 32 | parent | 123 |
+----+----------+----------+
сленг
ОБЯЗАТЕЛЬНО: Когда у босса есть еда, он ест вместе с боссом, когда у босса нет еды, я ухожу. Работайте, только если у вас есть еда
REQUIRES_NEW – Самостоятельность
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
Независимо от того, есть ли текущая транзакция, он создаст новую транзакцию для собственного использования, а если есть текущая транзакция, приостановит текущую транзакцию.
сирота сын самосовершенствование
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
saveChild2();
int i = 1 / 0;
}
В этом случае REQUIRED_NEW совпадает с REQUIRED.
mysql> select * from person;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 33 | parent | 123 |
+----+----------+----------+
Отец и сын не редкость
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
int i = 1 / 0;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveChildren() {
saveChild1();
saveChild2();
}
На этот раз я буду1/0
Перемещенный в родительский метод, родительский метод имеет транзакцию, поэтому вставка родительского метода будет отменена, но выполнение дочернего метода приостановит текущую транзакцию и создаст новую транзакцию, поэтому вставка дочернего метода все еще действует (после завершения выполнения дочернего метода транзакция родительского метода возобновится. будет автоматически восстановлена)
mysql> select * from person;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 40 | child1 | 456 |
| 41 | child2 | 789 |
+----+----------+----------+
сленг
REQUIRED_NEW: Независимо от того, есть у босса еда или нет, я покупаю еду для себя и не принимаю милостей от других.
NOT_SUPPORTED - не есть, а работать
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
Заставляет текущий код выполняться без транзакций, приостанавливая его, если есть текущая транзакция.
Отец и сын не нужны
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int i = 1 / 0;
saveChild2();
}
При выполнении дочернего метода транзакция приостанавливается, поэтому вставка дочернего элемента1 не откатывается, а транзакция возобновляется после возврата к родительскому методу, поэтому вставка родителя откатывается
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 43 | child1 | 456 |
+----+----------+----------+
Отец не имеет намерения сына
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int i = 1 / 0;
saveChild2();
}
Я не хочу выполнять этот метод транзакционным способом.Если текущей транзакции нет, разве это не будет именно то, что я хочу, чтобы родитель и ребенок1 не откатились
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 44 | parent | 123 |
| 45 | child1 | 456 |
+----+----------+----------+
сленг
NOT_SUPPORT: Я не ем независимо от того, есть у босса еда или нет, я машина, которая просто любит работать
НИКОГДА - бить, когда речь идет о еде
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
Выполните этот метод нетранзакционным способом, если есть прямое исключение текущей транзакции.
отец без сына
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {
saveChild1();
int i = 1 / 0;
saveChild2();
}
Этот сценарий имеет тот же эффект, что и неиспользование транзакции.
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 46 | parent | 123 |
| 47 | child1 | 456 |
+----+----------+----------+
отец и сын бастуют
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
}
@Transactional(propagation = Propagation.NEVER)
public void saveChildren() {
saveChild1();
int i = 1 / 0;
saveChild2();
}
При вызове подметода, поскольку в данный момент есть транзакция, подметод напрямую выдает исключение, родительская вставка откатывается, и подметод не выполняется, и, естественно, никакие данные не вставляются.
mysql> select * from person;
Empty set (0.00 sec)
сленг
НИКОГДА: когда босс упоминал о еде, я увольнялся.
NESTED
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager. Some JTA providers might support nested
* transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
Если текущей транзакции нет, эффект такой же, как и у REQUIRED, в противном случае этот метод выполняется как вложенные транзакции в текущую транзакцию. Откат внешней транзакции приводит к откату внутренней транзакции (даже если внутренняя транзакция выполняется нормально).
@Transactional(propagation = Propagation.REQUIRED)
public void savePersons() {
Person person = new Person();
person.setUsername("parent");
person.setPassword("123");
personMapper.insertSelective(person);
personService2.saveChildren();
int i = 1 / 0;
}
@Transactional(propagation = Propagation.NESTED)
public void saveChildren() {
saveChild1();
saveChild2();
}
Хотя подметод обычно выполняется в виде вложенной транзакции, операция вложенной транзакции должна ожидать фиксации самой внешней транзакции в текущей модели транзакции, которая будет записана в библиотеку, иначе будет выполнен откат с внешняя транзакция. Это следует отличать от REQUIRED_NEW, который приостанавливает текущую транзакцию и создает новую транзакцию (две транзакции не влияют друг на друга), а не создает вложенную транзакцию под текущей транзакцией (вложенная транзакция ограничивается текущей транзакцией). .
Следовательно, фиксация внутренней транзакции будет отменена вместе с внешней транзакцией:
mysql> select * from person;
Empty set (0.00 sec)
сленг
NESTED: если у босса нет еды, он покупает себе еду и ест все, что хочет; у босса есть еда, поэтому он ест вместе с боссом, а что есть, зависит от настроения босса