1, Dynamic agent
- Dynamic proxy is based on interface proxy or subclass proxy
1. Proxy interface
public interface Calculator {
public int add(int x, int y);
public int sub(int x, int y);
public int mul(int x, int y);
public int div(int x, int y);
}
2. Implementation class of the interface being proxied
public class MyCalculator implements Calculator{
@Override
public int add(int x, int y) {
return x+y;
}
@Override
public int sub(int x, int y) {
return x-y;
}
@Override
public int mul(int x, int y) {
return x*y;
}
@Override
public int div(int x, int y) {
return x/y;
}
}
3. Creating proxy objects with reflections
public class CalculatorProxy {
public static Calculator getCalculatorProxy(Calculator calculator) {
//Gets the class loader for the proxy object
ClassLoader loader = calculator.getClass().getClassLoader();
//Gets all the interfaces implemented by the proxy object
Class<?>[] interfaces = calculator.getClass().getInterfaces();
//handle executed by proxy object
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//Before advice
LogUtils.beforeRun(method, args);
//Method operation
Object o = null;
try {
o = method.invoke(calculator, args); //Run the method, and the calculator is based on the interface agent
//Post notification
LogUtils.ReturnRun(method, args);
} catch (Exception e) {
//Exception notification
LogUtils.ExceptionRun(method, args);
} finally {
//Final notice
LogUtils.AfterRun(method, args);
}
return o; //Parameter return value
}
};
Object o = Proxy.newProxyInstance(loader, interfaces, h);
return (Calculator) o;
}
}
4. Log enhancement methods
public class LogUtils {
public static void beforeRun(Method method, Object[] args) {
System.out.println("method" + method.getName() + "Pre run log" + Arrays.asList(args));
}
public static void ReturnRun(Method method, Object[] args) {
System.out.println("method" + method.getName() + "Run return log" + Arrays.asList(args));
}
public static void ExceptionRun(Method method, Object[] args) {
System.out.println("method" + method.getName() + "Run exception log" + Arrays.asList(args));
}
public static void AfterRun(Method method, Object[] args) {
System.out.println("method" + method.getName() + "Run end log" + Arrays.asList(args));
}
}
5. Test and results
@Test
public void proxyCalculator() {
Calculator calculator = new MyCalculator();
//Get proxy object
Calculator proxy = CalculatorProxy.getCalculatorProxy(calculator);
//Proxy here is actually a proxy type, class com.sun.proxy.$Proxy4, not a Calculator type
//If it is of type calculator, the method of calculator cannot be enhanced
System.out.println(proxy.getClass());
proxy.add(1, 2);
System.out.println("=====================================================================");
proxy.sub(1, 2);
System.out.println("=====================================================================");
proxy.mul(1, 2);
System.out.println("=====================================================================");
proxy.div(6, 0);
System.out.println("=====================================================================");
}

