Introduction of proxy pattern and AOP implementation of Spring
Agent model of AOP antecedents
Static proxy
Role analysis:
- Abstract role: it is usually solved by interface or abstract class
- Real role: the role represented
- Agent role: acting as a real role. After acting as a real role, we usually do some related business processing
- Client: the person who accesses the proxy object
Simulate static proxy
The existing service business is adding, deleting, modifying and querying
Service interface
package com.lwh.service; public interface Service { public void add(); public void delete(); public void update(); public void query(); }
service interface implementation class
package com.lwh.service; public class ServiceImpl implements Service{ @Override public void add() { System.out.println("Increase method"); } @Override public void delete() { System.out.println("Delete method"); } @Override public void update() { System.out.println("Modification method"); } @Override public void query() { System.out.println("Query method"); } }
client
package com.lwh.service; public class Client { public static void main(String[] args) { ServiceImpl service=new ServiceImpl(); service.add(); } }
Now I need to implement other functions. I need to add some other services and print the log before each method execution, but I can't modify the code. In the company, modifying the code is taboo, so I need to use the agent mode
We only need to add a new proxy class, ProxyServletImpl
package com.lwh.service; public class ProxyServletImpl implements Service{ private ServiceImpl service; public ServiceImpl getService() { return service; } public void setService(ServiceImpl service) { this.service = service; } @Override public void add() { log("add"); service.add(); } @Override public void delete() { log("delete"); service.delete(); } @Override public void update() { log("update"); service.update(); } @Override public void query() { log("query"); service.query(); } public void log(String asg) { System.out.println("Used"+asg+"method"); } }
We didn't change the original code. We used a new object to implement our own methods in our proxy class and the original service logic to successfully implement the required functions. This is the advantage of the proxy.
Dynamic agent
- Dynamic agents have the same role as static agents
- The proxy class of dynamic proxy is generated dynamically, which is not written directly
- Dynamic agents can be divided into two categories:
- Interface based dynamic agent, such as JDK dynamic agent
- Class based dynamic proxy, for example: cglib
Implementation of simulated dynamic agent based on AOP
Write dynamic proxy tool class
-
InvocationHandler interface is an interface implemented by the calling handler of proxy proxy instance. Each proxy instance has an associated calling handler; When a method is called by a proxy instance, the method call is encoded and dispatched to the invoke method of the calling handler.
-
The calling handler of each dynamic proxy class must implement the InvocationHandler interface, and the instance of each proxy class is associated with the dynamic proxy class calling handler that implements the interface. When we call a method through the dynamic proxy object, the call of this method will be forwarded to the invoke method that implements the InvocationHandler interface class
-
Proxy class is a class used to create a proxy object. It provides many methods, but the most commonly used method is newProxyInstance, which has the following parameters
- loader: a classloader object that defines which classloader object loads the generated proxy class
- Interfaces: an array of interface objects, which indicates what kind of interfaces we will provide to our proxy object. If we provide such an array of interface objects, we declare that the proxy class implements these interfaces, and the proxy class can call all the methods declared in the interface.
- h: An InvocationHandler object indicates which InvocationHandler object will be associated with when the method is called by the dynamic proxy object, and finally called by it.
package com.lwh.demo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { //Real object represented private Object target; //For the sake of generality, set method is used to realize the injection of real objects public void setTarget(Object target) { this.target = target; } //Get proxy implementation class public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName());//The name of the method passed in for execution Object result=method.invoke(target, args); return result; } //Other business logic added public void log(String msg){ System.out.println("Yes"+msg+"method"); } }
Client client
package com.lwh.demo; import com.lwh.service.Service; import com.lwh.service.ServiceImpl; public class Client { public static void main(String[] args) { ServiceImpl service=new ServiceImpl(); ProxyInvocationHandler proxyInvocationHandler=new ProxyInvocationHandler(); proxyInvocationHandler.setTarget(service); Service proxy = (Service) proxyInvocationHandler.getProxy(); proxy.add();//output The add method is executed } }
We can understand that in the process of running the client, we pass in ServiceImpl and automatically generate the dynamic proxy class. The dynamic proxy class automatically implements the inheritance interface of the real class we pass in, so that we can use all the methods defined in the interface. When we want to use the dynamic proxy class, we will call the invoke method of proxyInvocationHandler, Execute the code logic we added to realize business expansion!
Dynamic agent benefits:
- A dynamic proxy class represents an interface, which is generally a corresponding type of business (because what is passed in is a real object)
- A dynamic proxy class can proxy multiple classes as long as it implements the same interface. Because it is a dynamically generated class, multiple classes can be generated.
AOP
What is AOP
AOP (Aspect Oriented Programming) means: aspect oriented programming, which realizes the unified maintenance of program functions through precompiled mode and runtime dynamic agent. 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.
AOP implementation of Spring
First explain some official terms:
-
Crosscutting concerns: methods or functions that span multiple modules of an application, that is, those that have nothing to do with our business logic, but that we need to focus on are crosscutting concerns, such as logging, security, caching, transactions, and so on
-
Aspect: a special object whose crosscutting concerns are modularized. It is a class
-
Notification: the work that must be done by the aspect, that is, a method in the class
-
Target: notified object
-
Proxy: objects created after notification is applied to the target object
-
Entry point: the definition of the place where the aspect notification is executed
-
Join point: the execution point that matches the pointcut
Create a sub project and add maven:
<dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> </dependencies>
Modify beans 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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> </beans>
Method 1: use the API provided by Spring
- Write the Service interface first
package com.lwh.service; public interface Service{ public void add(); public void delete(); public void update(); public void query(); }
- Then write the real object
package com.lwh.service; public class ServiceImpl implements Service{ @Override public void add() { System.out.println("The addition method is used"); } @Override public void delete() { System.out.println("Deletion method used"); } @Override public void update() { System.out.println("Modification method used"); } @Override public void query() { System.out.println("Query method used"); } }
- Write Log class
You need to inherit the MethodBeforeAdvice interface before executing the method
package com.lwh.log; import org.springframework.aop.BeforeAdvice; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class BeforeLog implements MethodBeforeAdvice { /** *After MethodBeforeAdvice is inherited, the logic written in the before method will be called before the real object is executed. * @param method:Method representing the target object to execute * @param objects: Represents a parameter * @param o: Represents the target object * @throws Throwable */ @Override public void before(Method method, Object[] objects, Object o) throws Throwable { System.out.println(o.getClass().getName()+"of"+method.getName()+"Executed"); } }
If the operation is performed after the method is executed, and the operation returns a value, it needs to inherit the AfterReturningAdvice interface
package com.lwh.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { /** * AfterReturningAdvice It will be called after return, and it can get the return value of the real object. * @param returnValue * @param method * @param args * @param target * @throws Throwable */ @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("Yes"+method.getName()+"The returned result is"+returnValue); } }
- Register beans 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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd"> <bean id="userService" class="com.lwh.service.ServiceImpl"/> <bean id="blog" class="com.lwh.log.BeforeLog"/> <bean id="alog" class="com.lwh.log.AfterLog"/> <aop:config> <!--The entry point is where we need to execute,execution(Parameters to execute)--> <!--com.lwh.service.ServiceImpl This sentence represents ServiceImpl All methods under are proxied;*(..)Represents that any parameter can be passed in--> <aop:pointcut id="pointcut" expression="execution(* com.lwh.service.ServiceImpl.*(..))"/> <!--Execute surround increase,This is to cut the class configured below into the path configured above--> <aop:advisor advice-ref="alog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="blog" pointcut-ref="pointcut"/> </aop:config> </beans>
- Writing test classes
import com.lwh.service.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //The dynamic proxy is the interface. Just declare the interface class directly and accept it Service service=context.getBean("userService",Service.class); service.add(); /** * Output: * com.lwh.service.ServiceImpl add of is executed * The addition method is used * add is executed, and the returned result is null */ } }
Mode 2: implement AOP (section definition) by customization
- Write custom cut
package com.lwh.log; public class MyAspect { public void beforeMethod(){ System.out.println("========Before method execution========="); } public void afterMethod(){ System.out.println("========After method execution========="); } }
- Modify beans xml
<bean id="myaspect" class="com.lwh.log.MyAspect"/> <aop:config> <aop:aspect ref="myaspect"> <!--breakthrough point--> <aop:pointcut id="point" expression="execution(* com.lwh.service.ServiceImpl.*(..))"/> <!--notice--> <aop:before method="beforeMethod" pointcut-ref="point"/> <aop:after method="afterMethod" pointcut-ref="point"/> </aop:aspect> </aop:config>
- Test class
import com.lwh.service.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //The dynamic proxy is the interface. Just declare the interface class directly and accept it Service service=context.getBean("userService",Service.class); service.add(); /** * Output: * ========Before method execution========= * The addition method is used * ========After method execution========= */ } }
Mode 3: implement with annotation
-
Add a method in the Service interface
public String print();
-
Add the implementation of real object class
@Override public String print() { return "Print text"; }
-
Add proxy class annotationpointcut java
- Now annotate Aspect on the class name, indicating that this is an Aspect
- There are three notes for those with more land
- @Before means that before the method is executed, there are parameters inside to represent the pointcut. Like xml configuration, you need to set the value with the execution expression first
- @After means after the method is executed and the returned value, which can be understood as the last
- @Around stands for surround. Like others, you need to set a pointcut. You need to pass in a parameter proceed ingjoinpoint, which is automatically generated by Spring. There is a processed method in it to represent the return value after the method is executed. We use an Object to accept it and then print the results
Think: what is the output order?
package com.lwh.log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect //Mark this class is a section public class AnnotationPointCut { @Before("execution(* com.lwh.service.ServiceImpl.*(..))") public void before(){ System.out.println("===========Before method execution============"); } @After("execution(* com.lwh.service.ServiceImpl.*(..))") public void after(){ System.out.println("==========After method execution============="); } @Around("execution(* com.lwh.service.ServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("Surround front"); Object proceed = jp.proceed(); System.out.println(proceed); System.out.println("After surround"); } }
-
Test class
import com.lwh.service.Service; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //The dynamic proxy is the interface. Just declare the interface class directly and accept it Service service=context.getBean("userService",Service.class); service.print(); /**Output: * Surround front * ===========Before method execution============ * Print text * After surround * ==========After method execution============= */ } }