Spring Aop (11) - ProxyFactory for programmatically creating Aop proxy

Programmatically create ProxyFactory for Aop proxies

Spring Aop is proxy-based, and ProxyFactory is a factory class used internally by Spring Aop to create Proxy objects. If we need to dynamically apply Spring Aop at runtime, we can consider using ProxyFactory. When using ProxyFactory, we need to specify the target object we need to proxy for it, and the Advisor or Advice we need to use when proxying. The following example is a simple proxy that uses ProxyFactory to create a MyService object, and applies a MethodBeforeAdvice to it, that is, every time the method of the proxy object is called, the before method of MethodBeforeAdvice will be called first.

	@Test
	public void testProxyFactory() {
		MyService myService = new MyService();
		ProxyFactory proxyFactory = new ProxyFactory(myService);
		proxyFactory.addAdvice(new MethodBeforeAdvice() {

			@Override 
			public  void  before ( Method  method , Object [] args ,
                             Object  target ) throws  Throwable {
				 System . out . println( " Execute the logic before the target method call " );
				 // No need to manually call the target method,
                                //The target method will be called in Spring's built-in logic
			}
			
		});;
		MyService proxy = (MyService) proxyFactory.getProxy();
		proxy.add();
	}

Specifies the proxied object

ProxyFactory has multiple overloaded constructors. In the above example, the author uses the constructor that specifies the proxied object. If we apply other constructors, we can specify the proxied object through the setTarget(Object) method of ProxyFactory. If we do not specify the Class of the proxied object, the proxy object created by default is the type of the proxied object we pass, that is, the type of targetObject.getClass() is obtained. If the type of our proxied object contains multiple interface implementations or parent types, and we only want to proxy one of these types, we can specify which Class the created proxy object is based on through setTargetClass(Class) of ProxyFactory of. By default, ProxyFactory will choose whether the created proxy object is based on JDK proxy or based on CBLIB proxy according to the actual situation, that is, when the target object has interface implementation and is not set proxyTargetClass="true"or the specified targetClass is an interface, the JDK proxy will be used, otherwise, the JDK proxy will be used. Use CGLIB proxy. That is to say, even if you specify through ProxyFactory.setProxyTargetClass(true) that a Class-based CGLIB proxy will be established, it is not necessarily a CGLIB proxy in the end, because in this case, if the targetClass is an interface, a JDK proxy will also be established. The logic of this block is implemented by the createAopProxy() method of DefaultAopProxyFactory, whose source code is as follows.

	public AopProxy createAopProxy(AdvisedSupport config) 
            throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass()
                     || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				

throw new AopConfigException("TargetSource cannot determine target class: " +
	"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface()) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

The bottom layer of ProxyFactory actually delegates to the AopProxyFactory object when creating a proxy object. AopProxyFactory is an interface that defines only one createAopProxy() method. Spring provides a default implementation, DefaultAopProxyFactory. DefaultAopProxyFactory is used in ProxyFactory. Interested friends can refer to the source code of ProxyFactory.

Specify Advisor

When using Aop, we need to do some processing on the intercepted method. For Spring Aop, what kind of processing needs to be done on which method calls are defined by the Advisor, usually a PointcutAdvisor. The PointcutAdvisor interface contains two main interface methods, one is used to obtain the Pointcut, and the other is used to obtain the Advice object. The combination of the two constitutes which Advice needs to be applied to which Pointcut. So we can also implement our own Advisor implementation when needed.

/* * 
* Simple implementation of your own PointcutAdvisor 
* @author Elim May 9, 2017 
*/ public class MyAdvisor implements PointcutAdvisor { 
    

	@Override
	public Advice getAdvice() {
		return new MethodBeforeAdvice() {

			@Override
			public void before(Method method, Object[] args, 
  
                            Object target) throws Throwable {
				

System.out.println ( " BeforeAdvice implementation , called before the target method is called, the target method is: " 

+ method.getDeclaringClass().getName() + "."
						+ method.getName());
			}
		};
	}

	@Override
	public boolean isPerInstance() {
		return true;
	}

	@Override 
	public  Pointcut  getPointcut () {
		 // match all method calls 
		return  Pointcut . TRUE ;
	}

}
	@Test
	public void testProxyFactory2() {
		MyService myService = new MyService();
		ProxyFactory proxyFactory = new ProxyFactory(myService);
		proxyFactory.addAdvisor(new MyAdvisor());
		MyService proxy = (MyService) proxyFactory.getProxy();
		proxy.add();
	}

The above example is an example of specifying the Advisor corresponding to the proxy object. In fact, a proxy object can bind multiple Advisor objects at the same time. The addAdvisor() method of ProxyFactory can be called multiple times, and this method also has some overloaded method definitions, which can parameter Spring's API document.

Specify Advice

The proxy object specified in our first example is bound to an Advice, and the Advisor specified in the second example, do you have any questions about this? According to our understanding of Spring Aop, Spring's Aop proxy object must be bound to an Advisor, and usually a PointcutAdvisor, through which we can know which Pointcut (which method call) our Advice is to be applied to? When we bind an Advice object when creating a proxy object through ProxyFactory, in fact, the ProxyFactory internally converts it to an Advisor object for us, but the Pointcut corresponding to the Advisor object is a Pointcut instance that matches all method calls.

Specifies whether the proxy object needs to be published

When calling the method of the Aop proxy object, we cannot access the current proxy object by default. If we specify that the created proxy object needs to publish the proxy object externally, Spring will send the current proxy object when calling the method of the proxy object. The proxy object is stored in AopContext, and we can get the current proxy object through AopContext in the method of the target object. This is specified by the exposeProxy attribute. If we want to publish the proxy object externally, we can specify the value of this attribute to be true through the set method of exposeProxy. Such as:

	@Test
	public void testProxyFactory2() {
		MyService myService = new MyService();
		ProxyFactory proxyFactory = new ProxyFactory(myService);
		proxyFactory . setExposeProxy( true ); // Specify to publish the proxy object externally, that is, the current proxy object can be accessed through AopContext.currentProxy() in the target object method. 
		proxyFactory . addAdvisor( new  MyAdvisor ());
		proxyFactory . addAdvisor( new  MyAdvisor ()); // Specifying an Advisor multiple times will apply multiple Advisors at the same time 
		MyService proxy = ( MyService ) proxyFactory . getProxy();
		proxy.add();
	}

In addition to the above configuration information, ProxyFactory can actually configure a lot of other information. For more configuration information items, please refer to the source code of ProxyFactory or refer to the Spring API documentation.

Reference documentation

  • Spring4.1.0 official documentation
  • Spring source code

(This article is based on Spring 4.1.0, written by Elim on May 9, 2017)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326302403&siteId=291194637