2, AOP
1. Write the class of the proxy
public class Calculator { //There is no inherited parent class or implementation interface here. aop will use cglib to proxy common proxy objects of this class
public int add(int x, int y) {
System.out.println("Execution target method");
return x+y;
}
public int sub(int x, int y) {
System.out.println("Execution target method");
return x-y;
}
public int mul(int x, int y) {
System.out.println("Execution target method");
return x*y;
}
public int div(int x, int y) {
System.out.println("Execution target method");
return x/y;
}
}
2. Annotation configures two facet classes
- Pointcut configuration pointcut expression
- joinPoint gets information about the current enhancement method
- joinPoint.getArgs(); Get enhanced method parameter list
- getSignature().getName(); Gets the name of the method
- @Before("cutPoint()") pre notification
- @After returning (value = "cutPoint()", returning = "result") post notification
- returning can be used to receive return values
- @AfterThrowing(value = "cutPoint()", throwing = "exception") exception notification
- throwing is used to specify that only the current exception type will execute the exception notification method, so generally try to write exceptions as large as possible
- @Around(value = "cutPoint()") surround notification
@Aspect //Tag this is a faceted class
@Component //Inject into the container
public class Logs {
//Configuring global enhancements
@Pointcut("execution(public int com.myproject.service.Calculator.*(..))")
public void cutPoint() { }
/**
*
* @param joinPoint Gets information about the current enhancement method
* joinPoint.getArgs(); //Get enhanced method parameter list
* getSignature().getName(); //Gets the name of the method
*/
//Before advice
@Before("cutPoint()")
public static void beforeRun(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[logs]" + "method" + method + "Before advice " + Arrays.asList(args));
}
//Return notification
//returning can be used to receive return values
@AfterReturning(value = "cutPoint()",returning = "result")
public static void ReturnRun(JoinPoint joinPoint, Object result) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[logs]" +"method" + method + "Return notification" + Arrays.asList(args) + "The return value is" + result);
}
//Exception notification
//throwing is used to specify that only the current exception type will execute the exception notification method, so generally try to write exceptions as large as possible
@AfterThrowing(value = "cutPoint()", throwing = "exception")
public static void ExceptionRun(JoinPoint joinPoint, NullPointerException exception) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[logs]" + "method" + method + "Exception log notification" + Arrays.asList(args));
}
//Post notification
@After("cutPoint()")
public static void AfterRun(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[logs]" + "method" + method + "Post notification" + Arrays.asList(args));
}
//Around Advice
@Around(value = "cutPoint()")
public static Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//Method executes the return value
Object result = null;
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
try {
//Before advice
System.out.println("[logs surround]" + "method" + method + "Before advice " + Arrays.asList(args));
//The underlying layer actually calls the target method through reflection
result = joinPoint.proceed(args);
System.out.println("[logs surround]" + "method" + method + "Return notification" + Arrays.asList(args) + "The return value is" + result);
} catch (Exception e) {
System.out.println("[logs surround]" + "method" + method + "Exception notification" + Arrays.asList(args));
throw new RuntimeException();
} finally {
System.out.println("[logs surround]" + "method" + method + "Post notification" + Arrays.asList(args));
}
//Returns the result of the target method execution
return result;
}
}
@Aspect //Tag this is a faceted class
@Component //Inject into the container
public class Valid { //Enhancement of parameter validation
//Configuring global enhancements
@Pointcut("execution(public int com.myproject.service.Calculator.*(..))")
public void cutPoint() { }
/**
*
* @param joinPoint Gets information about the current enhancement method
* joinPoint.getArgs(); //Get enhanced method parameter list
* getSignature().getName(); //Gets the name of the method
*/
//Before advice
@Before("cutPoint()")
public static void beforeRun(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[Valid]" + "method" + method + "Before advice " + Arrays.asList(args));
}
//Return notification
//returning can be used to receive return values
@AfterReturning(value = "cutPoint()",returning = "result")
public static void ReturnRun(JoinPoint joinPoint, Object result) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[Valid]" +"method" + method + "Return notification" + Arrays.asList(args) + "The return value is" + result);
}
//Exception notification
//throwing is used to specify that only the current exception type will execute the exception notification method, so generally try to write exceptions as large as possible
@AfterThrowing(value = "cutPoint()", throwing = "exception")
public static void ExceptionRun(JoinPoint joinPoint, NullPointerException exception) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[Valid]" + "method" + method + "Exception log notification" + Arrays.asList(args));
}
//Post notification
@After("cutPoint()")
public static void AfterRun(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs(); //Get parameter list
String method = joinPoint.getSignature().getName(); //Gets the name of the method
System.out.println("[Valid]" + "method" + method + "Post direct notification" + Arrays.asList(args));
}
}
3. Tests and results
public class AopTest {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
@Test
public void test1() {
//Get facet class
Calculator calculator = context.getBean(Calculator.class);
//The calculator here is not a calculator type, but a proxy object. Only the proxy object can enhance the methods of the class
System.out.println(calculator.getClass());
int add = calculator.add(1, 2);
System.out.println(add);
}
}

