preface
Why aspect oriented programming (AOP)? We know that Java is an object-oriented (OOP) language, but it has some drawbacks, such as when we need to introduce a common behavior for multiple objects that do not have an inheritance relationship.
Such as logging, permission verification, transactions, and other functions, can only reference the common behavior in each object, which is not easy to maintain, and there is a lot of repeated code, AOP has made up for OOP this deficiency.
To clarify Spring AOP, let’s discuss it from the following aspects:
- Proxy mode.
- Principle and practice of static proxy.
- Dynamic proxy principle and practice.
- Principle and practice of Spring AOP.
The proxy pattern
Proxy mode: Provides a proxy for other objects to control access to that object.
This paragraph is quite official, but I prefer to use my own language to understand it: for example, if object A wants to do something, it will do it by itself before there is no proxy, and after A is the proxy class B of A will do it.
Proxying is essentially adding a layer of processing before and after the original instance, which is AOP’s primary contours.
Principle and practice of static proxy
Static proxy mode: Static proxy means that the bytecode file of the proxy class exists before the program runs, and the relationship between the proxy class and the original class is determined before the program runs.
Without further discussion, let’s take a look at the code. To make it easier to read, the blogger merges a separate class file into the interface, and readers can directly copy the code to run:
package test.staticProxy;
/ / interface
public interface IUserDao {
void save();
void find();
}
// Target object
class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("Simulation: Save the user!");
}
@Override
public void find() {
System.out.println("Simulation: Query user"); }}/** Static proxy features: 1. The target object must implement interface 2. The proxy object implements the same interface */ as the target object
class UserDaoProxy implements IUserDao{
// Proxy object, need to maintain a target object
private IUserDao target = new UserDao();
@Override
public void save() {
System.out.println("Agent operation: Start transaction...");
target.save(); // Execute the target object's method
System.out.println("Agent action: Commit transaction..."); } @Override public void find() { target.find(); }}Copy the code
Test results:
A static proxy ensures that the business class only needs to focus on the logic itself, and that an interface of the proxy object serves only one type of object. If there are many methods to be propped, it is necessary to propped for each method.
Furthermore, if you add a method, all proxy classes need to implement the method in addition to the implementation class. Increased code maintenance costs. So how to solve it? The answer is to use dynamic proxies.
Dynamic proxy principle and practice.
Dynamic proxy mode: The source code for dynamic proxy classes is generated dynamically during program execution through mechanisms such as JVM reflection, and the relationship between proxy classes and delegate classes is determined at runtime. Examples are as follows:
package test.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/ / interface
public interface IUserDao {
void save();
void find();
}
// Target object
class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("Simulation: Save the user!");
}
@Override
public void find() {
System.out.println("Query"); }}/** * dynamic proxy: * proxy factory, generate proxy objects for multiple target objects! * * /
class ProxyFactory {
// Receive a target object
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// Return the target object after the proxy.Public Object getProxyInstance() {Object proxy = proxy.newproxyInstance (target.getClass().getClassLoader(),// The class loader used by the target objectTarget. GetClass (). GetInterfaces (),// All interfaces implemented by the target object
new InvocationHandler() { // When the proxy object method is executed
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// Gets the method name of the currently executing method
String methodName = method.getName();
// The method returns a value
Object result = null;
if ("find".equals(methodName)) {
// Call the target object method directlyResult = method.invoke(target, args); }else {
System.out.println("Open transaction...");
// Execute the target object methodResult = method.invoke(target, args); System.out.println("Commit transaction...");
}
returnresult; }});returnproxy; }}Copy the code
The test results are as follows:
In the code that creates test class objects in the run test class
IUserDao proxy = (IUserDao)new ProxyFactory(target).getProxyInstance();
Copy the code
Instead, the JDK dynamically generates a class to implement the interface, hiding the process:
class $jdkProxy implements IUserDao{}
Copy the code
The premise of using dynamic proxies generated by the JDK is that the target class must have an interface to implement. The Cglib proxy is the solution to the problem that JDK dynamic proxies cannot be used if a class does not implement an interface.
Cglib is implemented with a dynamically generated subclass inheritance goal, dynamically building a subclass in memory at runtime as follows:
public class UserDao{}
//Cglib is implemented as a dynamically generated subclass inheritance target. The following process is hidden when the program executes
public class $Cglib_Proxy_class extends UserDao{}
Copy the code
Cglib is used on the premise that the target class cannot be final. Because final modified classes cannot be inherited.
Now let’s look at the definition of AOP: section-oriented programming, the core principle of which is to use the dynamic proxy pattern to add logic before and after method execution or when exceptions occur.
From the definition and the previous code we can see three things:
- AOP is based on the dynamic proxy pattern.
- AOP is method level.
- AOP can separate business code from concern code (duplicate code) and inject concern code dynamically as business code is executed. The aspect is the class formed by the code of concern.
Principle and practice of Spring AOP
As mentioned above, the JDK proxy and the Cglib proxy are two dynamic proxies. The excellent Spring framework integrates both approaches in the bottom layer, so we don’t need to worry about implementing dynamic proxies ourselves. So how does Spring generate proxy objects?
1. When creating container objects, generate proxy objects based on the classes intercepted by pointcut expressions.
2. If the target object has an implementation interface, use a JDK proxy. If the target object does not implement an interface, the Cglib proxy is used. It then gets the proxied object from the container and implants the methods of the “aspect” class at run time. In the DefaultAopProxyFactory class, we can find this statement in the Spring source code.
The simple fact is, if you have an interface, use a Jdk proxy, and if you don’t, use Cglib, which is exactly what I said earlier. If the target class does not implement an interface, and the class is final, then Spring AOP cannot be programmed!
With that in mind, we’ll now implement Spring AOP manually ourselves:
package test.spring_aop_anno;
import org.aspectj.lang.ProceedingJoinPoint;
public interface IUserDao {
void save();
}
// Test the Cglib dynamic proxy
class OrderDao {
public void save() {
//int i =1/0; Used to test exception notifications
System.out.println("Save order..."); }}// Used to test JDK dynamic proxies
class UserDao implements IUserDao {
public void save() {
//int i =1/0; Used to test exception notifications
System.out.println("Save user..."); }}/ / cut class
class TransactionAop {
public void beginTransaction() {
System.out.println("[Pre-notification] Start transaction..");
}
public void commit() {
System.out.println("[Post notification] Submission transaction..");
}
public void afterReturing(){
System.out.println("[Notification upon return]");
}
public void afterThrowing(){
System.out.println("[Exception Notification]");
}
public void arroud(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("[Around front:]");
pjp.proceed(); // Execute the target method
System.out.println([After wrapping:]"); }}Copy the code
XML configuration file for Spring:
<? 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"> <! -- Add dao instance to container --> <bean id="userDao" class="test.spring_aop_anno.UserDao"></bean> <! -- Add dao instance to container --> <bean id="orderDao" class="test.spring_aop_anno.OrderDao"></bean> <! -- Instantiate the aspect class --> <bean id="transactionAop" class="test.spring_aop_anno.TransactionAop"></bean> <! --> < Aop :config> <! Aop :pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..) )" id="transactionPointcut"/ > <! < AOP :aspect ref="transactionAop"> <! --> < AOP :around method="arroud" pointcut-ref="transactionPointcut"/ > <! < AOP :before method="beginTransaction" pointcut-ref="transactionPointcut"/ > <! --> <aop:after method="commit" pointcut-ref="transactionPointcut"/ > <! --> < AOP: returning method="afterReturing" pointcut-ref="transactionPointcut"/ > <! Aop :after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
</aop:aspect>
</aop:config>
</beans>
Copy the code
Pointcut expressions are not covered here. The code test results are as follows:
Now that we’ve covered Spring AOP, we return to our opening question: What do we do with it?
- Spring declarative transaction management configuration
- Parameter verification of the Controller layer
- Use Spring AOP to implement MySQL database read and write separation case analysis
- Before executing a method, determine whether permissions are available.
- Log calls to some functions. Monitor some important functions. If a specified exception is thrown, relevant personnel can be notified by SMS or email.
- Information filtering, page forwarding and other functions, a person’s power is limited, can only list so many, welcome to the comment area to make supplements to the article.
What can Spring AOP do, what magic function to achieve, is that we each ordinary and wise ape program!
From: my.oschina.net/liughDevelo…
Let Friends Know You’re “Watching”