Requirements: use spring framework to integrate DBUtils technology to realize user transfer function
-
Basic functions
Step analysis:
- Create a java project and import coordinates
- Write Account entity class
- Write the AccountDao interface and implementation class
- Write the AccountService interface and implementation class
- Write spring core configuration file
- Write test code
1. Create a java project and import coordinates
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.15</version> </dependency> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> </dependencies>
2. Write AccountDao interface and implementation class
public interface AccountDao { /** * Transfer out operation * @param outUser * @param money */ void out(String outUser,Double money); /** * Transfer in operation * @param inUser * @param money */ void in(String inUser,Double money); } @Repository("AccountDao") public class AccountDaoImpl implements AccountDao { @Autowired private QueryRunner queryRunner; @Override public void out(String outUser, Double money) { String sql = "update account set money = money - ? where name = ?"; try { queryRunner.update(sql, money, outUser); } catch (SQLException throwables) { throwables.printStackTrace(); } } @Override public void in(String inUser, Double money) { String sql = "update account set money = money + ? where name = ?"; try { queryRunner.update(sql, money, inUser); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
3. Write AccountService interface and implementation class
public interface AccountService { /** * Transfer method */ void transfer(String outUser,String inUser,Double money); } @Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; /** * Transfer method */ @Override public void transfer(String outUser, String inUser, Double money) { // Write transaction related code // The money reduction method was called accountDao.out(outUser,money); // Simulation error // int i= 1/0; // The add money method was called accountDao.in(inUser,money); } }
4. Write spring core configuration 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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- Enable annotation scanning --> <context:component-scan base-package="com.zm"/> <!-- introduce properties --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- to configure DataSource --> <bean id="dataSource"> <property name="driverClassName" value="$"/> <property name="url" value="$"/> <property name="username" value="$"/> <property name="password" value="$"/> </bean> <!-- to configure queryRunner --> <bean id="queryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean> </beans>
5. Write test code
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:applicationContext.xml"}) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer() { accountService.transfer("tom", "jerry", 100d); } }
problem analysis
The above code transaction is in the Dao layer, and the transfer out and transfer in operations are independent transactions. However, in actual development, the business logic should be controlled in one transaction, so the transaction should be moved to the Service layer.
-
Traditional affairs
Step analysis:
- Write thread binding tool class
- Write transaction manager
- Modify the service layer code
- Modify dao layer code
1. Write thread binding tool class
@Component public class ConnectionUtils { @Autowired private DataSource dataSource; private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); /** * Get the bound connection on the current thread: if the obtained connection is empty, get the connection from the data source and put it in ThreadLocal (bound to the current thread) */ public Connection getThreadConnection() { // 1. Get the connection from ThreadLocal first Connection connection = threadLocal.get(); // 2. Judge whether there is a Connection in the current thread if(connection == null){ // 3. Obtain a connection from the data source and store it in ThreadLocal try { // Not null connection = dataSource.getConnection(); threadLocal.set(connection); } catch (SQLException e) { e.printStackTrace(); } } return connection; } /** * Unbind the connection of the current thread */ public void removeThreadConnection(){ threadLocal.remove(); } }
2. Write transaction manager
@Component("transactionManager") public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; /** * Open transaction */ public void beginTransaction(){ // Get connection object Connection connection = connectionUtils.getThreadConnection(); try { // A manual transaction was started connection.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } } /** * Commit transaction */ public void commit(){ Connection connection = connectionUtils.getThreadConnection(); try { connection.commit(); } catch (SQLException e) { e.printStackTrace(); } } /** * Rollback transaction */ public void rollback(){ Connection connection = connectionUtils.getThreadConnection(); try { connection.rollback(); } catch (SQLException e) { e.printStackTrace(); } } /** * Release resources */ public void release(){ // Change manual transaction back to auto commit transaction Connection connection = connectionUtils.getThreadConnection(); try { connection.setAutoCommit(true); // Return connections to the connection pool connectionUtils.getThreadConnection().close(); // Unbind thread connectionUtils.removeThreadConnection(); } catch (SQLException e) { e.printStackTrace(); } } }
3. Modify the service layer code
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private TransactionManager transactionManager; @Override public void transfer(String outUser, String inUser, Double money) { try { // 1. Start transaction transactionManager.beginTransaction(); // 2. Business operation // Write transaction related code // The money reduction method was called accountDao.out(outUser, money); // Simulation error // int i= 1/0; // The add money method was called accountDao.in(inUser, money); // 3. Submission of services transactionManager.commit(); } catch (Exception e) { // 4. Rollback transaction transactionManager.rollback(); e.printStackTrace(); } finally { // 5. Release resources transactionManager.release(); } } }
4. Modify Dao layer code
@Repository("AccountDao") public class AccountDaoImpl implements AccountDao { @Autowired private QueryRunner queryRunner; @Autowired private ConnectionUtils connectionUtils; /** * Transfer out operation */ @Override public void out(String outUser, Double money) { String sql = "update account set money = money - ? where name = ?"; try { queryRunner.update(connectionUtils.getThreadConnection(), sql, money, outUser); } catch (SQLException throwables) { throwables.printStackTrace(); } } /** * Transfer in operation */ @Override public void in(String inUser, Double money) { String sql = "update account set money = money + ? where name = ?"; try { queryRunner.update(connectionUtils.getThreadConnection(), sql, money, inUser); } catch (SQLException throwables) { throwables.printStackTrace(); } } }
problem analysis
The above code can realize transaction control through the transformation of the business layer, but due to the addition of transaction control, a new problem also arises: the business layer method becomes bloated and filled with a lot of duplicate code. And the business layer method and transaction control method are coupled, which violates the object-oriented development idea.
Business code and transaction code can be split to enhance business methods through dynamic agent. This will not affect the business layer and solve the problem of coupling.
Common dynamic agent technologies:
-
JDK agent, a dynamic proxy technology based on interface, uses the interceptor (invocationHandler) and the reflection mechanism to generate an anonymous class of the proxy interface. It calls InvokeHandler before calling the specific method, so as to enhance the method.
-
CGLIB proxy, dynamic proxy technology based on parent class - dynamically generate a subclass to be proxy, and the subclass overrides all non final methods of the class to be proxy; In the subclass, the method interception technology is used to intercept the calls of all the parent methods, and the crosscutting logic is weaved in to enhance the methods
-
JDK dynamic proxy mode
Remove the transaction control code of AccountServiceImpl
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Override public void transfer(String outUser, String inUser, Double money) { accountDao.out(outUser, money); // Simulation error int i= 1/0; accountDao.in(inUser, money); } }
JDK factory class
@Component public class JDKProxyFactory { @Autowired private AccountService accountService; @Autowired private TransactionManager transactionManager; /** * JDK dynamic proxy technology is used to generate the proxy object of the target class * ClassLoader loader : Class loader: get the class loader with the proxy object * Class<?>[] interfaces : All interfaces that the proxy class needs to implement * InvocationHandler h : When the proxy object calls any method in the interface, the invoke method in InvocationHandler will be executed */ public AccountService createAccountServiceJdkProxy() { return (AccountService) Proxy.newProxyInstance( accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() { /** * @param proxy Current proxy object reference * @param method The reference of the called target method * @param args The parameters used by the called target method */ @Override public Object invoke(Object proxy, Method method, Object[] args) { try { if ("transfer".equalsIgnoreCase(method.getName())) { System.out.println("JDK Proxy: Pre-Enhance ..."); transactionManager.beginTransaction(); method.invoke(accountService, args); System.out.println("JDK Proxy: Post-Enhance ..."); transactionManager.commit(); } else { method.invoke(accountService, args); } } catch (Exception e) { transactionManager.rollback(); e.printStackTrace(); } finally { transactionManager.release(); } return null; } }); } }
Test code
@Autowired private JDKProxyFactory jdkProxyFactory; @Test public void testTransferProxyJDK(){ // The proxy object currently returned is actually the proxy object of AccountService AccountService accountServiceJDKProxy = jdkProxyFactory.createAccountServiceJDKProxy(); // When the proxy object invokes any method in the proxy interface, the underlying invoke method will be executed accountServiceJDKProxy.transfer("tom", "jerry", 100d); }
-
CGLIB dynamic proxy mode
CGLIB factory class
@Component public class CglibProxyFactory { @Autowired private AccountService accountService; @Autowired private TransactionManager transactionManager; /** * Write the API corresponding to cglib to generate proxy objects for return * Parameter 1: bytecode object of target class * Parameter 2: action class. When the proxy object calls the original method of the target object, the intercept method will be executed */ public AccountService createAccountServiceCglibProxy() { return (AccountService) Enhancer.create(accountService.getClass(), new MethodInterceptor() { /** * @param o Represents the generated proxy object * @param method Reference to the calling target method * @param objects Method input parameter * @param methodProxy Proxy method */ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) { try { System.out.println("CGLIB Proxy: Pre-Enhance ..."); // Start transaction manually: call the start transaction method in the transaction manager class transactionManager.beginTransaction(); method.invoke(accountService, objects); System.out.println("CGLIB Proxy: Post-Enhance ..."); transactionManager.commit(); } catch (Exception e) { // Manually rollback transactions transactionManager.rollback(); e.printStackTrace(); } finally { // Release resources manually transactionManager.release(); } return null; } }); } }Getting to know AOP
-
What is AOP
AOP is the abbreviation of Aspect Oriented Programming, which means Aspect Oriented Programming.
AOP is the continuation of OOP (object-oriented programming). It is a hot spot in software development and an important content in Spring framework. Using AOP can isolate each part of business logic, reduce the coupling between each part of business logic, improve the reusability of program and improve the efficiency of development.
Advantages:
- During the running of the program, the function of the method is enhanced without modifying the source code
- The logic is clear. When developing the core business, you don't have to pay attention to the code to enhance the business
- Reduce duplicate code, improve development efficiency and facilitate later maintenance
-
AOP underlying implementation
In fact, the underlying layer of AOP is implemented through the dynamic proxy technology provided by Spring. During operation, Spring dynamically generates proxy objects through dynamic proxy technology. When the proxy object method is executed, it intervenes to enhance the function, and calls the method of the target object, so as to complete the function enhancement.
-
AOP related terms
The bottom layer of Spring's AOP implementation is to encapsulate the code of the above dynamic agent. After encapsulation, we only need to code the parts that need attention, and complete the method enhancement of the specified goal through configuration.
Before formally explaining the operation of AOP, we must understand the relevant terms of AOP. The commonly used terms are as follows:
* Target(Target object): The target object of the agent; Proxy class * Proxy(Proxy): a class is AOP After weaving in the enhancement, a result proxy class is generated; Generate proxy object * Joinpoint(Connection points): the so-called connection points refer to those points that can be intercepted; stay spring In, these points refer to methods because spring Only connection points of method type are supported; Methods that can be intercepted and enhanced * Pointcut(Entry point): the so-called entry point refers to what we want to do Joinpoint Definition of interception; That is, the method of real interception and enhancement * Advice(notice/ Enhanced): the so-called notification refers to the interception of Joinpoint The next thing to do is to classify Notifications: pre notification, post notification, exception notification, final notification and surround notification - A type that can be manually controlled by code; Enhanced business logic * Aspect(Aspect: it is the combination of entry point and notification (Introduction) * Weaving(Weaving: refers to the process of applying enhancements to the target object to create a new proxy object. Spring Dynamic proxy weaving is adopted, and AspectJ Compile time weaving and class load time weaving are adopted
-
Clear matters for AOP development
Development stage
- Entry point for writing core business code (target method of target class)
- Extract the common code and make it into notification (enhancement method)
- In the configuration file, declare the relationship between pointcuts and notifications, that is, facets
Run phase (Spring framework completes automatically)
The Spring framework monitors the execution of pointcut methods. Once it is monitored that the pointcut method is running, the proxy mechanism is used to dynamically create the proxy object of the target object. According to the notification category, the corresponding function of the notification is woven into the corresponding position of the proxy object to complete the complete code logic operation.
Underlying agent implementation
In Spring, the framework will decide which dynamic proxy method to adopt according to whether the target class implements the interface:
- When the bean implements the interface, the JDK proxy mode is used
- When the bean does not implement the interface, it can be implemented with cglib (cglib can be forcibly used) (add < AOP: AspectJ AutoProxy proxy target class = "true" / > in the spring configuration)
Summary
- aop - aspect oriented programming
- The underlying implementation of aop: dynamic agent based on JDK and dynamic agent based on Cglib
- Key concepts of aop:
Pointcut: a truly enhanced method
Advice: encapsulates ways to enhance business logic
Aspect: pointcut + notification
Weaving: the process of combining pointcuts with notifications to generate proxy objects
-
quick get start
Step analysis:
- Create a java project and import AOP related coordinates
- Create target interfaces and target implementation classes (define pointcuts)
- Create notification class and method (define notification)
- Give spring the right to create the target class and notification class objects
- Configure the weaving relationship and section in the core configuration file
- Write test code
1. Create a java project and import AOP related coordinates
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.11</java.version> <maven.compiler.source>1.11</maven.compiler.source> <maven.compiler.target>1.11</maven.compiler.target> </properties> <dependencies> <!-- Import spring of context Coordinates, context rely on aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- aspectj This is required for the weaving (tangent point) expression of jar Package) --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!-- spring integration junit --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2. Create target interface and target implementation class
public interface AccountService { /** * Target method: (entry point: the method to be intercepted and enhanced) */ void transfer(); } public class AccountServiceImpl implements AccountService { @Override public void transfer() { System.out.println("The transfer method is implemented...."); //int i = 1/0; } }
3. Create notification class
public class MyAdvice { public void before(){ System.out.println("Pre notification executed...."); } }
4. Give the creation right of target class and notification class objects to spring
<!-- Target class to IOC container --> <bean id="accountServcie"/> <!-- Notification class to IOC container --> <bean id="myAdvice"/>
5. Configure the weaving relationship and section in the core configuration file
Import AOP namespace
<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="accountServcie"/> <bean id="myAdvice"/> <aop:config> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut="execution(public void com.zm.service.impl.AccountServiceImpl.transfer())"/> </aop:aspect> </aop:config> </beans>
6. Write test code
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:applicationContext.xml"}) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer() { accountService.transfer(); } }
-
Detailed explanation of XML configuration AOP
Tangent expression
Expression syntax:
excution( [Modifier ] Return value type package name.Class name.Method name (parameter)
- The access modifier can be omitted
- The returned value type, package name, class name and method name can be replaced by asterisk *, representing any
- A point between the package name and the class name. It represents the class under the current package. There are two points .. Represents the classes under the current package and its sub packages
- The parameter list can use two points.. to represent any number and any type of parameter list
example:
-
execution([modifier] return value type package name. Class name. Method name (parameter))
execution(public void com.renda.service.impl.AccountServiceImpl.transfer(java.lang.String)) -
The access modifier can be omitted
execution(void com.renda.service.impl.AccountServiceImpl.transfer(java.lang.String)) -
The returned value type, package name, class name and method name can be replaced by asterisk *, representing any
execution(* .....()) -
A point between the package name and the class name represents the classes under the current package, and two points.. represent the classes under the current package and its sub packages
execution(* ...*()) -
The parameter list can use two points.. to represent any number and any type of parameter list
execution(* ...*(..))
Tangent expression extraction:
When multiple enhanced pointcut expressions are the same, the pointcut expression can be extracted. In the enhancement, the pointcut ref attribute is used instead of the pointcut attribute to reference the extracted pointcut expression.
public class MyAdvice { public void before(){ System.out.println("Pre notification executed...."); } public void afterReturning(){ System.out.println("Post notification executed...."); } public void afterThrowing(){ System.out.println("Exception notification executed...."); } public void after(){ System.out.println("The final notice was executed...."); } /** * @param pjp Proceeding JoinPoint - Connection point being executed: tangent point */ public Object around(ProceedingJoinPoint pjp){ Object proceed = null; try { System.out.println("Pre notification executed"); // Tangent point method execution proceed = pjp.proceed(); System.out.println("Post notification executed"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("Exception notification executed"); }finally { System.out.println("The final notice was executed"); } return proceed; } }
<aop:config> <!-- Extracted tangent expression --> <aop:pointcut id="myPointcut" expression="execution(* com.zm.service.impl.AccountServiceImpl.*(..))"/> <!-- Configuration aspect: entry point + notice --> <aop:aspect ref="myAdvice"> <aop:before method="before" pointcut-ref="myPointcut"/> <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/> <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/> <aop:after method="after" pointcut-ref="myPointcut"/> <!-- <aop:around method="around" pointcut-ref="myPointcut"/> --> </aop:aspect> </aop:config>
Notification type
Configuration syntax for notifications:
<aop:Notification type method="Method name in notification class pointcut="Tangent expression"></aop:Notification type>namelabelexplainBefore advice <aop:before>Used to configure pre notification. Specifies that the enhanced method is executed before the pointcut methodPost notification<aop:afterReturning>Used to configure post notifications. Specifies that the enhanced method is executed after the pointcut methodException notification<aop:afterThrowing>Used to configure exception notification; Specifies that the enhanced method is executed after an exception occursFinal notice<aop:after>Used to configure the final notification; The pointcut method executes regardless of whether there is an exception during executionAround Advice <aop:around>Used to configure surround notification; Developers can manually control when enhanced code is executed
Note: in general, surround notifications are used independently
-
Summary
-
aop weaving configuration
<aop:config>
< AOP: aspect ref = "notification class" >
< AOP: before method = "notification method name" pointcut = "pointcut expression" > < / AOP: before >
</aop:aspect>
</aop:config> -
Type of notification
Pre notification, post notification, exception notification and final notification
Around Advice -
Tangent expression
execution([modifier] return value type package name. Class name. Method name (parameter))
-
quick get start
Step analysis:
- Create a java project and import AOP related coordinates
- Create target interfaces and target implementation classes (define pointcuts)
- Create notification class (define notification)
- Give spring the right to create the target class and notification class objects
- Use annotations to configure the weaving relationship in the notification class and upgrade it to a faceted class
- Turn on the automatic agent for component scanning and AOP in the configuration file
- Write test code
1. Create a java project and import AOP related coordinates
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding> <java.version>1.11</java.version> <maven.compiler.source>1.11</maven.compiler.source> <maven.compiler.target>1.11</maven.compiler.target> </properties> <dependencies> <!-- Import spring of context Coordinates, context rely on aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.1.5.RELEASE</version> </dependency> <!-- aspectj Weaving in --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.13</version> </dependency> <!-- spring integration junit --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.1.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2. Create target interface and target implementation class
public interface AccountService { void transfer(); } public class AccountServiceImpl implements AccountService { @Override public void transfer() { System.out.println("The transfer method is implemented...."); } }
3. Create notification class
public class MyAdvice { public void before(){ System.out.println("Pre notification executed...."); } }
4. Give the creation right of target class and notification class objects to spring
@Service public class AccountServiceImpl implements AccountService { @Override public void transfer() { System.out.println("The transfer method is implemented...."); } } @Component public class MyAdvice { ... }
5. Use annotations to configure the weaving relationship in the notification class and upgrade it to the aspect class
@Component @Aspect // Upgrade to aspect class: configure the relationship between pointcuts and notifications public class MyAdvice { @Before("execution(* com.zm.service.impl.AccountServiceImpl.*(..))") public void before(){ System.out.println("Pre notification executed...."); } }
6. Enable the automatic agent of component scanning and AOP in the configuration 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:aop="http://www.springframework.org/schema/aop" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- open IOC Annotation scan --> <context:component-scan base-package="com.zm"/> <!-- aop Automatic agent: dynamic agent is used to enhance weaving and generate agent; proxy-target-class="true" Indicates mandatory use cglib Dynamic agent--> <aop:aspectj-autoproxy proxy-target-class="false"/> </beans>
7. Write test code
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer(){ accountService.transfer(); } }
-
Annotation configuration AOP details
Tangent expression
Extraction of tangent expression
@Component @Aspect // Upgrade to aspect class: configure the relationship between pointcuts and notifications public class MyAdvice { @Pointcut("execution(* com.zm.service.impl.AccountServiceImpl.*(..))") public void myPoint(){ } @Before("MyAdvice.myPoint()") public void before(){ System.out.println("Pre notification executed...."); } @AfterReturning("MyAdvice.myPoint()") public void afterReturning(){ System.out.println("Post notification executed...."); } @AfterThrowing("MyAdvice.myPoint()") public void afterThrowing(){ System.out.println("Exception notification executed...."); } @After("MyAdvice.myPoint()") public void after(){ System.out.println("The final notice was executed...."); } /** * @param pjp Proceeding JoinPoint - Connection point being executed: tangent point */ public Object around(ProceedingJoinPoint pjp){ Object proceed = null; try { System.out.println("Pre notification executed"); // Tangent point method execution proceed = pjp.proceed(); System.out.println("Post notification executed"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("Exception notification executed"); }finally { System.out.println("The final notice was executed"); } return proceed; } }
Notification type
Configuration syntax of notification: @ notification annotation ("pointcut expression")
namelabelexplainBefore advice @BeforeUsed to configure pre notification. Specifies that the enhanced method is executed before the pointcut methodPost notification@AfterReturningUsed to configure post notification. Specifies that the enhanced method is executed after the pointcut methodException notification@AfterThrowingUsed to configure exception notification; specifies the enhanced method to execute after an exception occursFinal notice@AfterUsed to configure the final notification; it will be executed regardless of whether there are exceptions when the pointcut method is executedAround Advice @AroundUsed to configure surround notifications; developers can manually control when enhanced code is executedbe careful
When the current four notifications are combined, a Bug of Spring execution order appears. The wrong execution order is as follows:
@Before -> @After -> @AfterReturning(If there are exceptions:@AfterThrowing)
If the surround notification @ Around annotation is used alone, there will be no Bug. The execution sequence is as follows:
@Before -> @AfterReturning(If there are exceptions:@AfterThrowing)-> @After
Annotation only configuration
Remove the applicationContext.xml configuration file and add the SpringConfig configuration class
@Configuration @ComponentScan("com.zm") @EnableAspectJAutoProxy // Turn on the automatic proxy of AOP and replace the < AOP: AspectJ AutoProxy / > configured in xml public class SpringConfig { }
Modify test class
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfig.class) public class AccountServiceTest { @Autowired private AccountService accountService; @Test public void testTransfer(){ accountService.transfer(); } }
-
summary
- Use the @ Aspect annotation to label the facet class
- Use @ Before and other annotations to mark the notification method
- Use @ Pointcut annotation to extract tangent expression
- Configure aop auto proxy < aop: AspectJ AutoProxy / > or @ EnableAspectJAutoProxy
Still using the previous transfer case, the two agent factory objects are deleted directly and implemented with spring's AOP idea
-
xml configuration implementation
configuration 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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- Enable annotation scanning --> <context:component-scan base-package="com.zm"/> <!-- introduce properties,load jdbc configuration file --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- to configure DataSource --> <bean id="dataSource"> <property name="driverClassName" value="$"/> <property name="url" value="$"/> <property name="username" value="$"/> <property name="password" value="$"/> </bean> <!-- to configure queryRunner --> <bean id="queryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean> <!-- AOP to configure --> <aop:config> <!-- 1.Tangent expression --> <aop:pointcut id="myPointcut" expression="execution(* com.zm.service.impl.AccountServiceImpl.*(..))"/> <!-- 2.Section configuration --> <aop:aspect ref="transactionManager"> <aop:before method="beginTransaction" pointcut-ref="myPointcut"/> <aop:after-returning method="commit" pointcut-ref="myPointcut"/> <aop:after-throwing method="rollback" pointcut-ref="myPointcut"/> <aop:after method="release" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans>
Transaction manager (notification)
package com.zm.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.sql.Connection; import java.sql.SQLException; /** * The transaction manager tool class includes: start transaction, commit transaction, rollback transaction, and release resource * Spring AOP Notification class for * */ @Component("transactionManager") public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; /** * Open transaction */ public void beginTransaction(){ // Get connection object Connection connection = connectionUtils.getThreadConnection(); try { // A manual transaction was started connection.setAutoCommit(false); System.out.println("Open transaction"); } catch (SQLException e) { e.printStackTrace(); } } /** * Commit transaction */ public void commit(){ Connection connection = connectionUtils.getThreadConnection(); try { connection.commit(); System.out.println("Commit transaction"); } catch (SQLException e) { e.printStackTrace(); } } /** * Rollback transaction */ public void rollback(){ Connection connection = connectionUtils.getThreadConnection(); try { connection.rollback(); System.out.println("Rollback transaction"); } catch (SQLException e) { e.printStackTrace(); } } /** * Release resources */ public void release(){ // Change manual transaction back to auto commit transaction Connection connection = connectionUtils.getThreadConnection(); try { connection.setAutoCommit(true); // Return connections to the connection pool connectionUtils.getThreadConnection().close(); // Unbind thread connectionUtils.removeThreadConnection(); System.out.println("Release resources"); } catch (SQLException e) { e.printStackTrace(); } } }
-
Annotation configuration implementation
configuration 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 http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- Enable annotation scanning --> <context:component-scan base-package="com.zm"/> <!-- introduce properties --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- to configure DataSource --> <bean id="dataSource"> <property name="driverClassName" value="$"/> <property name="url" value="$"/> <property name="username" value="$"/> <property name="password" value="$"/> </bean> <!-- to configure queryRunner --> <bean id="queryRunner"> <constructor-arg name="ds" ref="dataSource"/> </bean> <!-- open AOP Automatic proxy for --> <aop:aspectj-autoproxy/> </beans>
Transaction manager (notification)
@Component("transactionManager") @Aspect // Indicates that this class is a section class public class TransactionManager { @Autowired private ConnectionUtils connectionUtils; @Around("execution(* com.zm.service.impl.AccountServiceImpl.*(..))") public Object around(ProceedingJoinPoint pjp) throws SQLException { Object proceed = null; try { // Start manual transaction System.out.println("Open transaction"); connectionUtils.getThreadConnection().setAutoCommit(false); // Pointcut method execution proceed = pjp.proceed(); // Manually commit transactions System.out.println("Commit transaction"); connectionUtils.getThreadConnection().commit(); } catch (Throwable throwable) { throwable.printStackTrace(); // Manually rollback transactions System.out.println("Rollback transaction"); connectionUtils.getThreadConnection().rollback(); } finally { System.out.println("Release resources"); // Restore manual transactions to automatic transactions connectionUtils.getThreadConnection().setAutoCommit(true); // Return connections to the connection pool connectionUtils.getThreadConnection().close(); // Unbind thread connectionUtils.removeThreadConnection(); } return proceed; } }