4. xml configuration
<?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">
<bean id="log" class="com.myproject.xml.LogsXml"></bean>
<bean id="valid" class="com.myproject.xml.ValidXml"></bean>
<bean id="calculator" class="com.myproject.service.Calculator"></bean>
<aop:config>
<aop:pointcut id="point" expression="execution(public * com.myproject.service.*.*(..))"/>
<!-- Configure global pointcut expressions-->
<!-- order You can set the execution order of facet class methods. The smaller the value, the earlier the pre notification will be executed-->
<aop:aspect ref="log" order="2">
<!-- If the surround notification is configured before the ordinary notification, it will be executed first, otherwise it will be executed after the ordinary notification-->
<aop:around method="around" pointcut-ref="point"/>
<aop:before pointcut-ref="point" method="beforeRun"/>
<aop:after-returning method="returnRun" pointcut-ref="point" returning="result"/>
<aop:after-throwing method="exceptionRun" pointcut-ref="point" throwing="exception"/>
<aop:after method="afterRun" pointcut-ref="point" />
</aop:aspect>
<aop:aspect ref="valid" order="1">
<aop:before pointcut-ref="point" method="beforeRun"/>
<aop:after-returning method="returnRun" pointcut-ref="point" returning="result"/>
<aop:after-throwing method="exceptionRun" pointcut-ref="point" throwing="exception"/>
<aop:after method="afterRun" pointcut-ref="point" />
</aop:aspect>
</aop:config>
</beans>
3, Transaction enhancement
1. Configure transaction enhancement applicationcontext.xml
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Import external profile-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- Enable annotation package scanning-->
<context:component-scan base-package="com.myproject"/>
<!--Configure data sources-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="driverClass" value="${jdbc.driver}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--Operation database-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Configure transaction manager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--Enable annotation based transaction configuration-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2. The service layer needs to add transaction methods
- Annotation configuration transactions only need to mark @ Transactional on the method
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
/**
* Transactional Properties:
* Propagation propagation() default Propagation.REQUIRED; Propagation behavior of transactions
* Isolation isolation() default Isolation.DEFAULT; Isolation property of things
* int timeout() default -1; Set transaction timeout, int
* String timeoutString() default ""; Set transaction timeout, String
* boolean readOnly() default false; Set whether the transaction is a read-only transaction. You can optimize the transaction and speed up the query, but you can't add, delete or modify it
* Class<? extends Throwable>[] rollbackFor() default {}; Set which exceptions need to be rolled back, class array
* String[] rollbackForClassName() default {}; Set which exceptions need to be rolled back, String the full class name, and roll back the exceptions that were not rolled back
* Exception classification:
* (If an exception occurs during compilation, the system will not roll back by default
* (Default rollback in case of non inspected) runtime exception
* Class<? extends Throwable>[] noRollbackFor() default {}; Set which exceptions do not need to be rolled back, the class array, and the original rolled back exceptions do not need to be rolled back
* String[] noRollbackForClassName() default {}; Set which exceptions do not need to be rolled back. String the full class name
*/
@Transactional(propagation = Propagation.REQUIRED)
public void balanceSwap(int money) {
System.out.println("ccc To users ddd transfer accounts" + money);
accountDao.addBalance(money);
accountDao.subBalance(money);
}
}
@Service
public class CouterService {
@Autowired
private CouterDao couterDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update(int money) {
couterDao.update(money);
}
}
@Service
public class MuliService {
@Autowired
private AccountService accountService;
@Autowired
private CouterService couterService;
@Transactional
public void updateAll(int money) {
accountService.balanceSwap(money);//REQUIRED
couterService.update(money); //REQUIRES_NEW
int i = 1/0; //Manufacturing exception
}
}
- There are two methods in the above updateAll transaction, balanceSwap and update. Both methods add transactions;
- The transaction propagation attribute of balanceSwap is REQUIRED, so balanceSwap is directly added to the updateAll transaction for execution;
- The transaction propagation attribute of update is requirements_ New, update will restart another transaction execution;
- When an exception occurs, the update transaction can be successfully committed, and the balanceSwap transaction is rolled back;
- Tip: when adding a transaction to a method, you should use its proxy object to call it, otherwise the transaction will not take effect, such as the following:
@Service
public class AccountService2 {
@Autowired
private AccountDao accountDao;
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update1() {
accountDao.subBalance(10);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void update2() {
accountDao.addBalance(10);
}
@Transactional
public void update3() {
//If required according to the propagation behavior of the transaction_ New, even if exceptions are encountered, the two transactions are committed normally, but both transactions are rolled back here
update1();
update2();
int i = 1/0;
}
}
- This is because update3 calls update1 and update2 inside the AccountService2 class without using the proxy object of AccountService2. Therefore, update1 and update2 are simply called here, which has nothing to do with transactions, As we mentioned earlier, aop enhances methods by using proxy objects to call methods.
3. Transaction propagation behavior requirements_ New and REQUIRED instances
service() { //This method adds a transaction
//REQUIRED
a(){
//REQUIRES_NEW
b(){}
//REQUIRED
c(){}
}
//REQUIRES_NEW
d(){
do()
//REQUIRED
e(){
//REQUIRES_NEW
f(){
Exception2
}
}
//REQUIRES_NEW
g(){}
}
Exception1
}
- If an exception occurs in Exception1, Transactions B, D, e and G can be successfully committed; a. C transaction rollback
- If an exception occurs in Exception2, the transaction b can be successfully committed; a. C, F, g and e transactions are rolled back. To be exact, g will not be executed because the exception has occurred earlier. The do transaction is in requirements_ It can be submitted under new and rolled back under REQUIRED