Aop related reading
Before reading this article, you need to master the contents of the following three articles, otherwise it will be difficult.
-
Spring Series Part 15: detailed explanation of proxy (java Dynamic Proxy & cglib proxy)
-
Spring series Chapter 32: AOP core source code and principle explanation
This article continues Aop.
There are two main ways in which AOP creates agents
Manual mode
Also known as manual method, you need to hard code to create agents one by one.
Automated way
Also known as batch method, batch method is used in spring environment to create proxies for qualified beans through bean post processor
The manual mode is basically hard coded, which is relatively more flexible and can be used away from the spring environment, while the automatic mode is mainly used in the spring environment, which is easier and more powerful to integrate with spring.
AOP creates proxy related classes
The proxycreator support on the left and the following are manual methods. There are three classes.
Under the AbstractAutoProxyCreator on the right is the method of automatically creating agents, mainly with five implementation classes.
Manual 3 modes
ProxyFactory mode
This method is hard coded and can be used directly without spring. It is widely used. The creation of agents in the automatic method depends on ProxyFactory. Therefore, we must understand the principle of this method. It has been introduced in the previous article. If you are not clear, you can take a look at it: Spring series Chapter 32: AOP core source code and principle explanation
AspectJProxyFactory mode
AspectJ is an aspect oriented framework. It is the best and most convenient AOP framework at present. Spring integrates it. It is very convenient to realize AOP proxy through some functions provided by AspectJ. The next article will explain it in detail.
ProxyFactoryBean mode
This article mainly introduces a way to create a proxy for a specified bean in the Spring environment.
ProxyFactoryBean
This class implements an interface FactoryBean. If the FactoryBean is unclear, you can see: Spring Series Part 5: how do you know about creating bean instances?
ProxyFactoryBean creates a proxy object for the specified bean through FactoryBean.
To create an agent, there are three key messages:
-
Functions that need to be enhanced are implemented in Advice
-
target: indicates which object you need to enhance
-
Proxy object: a proxy object formed by combining the enhanced functions with the target object. The target object is accessed through the proxy object to enhance the target object.
Using ProxyFactoryBean also revolves around three parts. The steps used by ProxyFactoryBean are as follows:
1.establish ProxyFactoryBean object
2.adopt ProxyFactoryBean.setTargetName Sets the of the target object bean Name, target object is spring One of the containers bean
3.adopt ProxyFactoryBean. setInterceptorNames Add notifications that need to be enhanced
4.take ProxyFactoryBean Register to Spring Container, assuming the name is proxyBean
5.from Spring Find name is proxyBean of bean,this bean Is to generate a good proxy object
Last case.
Class Service1
package com.javacode2018.aop.demo8.test1;
public class Service1 {
public void m1() {
System.out.println("I am m1 method");
}
public void m2() {
System.out.println("I am m2 method");
}
}
demand
Register the bean of the above class in the spring container with the name of service1. The bean is enhanced through proxy to receive two notifications
A pre notification: before calling any method in service1, output a message: ready to call xxxx method
A wrap around notification: copy counts the time-consuming of all methods.
The following is the implementation of the code
package com.javacode2018.aop.demo8.test1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
@Configuration
public class MainConfig1 {
//Register target object
@Bean
public Service1 service1() {
return new Service1();
}
//Register a pre notification
@Bean
public MethodBeforeAdvice beforeAdvice() {
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("Ready to call:" + method);
}
};
return advice;
}
//Register a post notification
@Bean
public MethodInterceptor costTimeInterceptor() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + ",time consuming(nanosecond): " + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//Register ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1. Create ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2. Set the bean name of the target object
proxyFactoryBean.setTargetName("service1");
//3. Set the bean name list of the interceptor. Here are 2 (advice1 and advice2)
proxyFactoryBean.setInterceptorNames("beforeAdvice", "costTimeInterceptor");
return proxyFactoryBean;
}
}
Now start the spring container and get the proxy object
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig1.class);
//Get the proxy object. The name of the proxy object bean is the name of the registered ProxyFactoryBean, that is, service1Proxy
Service1 bean = context.getBean("service1Proxy", Service1.class);
System.out.println("----------------------");
//Method of calling proxy
bean.m1();
System.out.println("----------------------");
//Method of calling proxy
bean.m2();
Run output
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m1()
I am m1 method
public void com.javacode2018.aop.demo8.test1.Service1.m1(),time consuming(nanosecond): 8680400
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m2()
I am m2 method
public void com.javacode2018.aop.demo8.test1.Service1.m2(),time consuming(nanosecond): 82400
As you can see from the output, the target object service1 has been enhanced.
interceptorNames in ProxyFactoryBean
interceptorNames is used to specify the bean name list of interceptors. There are two common methods.
-
Batch mode
-
Non batch mode
Batch mode
usage method
proxyFactoryBean.setInterceptorNames("Need matching bean name*");
The bean names that need to be matched are followed by a *, which can be used for batch matching, such as interceptor *. At this time, spring will find all beans of the following 2 types from the container, and those whose bean names start with interceptor will be used as enhancers
org.springframework.aop.Advisor
org.aopalliance.intercept.Interceptor
When using this place, it should be noted that when registering in batch mode, if the type of enhancer is not the above two types, such as the following three types of notifications, we need to package it as Advisor, and MethodInterceptor is of Interceptor type, so we don't need to package it as Advisor type.
MethodBeforeAdvice(Method (pre notification)
AfterReturningAdvice(Method (post notification)
ThrowsAdvice(Exception notification)
Let's take a case and feel it.
case
Two enhancers are registered in batch below.
package com.javacode2018.aop.demo8.test2;
import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class MainConfig2 {
//Register target object
@Bean
public Service1 service1() {
return new Service1();
}
//Define an intensifier: interceptor1, which is a pre notification inside and needs to be packaged as an Advisor type
@Bean
public Advisor interceptor1() {
MethodBeforeAdvice advice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("Ready to call:" + method);
}
};
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setAdvice(advice);
return advisor;
}
//Define an intensifier: interceptor2
@Bean
public MethodInterceptor interceptor2() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + ",time consuming(nanosecond): " + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//Register ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1. Create ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2. Set the bean name of the target object
proxyFactoryBean.setTargetName("service1");
//3. Set the bean name list of the interceptor. Batch register here
proxyFactoryBean.setInterceptorNames("interceptor*"); //@1
return proxyFactoryBean;
}
}
Two intensifiers are defined above:
interceptor1: pre notification. The package is of Advisor type
interceptor2: surround notification, of type MethodInterceptor
Test code
@Test
public void test2() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
//Get the proxy object. The name of the proxy object bean is the name of the registered ProxyFactoryBean, that is, service1Proxy
Service1 bean = context.getBean("service1Proxy", Service1.class);
System.out.println("----------------------");
//Method of calling proxy
bean.m1();
System.out.println("----------------------");
//Method of calling proxy
bean.m2();
}
Run output
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m1()
I am m1 method
public void com.javacode2018.aop.demo8.test1.Service1.m1(),time consuming(nanosecond): 10326200
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m2()
I am m2 method
public void com.javacode2018.aop.demo8.test1.Service1.m2(),time consuming(nanosecond): 52000
Non batch mode
usage
In the non batch mode, multiple enhancers need to be registered, and the bean names of multiple enhancers need to be clearly specified. Multiple enhancers are executed in the order specified in the parameters, such as
proxyFactoryBean.setInterceptorNames("advice1","advice2");
The bean types corresponding to advice1 and advice2 must be the types specified in the following list. The type range is wider than the matching method
MethodBeforeAdvice(Method (pre notification)
AfterReturningAdvice(Method (post notification)
ThrowsAdvice(Exception notification)
org.aopalliance.intercept.MethodInterceptor((surround notification)
org.springframework.aop.Advisor((consultant)
Let's take a case.
case
This time, three notifications are given to service1: pre, surround and post
package com.javacode2018.aop.demo8.test3;
import com.javacode2018.aop.demo8.test1.Service1;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
public class MainConfig3 {
//Register target object
@Bean
public Service1 service1() {
return new Service1();
}
//Define a pre notification
@Bean
public MethodBeforeAdvice methodBeforeAdvice() {
MethodBeforeAdvice methodBeforeAdvice = new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println("Ready to call:" + method);
}
};
return methodBeforeAdvice;
}
//Define a surround notification
@Bean
public MethodInterceptor methodInterceptor() {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long starTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();
System.out.println(invocation.getMethod() + ",time consuming(nanosecond): " + (endTime - starTime));
return result;
}
};
return methodInterceptor;
}
//Define a post notification
@Bean
public AfterReturningAdvice afterReturningAdvice() {
AfterReturningAdvice afterReturningAdvice = new AfterReturningAdvice() {
@Override
public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
System.out.println(method + ",completion of enforcement!");
}
};
return afterReturningAdvice;
}
//Register ProxyFactoryBean
@Bean
public ProxyFactoryBean service1Proxy() {
//1. Create ProxyFactoryBean
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
//2. Set the bean name of the target object
proxyFactoryBean.setTargetName("service1");
//3. Set the bean name list of the interceptor. Batch register here
proxyFactoryBean.setInterceptorNames("methodBeforeAdvice", "methodInterceptor", "afterReturningAdvice");
return proxyFactoryBean;
}
}
Test code
@Test
public void test3() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
//Get the proxy object. The name of the proxy object bean is the name of the registered ProxyFactoryBean, that is, service1Proxy
Service1 bean = context.getBean("service1Proxy", Service1.class);
System.out.println("----------------------");
//Method of calling proxy
bean.m1();
System.out.println("----------------------");
//Method of calling proxy
bean.m2();
}
Run output
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m1()
I am m1 method
public void com.javacode2018.aop.demo8.test1.Service1.m1(),completion of enforcement!
public void com.javacode2018.aop.demo8.test1.Service1.m1(),time consuming(nanosecond): 12724100
----------------------
Ready to call:public void com.javacode2018.aop.demo8.test1.Service1.m2()
I am m2 method
public void com.javacode2018.aop.demo8.test1.Service1.m2(),completion of enforcement!
public void com.javacode2018.aop.demo8.test1.Service1.m2(),time consuming(nanosecond): 76700
Source code analysis
The focus is on the following method
org.springframework.aop.framework.ProxyFactoryBean#getObject
Source code:
public Object getObject() throws BeansException {
//Initialize the advisor chain
initializeAdvisorChain();
//Is it a single instance
if (isSingleton()) {
//Create a singleton proxy object
return getSingletonInstance();
}
else {
//Create multiple proxy objects
return newPrototypeInstance();
}
}
The initializeAdvisorChain method is used to initialize the advisor (interceptor) chain. According to the interceptorNames configuration, find the qualified interceptors in the spring container and put them into the configuration for creating aop agents
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (!ObjectUtils.isEmpty(this.interceptorNames)) {
// polling interceptorNames
for (String name : this.interceptorNames) {
//Batch registration method: judge whether name ends with *
if (name.endsWith(GLOBAL_SUFFIX)) {
//@1: Find a matching enhancer from the container and add it to the aop configuration
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}
else {
//Non matching method: find the bean by name, wrap it as Advisor and throw it into the aop configuration
Object advice;
//Find bean s from container
advice = this.beanFactory.getBean(name);
//@2: Add advice to the interceptor list
addAdvisorOnChainCreation(advice, name);
}
}
}
}
@1: addGlobalAdvisor batch mode. Take a look at the source code. It's relatively simple
/**
* Add all global interceptors and pointcuts,
* For all beans of type Advisor/Interceptor in the container, the bean name beginning with prefix will be added to the interceptor chain
*/
private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
//Get all bean s of type Advisor in the container
String[] globalAdvisorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
//Gets all bean s of type Interceptor in the container
String[] globalInterceptorNames =
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
List<Object> beans = new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
Map<Object, String> names = new HashMap<>(beans.size());
for (String name : globalAdvisorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
for (String name : globalInterceptorNames) {
Object bean = beanFactory.getBean(name);
beans.add(bean);
names.put(bean, name);
}
//To sort beans, you can implement the Ordered interface. The sorting rule is order asc
AnnotationAwareOrderComparator.sort(beans);
for (Object bean : beans) {
String name = names.get(bean);
//Determine whether the bean starts with prefix
if (name.startsWith(prefix)) {
//Add it to the interceptor chain
addAdvisorOnChainCreation(bean, name);
}
}
}
@2: addAdvisorOnChainCreation
private void addAdvisorOnChainCreation(Object next, String name) {
//namedBeanToAdvisor is used to convert a bean to an advisor
Advisor advisor = namedBeanToAdvisor(next);
//Add advisor to interceptor chain
addAdvisor(advisor);
}
namedBeanToAdvisor method
private AdvisorAdapterRegistry advisorAdapterRegistry = new DefaultAdvisorAdapterRegistry();
private Advisor namedBeanToAdvisor(Object next) {
//Wrap objects as Advisor objects
return this.advisorAdapterRegistry.wrap(next);
}
For those unclear about advisor adapter registry, please refer to the previous article: Spring series Chapter 32: AOP core source code and principle explanation
The advisorAdapterRegistry#wrap method wraps the adviceObject as an Advisor object. The code is as follows, which is relatively simple
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
Advice advice = (Advice) adviceObject;
if (advice instanceof MethodInterceptor) {
return new DefaultPointcutAdvisor(advice);
}
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
summary
-
There are two main ways to create proxies in Spring: manual and automatic
-
The manual method is hard coded. You can only create proxy objects for one target object at a time. It is relatively flexible and more flexible for developers. It can usually be used independently of the spring environment; The automation method is mainly used in the spring environment. It is usually a matching method to create proxies for qualified target bean s, which is easier to use
-
The ProxyFactoryBean introduced in this article is used to create proxy objects for specified beans in the spring environment. It is not used too much. You can learn about it
Case source code
https://gitee.com/javacode2018/spring-series
All the case codes of passerby a java will be put on this in the future. Let's watch it and continue to pay attention to the dynamics.
Source: https://mp.weixin.qq.com/s?__ biz=MzA5MTkxMDQ4MQ==&mid=2648934977&idx=1&sn=8e4caf6a17bf5e123884df81a6382214&chksm=8862127fbf159b699c4456afe35a17f0d7bed119a635b11c154751dd95f59917487c895ccb84&scene=21#wechat_ redirect