Ideas of this paper
- How to implement dynamic proxy in jdk, cglib and spring.
- spring implements ProxyFactory details of dynamic agents.
- Understand the general idea of AOP implementation from the use of ProxyFactory.
What is an agent
- Provide a proxy for other objects to control access to this object, enhance a method in a class, and extend the program.
- There are three common implementation methods: jdk, cglib and ProxyFactory of spring.
How to create a dynamic proxy JDK
- The dynamic proxy of jdk must have an interface!
UserService target = new UserService();
// Proxy object for UserInterface interface
// The first parameter is the class loader, the second parameter is the interface of the agent, and the third parameter is the specific logic of the agent
Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[]{UserInterface.class}, new InvocationHandler() {
/**
* proxy: Proxy object
* method: Method of execution
* args: Method parameters
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
Object result = method.invoke(target, args);
System.out.println("after...");
return result;
}
});
UserInterface userService = (UserInterface) proxy;
userService.test();
How to create dynamic proxy cglib
UserService target = new UserService();
// Through cglib Technology
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
// Define additional logic, that is, proxy logic
enhancer.setCallbacks(new Callback[]{new MethodInterceptor() {
/**
* o:Proxy object
* method: Method of proxy object
* objects: Parameters of function call
* methodProxy: Proxy for method
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
Object result = methodProxy.invoke(target, objects);
System.out.println("after...");
return result;
}
}});
// UserService object created by dynamic agent
UserService userService = (UserService) enhancer.create();
// When the test method of this userService is executed, some additional logic will be executed
userService.test();
How to create a dynamic proxy: ProxyFactory of spring
// Get the original object
UserService target = new UserService();
// Create agent factory
ProxyFactory proxyFactory = new ProxyFactory();
// Specifies that the interface uses the dynamic proxy of jdk, and does not specify that the interface uses the dynamic proxy of cglib
// proxyFactory.setInterfaces(UserInterface.class);
// Set original object
proxyFactory.setTarget(target);
// Add an agent logic and various Advice
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
// Execute the proxy method
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
// Get the proxy object and execute
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
What is Advice
- Method before advice: the method is executed before. You can override the before method with new MethodBeforeAdvice.
- AfterReturningAdvice: the method is executed after return. You can override the afterReturning method with new AfterReturningAdvice.
- ThrowsAdvice: the method executes after throwing an exception. You can override the afterThrowing method with new ThrowsAdvice. The fourth parameter of afterThrowing is a specific exception, which will be matched.
- After (finally) advice: the method is executed after it is finally executed. This is the last, which is later than return.
- MethodInterceptor: surround execution. This is the most powerful Advice. You can customize the execution order. You can override the invoke method with the new MethodInterceptor.
Advisor: judgment is added on the basis of Advice!
- Advisor is composed of a Pointcut and an Advice.
- Pointcut allows you to specify the logic to be represented. For example, you can specify the method of that class to proxy.
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(new PointcutAdvisor() {
// Make logical matching
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("testAbc");
}
};
}
// Specific agent logic
@Override
public Advice getAdvice() {
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
// Useless methods. At present, there are only a few test method calls in the source code
@Override
public boolean isPerInstance() {
return false;
}
});
UserInterface userService = (UserInterface) proxyFactory.getProxy();
userService.test();
Make the object of dynamic proxy Bean: ProxyFactoryBean
/**
* Bean of specific agent
*/
@Bean
public ProxyFactoryBean userService(){
// Define original object
UserService userService = new UserService();
// Get ProxyFactoryBean object
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
// Sets the object to proxy
proxyFactoryBean.setTarget(userService);
// Set which BeanName proxy logic to execute
proxyFactoryBean.setInterceptorNames("zhangweiAroundAdvise");
// Return proxy object
return proxyFactoryBean;
}
/**
* Agent logic
*/
@Bean
public MethodInterceptor zhangweiAroundAdvise(){
return new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
};
}
Solve the problem that beans do not need to specify their own proxy logic and reduce the coupling degree: BeanNameAutoProxyCreator
/**
* BeanNameAutoProxyCreator It is a BeanPostProcessor, which will execute before instantiation!
*/
@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
// All beannames beginning with userSe will execute the proxy logic of Zhangwei around advise
beanNameAutoProxyCreator.setBeanNames("userSe*");
beanNameAutoProxyCreator.setInterceptorNames("zhangweiAroundAdvise");
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
Solve the problem that BeanNameAutoProxyCreator can only match according to BeanName: defaultadvisor autoproxycreator that can match methods
/**
* An Advisor defined.
* The current logic indicates that as long as the method is test, the ZhangweiAfterReturningAdvise agent will be executed
*/
@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.addMethodName("test");
DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
defaultPointcutAdvisor.setPointcut(pointcut);
defaultPointcutAdvisor.setAdvice(new ZhangweiAfterReturningAdvise());
return defaultPointcutAdvisor;
}
/**
* DefaultAdvisorAutoProxyCreator It is a BeanPostProcessor, which will be executed during initialization!
* He will look for which beans in the spring container are of type Advisor. Match whether the names in the container match
* The code of this Bean can be written as @ import (defaultadvisor autoproxycreator. Class)
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
return defaultAdvisorAutoProxyCreator;
}
Simplify again: @ Aspect
@Aspect
@Component
public class ZhangweiAspect {
@Before("execution(public void com.zhangwei.service.UserService.test())")
public void zhangweiBefore(JoinPoint joinPoint) {
System.out.println("zhangweiBefore");
}
}
Concluding remarks
- Get more pre knowledge articles of this article and new valuable articles. Let's become architects together!
- Paying attention to the official account enables you to have an in-depth understanding of MySQL, concurrent programming and spring source code.
- Pay attention to the official account and follow the continuous and efficient learning of JVM!
- This official account is not advertising!!! Update daily!!!