Problems in transfer operation
Suppose we want to implement a transfer operation, we add a transfer method in IAccountService (business layer interface of the account),
/** * transfer accounts * @param sourceName Name of transfer out merchant * @param targetName Transfer in account name * @param money Transfer amount */ void transfer(String sourceName,String targetName,float money);
Then add the implementation of the method to its implementation class
@Override public void transfer(String sourceName, String targetName, float money) { //1. Query transfer out account by name Account source = dao.findAccountByName(sourceName); //2. Query transfer in account by name Account target = dao.findAccountByName(targetName); //3. Deduction from transfer out account source.setMoney(source.getMoney()-money); //4. Transfer in account plus money target.setMoney(target.getMoney()+money); //5. Update transfer out account dao.updateAccount(source); //6. Update transfer in account dao.updateAccount(target); }
Add test method
package com.spring.test; import com.spring.service.IAccountService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml") public class AccountservicImpl { @Autowired private IAccountService service; /** * Test transfer */ @Test public void testTransfer() { service.transfer("aaa","bbb",100); } }
At this time, the operation can be executed normally
But at this time, we have to do something in the implementation of the method
@Override public void transfer(String sourceName, String targetName, float money) { //1. Query transfer out account by name Account source = dao.findAccountByName(sourceName); //2. Query transfer in account by name Account target = dao.findAccountByName(targetName); //3. Deduction from transfer out account source.setMoney(source.getMoney()-money); //4. Transfer in account plus money target.setMoney(target.getMoney()+money); //5. Update transfer out account dao.updateAccount(source); //Make trouble int a=1/0; //6. Update transfer in account dao.updateAccount(target); }
At this time, an error will be reported during the operation, and when you check the database, you will find that the money has been deducted, but the transferred account has not been received.
After the teacher's analysis:
We need to encapsulate these operations into a transaction (modify the statement if successful, and roll back if unsuccessful)
Preliminary problem solving
Let's solve this problem:
First, we need to create a ConnectionUtils to implement the binding between thread and connection:
import javax.sql.DataSource; import java.sql.Connection; /** * Connection tool class * Implement thread binding * @author 28985 */ public class ConnectionUtils { private ThreadLocal<Connection> t1 = new ThreadLocal<Connection>(); private DataSource source; public void setSource(DataSource source) { this.source = source; } public Connection getThreadConnection(){ try { //Get connection from threadlocal Connection connection = t1.get(); //Determine whether there is a connection on the thread if (connection==null){ //Get the connection from the data source, bind it with the thread and store it in ThreadLocal connection=source.getConnection(); t1.set(connection); } return connection; } catch (Exception e){ throw new RuntimeException(e); } } public void removeConnection(){ t1.remove(); } }
The details of ThreadLocal are as follows: https://www.jianshu.com/p/6fc3bba12f38
After that, we need to create a transaction related class to provide us with various transaction operation methods TransactionManage
/** * Transaction related * @author 28985 */ public class TransactionManage { private ConnectionUtils connectionUtils; public void setConnectionUtils(ConnectionUtils connectionUtils) { this.connectionUtils = connectionUtils; } /** * Open transaction */ public void openTransaction(){ try { connectionUtils.getThreadConnection().setAutoCommit(false); } catch (Exception e){ throw new RuntimeException(e); } } /** * Commit transaction */ public void commit(){ try { connectionUtils.getThreadConnection().commit(); } catch (Exception e){ throw new RuntimeException(e); } } /** * Rollback transaction */ public void rollback(){ try { connectionUtils.getThreadConnection().rollback(); } catch (Exception e){ throw new RuntimeException(e); } } /** * Close transaction */ public void release(){ try { connectionUtils.getThreadConnection().close();//Return the connection back to the connection pool connectionUtils.removeConnection();//Unbind threads and connections } catch (Exception e){ throw new RuntimeException(e); } } }
Then you can make adjustments to the business layer
Its basic structure is:
try { //1 start transaction //2 perform the operation //3 commit transaction //4 return results } catch (Exception e){ //5 rollback operation } finally { //6 release the connection }
So you have the following code
/** * Business layer implementation class of account * Transaction control should be in the business layer * @author 28985 */ public class IAccountServiceImpl implements IAccountService { private IAccountDao dao; private TransactionManage manage; public void setManage(TransactionManage manage) { this.manage = manage; } public IAccountDao getDao() { return dao; } public void setDao(IAccountDao dao) { this.dao = dao; } public IAccountServiceImpl(IAccountDao dao) { this.dao = dao; } public IAccountServiceImpl() { } @Override public List<Account> findAllAccount() { try { //1 start transaction manage.openTransaction(); //2 perform the operation List<Account> allAccount = dao.findAllAccount(); //3 commit transaction manage.commit(); //4 return results return allAccount; } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } @Override public Account findAccountById(Integer id) { try { //1 start transaction manage.openTransaction(); //2 perform the operation Account account = dao.findAccountById(id); //3 commit transaction manage.commit(); //4 return results return account; } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } @Override public void saveAccount(Account account) { try { //1 start transaction manage.openTransaction(); //2 perform the operation dao.saveAccount(account); //3 commit transaction manage.commit(); } catch (Exception e){ //5 rollback operation manage.rollback(); } finally { //6 release the connection manage.release(); } } @Override public void updateAccount(Account account) { try { //1 start transaction manage.openTransaction(); //2 perform the operation dao.updateAccount(account); //3 commit transaction manage.commit(); } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } @Override public void delAccount(int id) { try { //1 start transaction manage.openTransaction(); //2 perform the operation dao.delAccount(id); //3 commit transaction manage.commit(); } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } @Override public void transfer(String sourceName, String targetName, float money) { try { //1 start transaction manage.openTransaction(); //2 perform the operation //2.1. Query transfer out account by name Account source = dao.findAccountByName(sourceName); //2.2. Query transfer in account by name Account target = dao.findAccountByName(targetName); //2.3. Deduction from transfer out account source.setMoney(source.getMoney()-money); //2.4. Transfer in account plus money target.setMoney(target.getMoney()+money); //2.5. Update transfer out account dao.updateAccount(source); //2. Make trouble int a=1/0; //2.6. Update transfer in account dao.updateAccount(target); //3 commit transaction manage.commit(); } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } }
So we can find that the Connection has been transferred from QueryRunner to ConnectionUtils
Therefore, for each runner. Query ("select * from account", new beanlisthandler < account > (account. Class));
Change to runner. Query (connectionutil. Getthreadconnection(), "select * from account", new beanlisthandler < account > (account. Class));
Namely:
/** * Persistence layer implementation class of account * @author 28985 */ public class AccountDaoImpl implements IAccountDao { private QueryRunner runner; private ConnectionUtils connectionUtil; public void setConnectionUtil(ConnectionUtils connectionUtil) { this.connectionUtil = connectionUtil; } public void setRunner(QueryRunner runner) { this.runner = runner; } @Override public List<Account> findAllAccount() { try { return runner.query(connectionUtil.getThreadConnection(),"select * from account",new BeanListHandler<Account>(Account.class)); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public Account findAccountById(Integer id) { try { return runner.query(connectionUtil.getThreadConnection(),"select * from account where id ="+id,new BeanHandler<Account>(Account.class)); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public void saveAccount(Account account) { try { runner.update(connectionUtil.getThreadConnection(),"insert into account(name,money) value(?,?)",account.getName(),account.getMoney()); } catch (SQLException e) { throw new RuntimeException(e); } } @Override public void updateAccount(Account account) { try { runner.update(connectionUtil.getThreadConnection(),"update account set name=?,money=? where id = ?",account.getName(),account.getMoney(),account.getId()); } catch (SQLException e) { e.printStackTrace(); } } @Override public void delAccount(int id) { try { runner.update(connectionUtil.getThreadConnection(),"delete from account where id = ?",id); } catch (SQLException e) { e.printStackTrace(); } } @Override public Account findAccountByName(String name) { try { List<Account> accounts=runner.query(connectionUtil.getThreadConnection(),"select * from account where name = ?",new BeanListHandler<Account>(Account.class),name); if (accounts.size()==0||accounts == null){ return null; } if (accounts.size()>1){ throw new RuntimeException("There is a problem with the result set"); } return accounts.get(0); } catch (SQLException e) { throw new RuntimeException(e); } } }
Then comes the 'spring' configuration file
Note that there is no need to inject datasource into QueryRunner
<?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"> <!--Configure business layer--> <bean id="accountService" class="com.spring.service.impl.IAccountServiceImpl"> <property name="dao" ref="accountDao"/> <property name="manage" ref="transactionManage"/> </bean> <!--Configure persistence layer--> <bean id="accountDao" class="com.spring.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"/> <property name="connectionUtil" ref="connectionUtils"></property> </bean> <!--to configure runner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <!-- <constructor-arg name="ds" ref="ds"/> At this time, it is no longer necessary to pass QueryRunner The connection has been handed over to Utils To manage--> </bean> <!--to configure DataSource((data source)--> <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="root"/> <property name="password" value="adminadmin"/> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_test"/> </bean> <!-- Configure connection tool class connection and thread binding implementation class--> <bean id="connectionUtils" class="com.spring.utils.ConnectionUtils"> <property name="source" ref="ds"></property> </bean> <!-- Configure transaction related--> <bean id="transactionManage" class="com.spring.utils.TransactionManage"> <property name="connectionUtils" ref="connectionUtils"></property> </bean> </beans>
After running the previous transfer method, you will find that the transaction works and the rollback is successful
But there is a problem here. The code is very bloated and the reuse rate is very low, so we will use dynamic agent to solve these problems
Dynamic agent
Features: bytecode (class) files are created with use and loaded with use
Function: enhance the method without modifying the implementation class source code
implement the method when there is no implementation class
Classification:
interface based dynamic proxy
subclass based dynamic proxy
https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984#0
Interface based dynamic agent
Class involved: Proxy
Provider: jdk official
How to create a proxy object:
newProxyInstance method in Proxy class
Requirements for creating proxy objects:
the proxied object implements at least one interface, otherwise it cannot be used
Parameters of newProxyInstance method:
ClassLoader class loader
load the bytecode of the proxy object and use the same class loader as the proxy object
Class [] bytecode array
let the proxy object and the proxy object have the same method (the interface of the proxy object)
InvocationHandler allows us to write how to proxy. Generally, we write an implementation class of the interface, which is usually an internal class
Example:
Manufacturer's interface:
package com.spring.proxy.producers; /** * @author GeGeGe * @version 1.0 * @date 2021/10/2 9:56 */ public interface IProducer { /** * purchase * @param money Money received by the manufacturer */ public void buy(float money); /** * After sale * @param money Money received by the manufacturer s */ public void shouHou(float money); }
Manufacturer implementation class
package com.spring.proxy.producers; /** * @author GeGeGe * @version 1.0 * @date 2021/10/2 10:00 */ public class ProducerImpl implements IProducer{ @Override public void buy(float money) { System.out.println("The goods have been sold and received"+money+"element"); } @Override public void shouHou(float money) { System.out.println("Goods received after sale"+money+"element"); } }
Main function
public static void main(String[] args) { ProducerImpl producer = new ProducerImpl(); IProducer producer1 = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() { /** *Any interface method that executes the proxied object will pass through this method */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { float money = (float) args[0]; if ("buy".equals(method.getName())) { money *= 0.8f;//Manufacturer's draw 0.2 } return method.invoke(producer,money); } }); producer1.buy(10000); }
The running result is
This indicates that the producer1 object has been proxied
Subclass based dynamic agent
Category involved: Enhancer
Provider: cglib
How to create a proxy object:
creat e method in Enhancer class
Requirements for creating proxy objects:
the proxied object cannot be the final class
Parameters of newProxyInstance method:
ClassLoader class loader
load the bytecode of the proxy object and use the same class loader as the proxy object
Class [] bytecode array
let the proxy object and the proxy object have the same method (the interface of the proxy object)
InvocationHandler allows us to write how to proxy. Generally, we write an implementation class of the interface, which is usually an internal class
Because cglib is used, we first import the dependency of cglib
<dependencies> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> </dependencies>
Change main code
public class Client { public static void main(String[] args) { ProducerImpl producer = new ProducerImpl(); IProducer cglibproducer = (IProducer) Enhancer.create(ProducerImpl.class, new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { float money = (float) objects[0]; if ("buy".equals(method.getName())) { money *= 0.8f;//Manufacturer's draw 0.2 } return method.invoke(producer,money); } }); cglibproducer.buy(1000); } }
At this point, you can run and get the results:
Using dynamic agent to solve the problem of bloated transaction control code
Let's delete all the transaction control codes in the original service and change them to their original appearance
package com.spring.service.impl; import com.spring.dao.IAccountDao; import com.spring.domain.Account; import com.spring.service.IAccountService; import com.spring.utils.TransactionManage; import java.util.List; /** * Business layer implementation class of account * Transaction control should be in the business layer * @author 28985 */ public class IAccountServiceImpl implements IAccountService { private IAccountDao dao; public IAccountDao getDao() { return dao; } public void setDao(IAccountDao dao) { this.dao = dao; } public IAccountServiceImpl(IAccountDao dao) { this.dao = dao; } public IAccountServiceImpl() { } @Override public List<Account> findAllAccount() { return dao.findAllAccount(); } @Override public Account findAccountById(Integer id) { return dao.findAccountById(id); } @Override public void saveAccount(Account account) { dao.saveAccount(account); } @Override public void updateAccount(Account account) { dao.updateAccount(account); } @Override public void delAccount(int id) { dao.delAccount(id); } @Override public void transfer(String sourceName, String targetName, float money) { //2.1. Query transfer out account by name Account source = dao.findAccountByName(sourceName); //2.2. Query transfer in account by name Account target = dao.findAccountByName(targetName); //2.3. Deduction from transfer out account source.setMoney(source.getMoney()-money); //2.4. Transfer in account plus money target.setMoney(target.getMoney()+money); //2.5. Update transfer out account dao.updateAccount(source); //2. Make trouble // int a=1/0; //2.6. Update transfer in account dao.updateAccount(target); } }
At this point, create another BeanFactory factory class
It is used to create the proxy object of AccountService
/** * @author GeGeGe * @version 1.0 * @date 2021/10/2 12:19 */ public class BeanFactory { private IAccountService accountService; private TransactionManage manage; public void setManage(TransactionManage manage) { this.manage = manage; } public void setAccountService(IAccountService accountService) { this.accountService = accountService; } /** * Gets the proxy object of the service * @return */ public IAccountService getAccountService() { IAccountService proxyAccountService=(IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(), accountService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object rtObject; try { //1 start transaction manage.openTransaction(); //2 perform the operation rtObject = method.invoke(accountService,args); //3 commit transaction manage.commit(); //4 return results return rtObject; } catch (Exception e){ //5 rollback operation manage.rollback(); throw new RuntimeException(e); } finally { //6 release the connection manage.release(); } } }); return proxyAccountService; } }
Transaction related code has been enhanced in the agent
Then inject manage and accountService for beanfactory in bean.xml
And produce an accountService with an agent added
<?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"> <!-- Factory production agent AccountService--> <bean id="proxyService" factory-bean="beanFactory" factory-method="getAccountService"/> <!-- Configuration factory--> <bean id="beanFactory" class="com.spring.factory.BeanFactory"> <property name="accountService" ref="accountService"/> <property name="manage" ref="transactionManage"/> </bean> <!--Configure business layer--> <bean id="accountService" class="com.spring.service.impl.IAccountServiceImpl"> <property name="dao" ref="accountDao"/> </bean> <!--Configure persistence layer--> <bean id="accountDao" class="com.spring.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"/> <property name="connectionUtil" ref="connectionUtils"></property> </bean> <!--to configure runner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"> <!-- <constructor-arg name="ds" ref="ds"/> At this time, it is no longer necessary to pass QueryRunner The connection has been handed over to Utils To manage--> </bean> <!--to configure DataSource((data source)--> <bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="root"/> <property name="password" value="adminadmin"/> <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_test"/> </bean> <!-- Configure connection tool class connection and thread binding implementation class--> <bean id="connectionUtils" class="com.spring.utils.ConnectionUtils"> <property name="source" ref="ds"></property> </bean> <!-- Configure transaction related--> <bean id="transactionManage" class="com.spring.utils.TransactionManage"> <property name="connectionUtils" ref="connectionUtils"></property> </bean> </beans>
Run test:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:bean.xml") public class AccountservicImpl { @Qualifier("proxyService") @Autowired private IAccountService service; /** * Test transfer */ @Test public void testTransfer() { service.transfer("aaa","bbb",100); } }
Note that a note is added here: @ Qualifier("proxyService") is because the proxy object produced by the factory and the beanaccountService configured in the business layer meet the conditions of Autowired. At this time, we need to specify it
So far, using dynamic agents to solve the problem of transaction control is over, but we find that the configuration file is very cumbersome, so we need to launch AOP in spring
AOP
What is AOP
The Chinese meaning of AOP is a technology for aspect oriented programming, which realizes the unified maintenance of program functions through precompiled mode and dynamic agent during operation. AOP is the continuation of OOP, a hot spot in software development, an important content in Spring framework, and a derivative paradigm of functional programming. 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-------- Baidu Encyclopedia
Role and advantages of AOP
Function: during the operation of the program, the source code is not modified, and the existing methods are enhanced
Advantages:
Reduce duplicate codes
Improve development efficiency
Convenient maintenance
AOP in spring
AOP related terms (understand)
Joinpoint:
The so-called connection points refer to those intercepted points. In spring, these points refer to methods, because spring only supports connection points of method type
pointout:
The so-called pointcut refers to the definition of which joinpoints we want to intercept (in this case, it refers to the methods that need transaction control, and the connection points that do not need transaction control cannot be called pointcuts)
Advice:
Notification refers to what to do after intercepting the Joinpoint
Notification types: pre notification, post notification, exception notification, final notification, and surround notification
Introduction:
A special notification that dynamically adds some methods to a class during runtime without modifying the code
Target (target object)
The target object of the proxy (the proxied object)
Weaving:
Refers to the process of applying enhancements to the target object to create a new proxy object
srping uses dynamic proxy weaving, AspectJ uses compile time weaving and class loading time weaving
Proxy:
After a class is woven and enhanced by AOP, a result proxy class is generated
Aspect:
It's a combination of pointcuts and notifications
AOP based on xml
Create business layer interface and implementation class
package com.spring.services; /** * Business layer interface of account * @author GeGeGe * @version 1.0 * @date 2021/10/5 20:13 */ public interface IAccountService { /** * Simulated save account */ public void save(); /** * Simulation update account * @param i */ public void update(int i); /** * Simulate account deletion * @return */ public int delete(); }
import com.spring.services.IAccountService; /** * @author GeGeGe * @version 1.0 * @date 2021/10/5 20:27 */ public class AccountServiceImpl implements IAccountService { @Override public void save() { System.out.println("preservation"); } @Override public void update(int i) { System.out.println("to update"); } @Override public int delete() { System.out.println("delete"); return 0; } }
Provide common methods when creating a logger class
package com.spring.utils; /**Logging tool, which provides public code * @author GeGeGe * @version 1.0 * @date 2021/10/5 20:29 */ public class Logger { /** * Pre message */ public void beforePrint(){ System.out.println("Pre message"); } /** * Post message */ public void afterPrint(){ System.out.println("Post message"); } /** * Exception message */ public void tryPrint(){ System.out.println("Exception message"); } /** * news */ public void finallyPrint(){ System.out.println("Final message"); } }
Configuration 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: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"> <!-- to configure srping of Ioc,hold service Object configuration--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> <!-- to configure Logger class --> <bean id="logger" class="com.itheima.utils.Logger"></bean> <!--to configure AOP--> <aop:config> <!-- Configure pointcut expressions id Property specifies the unique identity of the expression. expression Property specifies the content of the expression This label is written in aop:aspect The inside of the label can only be used in the current section. It can also be written in aop:aspect Outside, all sections are now available --> <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"></aop:pointcut> <!--Configure section --> <aop:aspect id="logAdvice" ref="logger"> <!-- Configure pre notification: executed before the pointcut method is executed <aop:before method="beforePrint" pointcut-ref="pt1" ></aop:before>--> <!-- Configure post notification: the value after the pointcut method executes normally. It and exception notification can always be executed only once <aop:after-returning method="afterPrint" pointcut-ref="pt1"></aop:after-returning>--> <!-- Configure exception notification: executed after the exception is generated during the execution of pointcut methods. It and post notification can always execute only one <aop:after-throwing method="tryPrint" pointcut-ref="pt1"></aop:after-throwing>--> <!-- Configure final notification: the pointcut method executes after it whether it executes normally or not <aop:after method="finallyPrint" pointcut-ref="pt1"></aop:after>--> </aop:aspect> </aop:config> </beans>
Write main method:
import com.spring.services.IAccountService; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author GeGeGe * @version 1.0 * @date 2021/10/6 15:50 */ public class AopTest { public static void main(String[] args) { ApplicationContext appletContext = new ClassPathXmlApplicationContext("bean.xml"); IAccountService as = (IAccountService)appletContext.getBean("accountService"); as.save(); as.update(1); as.delete(); } }
Operation results:
Pre message preservation Post message Final message Pre message to update Post message Final message Pre message delete Post message Final message
Let's change the delete method
@Override public int delete() { System.out.println("delete"); int k = 5/0; return 0; }
The result is:
Pre message preservation Post message Final message Pre message to update Post message Final message Pre message delete Exception message Final message Exception in thread "main" java.lang.ArithmeticException: / by zero
At this point, we can conclude that the exception message and the post message conflict, and there can only be one
Annotation based AOP
The xml configuration of annotation based aop is very simple
You only need to specify the package to scan and turn on spring's support for aop annotations
The configuration file is as follows
<?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"> <!-- Specify the packages to scan--> <context:component-scan base-package="com.spring"/> <!-- open spring yes aop Annotation support--> <aop:aspectj-autoproxy/> </beans>
First, implement the previously implemented in xml
<!-- to configure srping of Ioc,hold service Object configuration--> <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean> <!-- to configure Logger class --> <bean id="logger" class="com.itheima.utils.Logger"></bean>
These two lines of code
We use annotations instead
Just add @ Service and @ Component annotations to AccountServiceImpl and Logger
Next is the aop part
The first sentence has no practical effect. There is no need to configure here
In the second sentence, pointcut, we use the pointcut annotation instead to define an empty function
@Pointcut("execution(* com.spring.services.impl.*.*(..))") public void pointcut(){}
The third sentence is the statement
We need to add the @ Aspect annotation to the class
@Aspect
The following corresponding notes
@Before("pointcut()") @AfterReturning("pointcut()") @AfterThrowing("pointcut()") @After("pointcut()") @Around("pointcut()")
So the logger is changed to
package com.spring.utils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; /**Logging tool, which provides public code * @author GeGeGe * @version 1.0 * @date 2021/10/5 20:29 */ @Component @Aspect//Indicates that the current class is a faceted class public class Logger { @Pointcut("execution(* com.spring.services.impl.*.*(..))") public void pointcut(){} /** * Pre message */ @Before("pointcut()") public void beforePrint(){ System.out.println("Pre message"); } /** * Post message */ @AfterReturning("pointcut()") public void afterPrint(){ System.out.println("Post message"); } /** * Exception message */ @AfterThrowing("pointcut()") public void tryPrint(){ System.out.println("Exception message"); } /** * news */ @After("pointcut()") public void finallyPrint(){ System.out.println("Final message"); } /** * Around Advice * @param pjp */ // @Around("pointcut()") public void aroundPrint(ProceedingJoinPoint pjp){ try { System.out.println("Front"); Object proceed = pjp.proceed(pjp.getArgs()); System.out.println("Post"); } catch (Throwable e) { System.out.println("abnormal"); throw new RuntimeException(e); }finally { System.out.println("final"); } } }
Finally, you can get the same result as the xml configuration. Note that in some versions of spring 5.0. X, the post message and final message are sequential bug s
Do not use xml at all
@Configuration @ComponentScan(basePackages="com.spring") @EnableAspectJAutoProxy public class SpringConfiguration { }