Catalog
3. Notes on Handwritten Transactions
3.4 Packaging Programming Transactions
1. Introduction
Transactional is a transaction annotation defined in spring, which is added to a method or class to open a transaction. The annotation information of bean s is acquired mainly by reflection, and programmable transactions are encapsulated and implemented by AOP. (spring-5.1.8.RELEASE)
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { @AliasFor("transactionManager") String value() default ""; @AliasFor("value") String transactionManager() default ""; Propagation propagation() default Propagation.REQUIRED; Isolation isolation() default Isolation.DEFAULT; int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; boolean readOnly() default false; Class<? extends Throwable>[] rollbackFor() default {}; String[] rollbackForClassName() default {}; Class<? extends Throwable>[] noRollbackFor() default {}; String[] noRollbackForClassName() default {}; }
2. Custom Annotations
2.1 Definition
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface MyAnnotation { //Properties of custom annotations int id() default 0; String name() default "Default name"; String[]arrays(); String title() default "Default title"; }
2.2 Test
public class App { @MyAnnotation(name = "Yongqian Wang", arrays = {"2", "3"}) public void aMethod() {} public void bMethod() {} public static void main(String[] args) throws ClassNotFoundException { // Reflect the information to the class Class<?> clazz = Class.forName("com.wyq.App"); // Get all methods of the current class (excluding inheritance) Method[] methods = clazz.getDeclaredMethods(); // Traversing the information for each method for (Method method : methods) { System.out.println("Method name:" + method.getName()); // Get the annotations above the method MyAnnotation annotation = method.getDeclaredAnnotation(MyAnnotation.class); if (annotation == null) { System.out.println("There is no comment on this method...."); } else { System.out.println("Id : " + annotation.id()); System.out.println("Name : " + annotation.name()); System.out.println("Title : " + annotation.title()); System.out.println("Arrays : " + annotation.arrays()); } System.out.println("--------------------------"); } } } //Console information: //Method name: main //There is no comment on this method. -------------------------- //Method Name: aMethod Id : 0 Name : Yongqian Wang Title : Default title Arrays : [Ljava.lang.String;@24d46ca6 -------------------------- //Method Name: bMethod //There is no comment on this method. --------------------------
2.3 Summary
From a small demo like the one above, we can see that reflection captures annotation information for each method and then judges it. If this is the @Transactional annotation, spring will open the transaction. Next, we can implement a transaction annotation on our own in this way.
3. Notes on Handwritten Transactions
3.1 maven dependence
<dependencies> <!-- Introduce Spring-AOP Equivalent correlation Jar --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.5.4</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency> </dependencies>
3.2 Configure spring.xml file
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--Scan the specified path--> <context:component-scan base-package="com.wyq" /> <!--Open Section Agent--> <aop:aspectj-autoproxy /> <!--Data source object, C3P0 Connection pool--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/> <property name="user" value="root"/> <property name="password" value="123456"/> </bean> <!--JdbcTemplate Tool class instance--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!--Configuration transaction--> <bean id="dataSoutceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
3.3 Customize transaction annotations (by reflecting annotations on parsing methods, if any, execute transaction logic)
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface MyAnnotation { //Properties of custom annotations int id() default 0; String name() default "Default name"; String[] arrays() default {}; String title() default "Default title"; }
3.4 Packaging Programming Transactions
@Component @Scope("prototype") public class TransactionUtil { // Global Acceptance Transaction State private TransactionStatus transactionStatus; // Acquisition Transaction Origin @Autowired private DataSourceTransactionManager dataSourceTransactionManager; // Open a transaction public TransactionStatus begin() { System.out.println("Open a transaction"); transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute()); return transactionStatus; } // Submission transaction public void commit(TransactionStatus transaction) { System.out.println("Submission transaction"); if (dataSourceTransactionManager != null) { dataSourceTransactionManager.commit(transaction); } } public void rollback(TransactionStatus transaction) { System.out.println("Roll back transactions"); if (dataSourceTransactionManager != null) { dataSourceTransactionManager.rollback(transaction); } } }
3.5 Encapsulate transaction tool classes via AOP to trigger transactions based on surround notification and exception notification
@Component @Aspect public class AopTransaction { @Autowired private TransactionUtil transactionUtil; private TransactionStatus transactionStatus; /** * Around notifications, before and after methods * * @param pjp breakthrough point */ @Around("execution(* com.wyq.service.*.*(..))") public void around(ProceedingJoinPoint pjp) throws Throwable { // Annotations for acquisition methods MyAnnotation annotation = this.getMethodMyAnnotation(pjp); // Determine whether a transaction needs to be opened transactionStatus = begin(annotation); // Calling the target proxy object method pjp.proceed(); // Judging closure of transactions commit(transactionStatus); } /** * Get transaction annotations on proxy methods * * @param pjp * @return * @throws Exception */ private MyAnnotation getMethodMyAnnotation(ProceedingJoinPoint pjp) throws Exception { // Method of obtaining proxy object String methodName = pjp.getSignature().getName(); // Getting the target object Class<?> classTarget = pjp.getTarget().getClass(); // Get the target object type Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes(); // Method of Obtaining Target Object Method objMethod = classTarget.getMethod(methodName, par); // Obtain transaction annotations on this method MyAnnotation annotation = objMethod.getDeclaredAnnotation(MyAnnotation.class); return annotation; } /** * Open a transaction * * @param annotation * @return */ private TransactionStatus begin(MyAnnotation annotation) { if (annotation == null) { return null; } return transactionUtil.begin(); } /** * Submission transaction * * @param transactionStatus */ private void commit(TransactionStatus transactionStatus) { if (transactionStatus != null) { transactionUtil.commit(transactionStatus); } } /** * Exception notification rollback transaction */ @AfterThrowing("execution(* com.wyq.service.*.*(..))") public void afterThrowing() { // Get the current transaction rollback directly if (transactionStatus != null) { transactionUtil.rollback(transactionStatus); } } }
3.6 dao layer
/* CREATE TABLE `t_user0` ( `name` varchar(20) NOT NULL, `age` int(5) DEFAULT NULL, PRIMARY KEY (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; */ @Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public int add(String name, Integer age) { String sql = "INSERT INTO t_user0(NAME, age) VALUES(?,?);"; int result = jdbcTemplate.update(sql, name, age); System.out.println("Successful insertion"); return result; } }
3.7 service Layer
@Service public class UserService { @Autowired private UserDao userDao; // Add transaction annotations @MyAnnotation public void add() { userDao.add("test001", 20); int i = 1 / 0; //Set exceptions to check the correctness of transactions userDao.add("test002", 21); // // If you do not want to try, then an exception will not be detected by aop's exception notification, and you must manually roll back the transaction in the catch. // try { // userDao.add("test001", 20); // int i = 1 / 0; // userDao.add("test002", 21); // } catch (Exception e) { // // Roll back the current transaction // System.out.println("rollback"); // TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // } } public void del() { System.out.println("del..."); } }
3.8 Test
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userService = (UserService) applicationContext.getBean("userService"); // aop() intercepts the userService class, and adding custom transaction annotations triggers transaction logic userService.add(); // If the del() method is not annotated, nothing will trigger. //userService.del(); } }