Dynamic Proxy
Proxy pattern: Provide a proxy for other objects to control access to this object, enhance a method in a class, and extend the program.
Dynamic proxies can add additional logic to methods in a class without modifying the class source code
Creation of proxy objects through cglib:
Based on Parent and Child Classes, the proxy class is the parent class, the proxy class is the subclass, the proxy object is the instance object of the proxy class, and the proxy class is created by cglib
import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; // cglib dynamic proxy is used alone public class Test { public static void main(String[] args) { 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() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before..."); // // Writing method one // Object result = methodProxy.invoke(target, objects); // // Writing method two // Object result = method.invoke(target, objects); //Writing method three Object result = methodProxy.invokeSuper(o, objects); System.out.println("after..."); return result; } }}); // UserService object created by dynamic proxy UserService userService = (UserService)enhancer.create(); //When executing the test method of this userService, some other logic will be executed. userService.test(); } } public class UserService { public void test() { System.out.println("test"); } }
Use JDK dynamic proxy to generate a proxy object:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { UserService target = new UserService(); //Proxy object of UserInterface interface Object proxy = Proxy.newProxyInstance(UserService.class.getClassLoader(), new Class[] {UserInterface.class}, new InvocationHandler() { @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; } }); // Note that this must be the interface type UserInterface. If it is the UserService class, an error will be reported. UserInterface userService = (UserInterface)proxy; userService.test(); } } public class UserService implements UserInterface { @Override public void test() { System.out.println("test"); } } public interface UserInterface { public void test(); }
Note: The proxy object proxy must be of interface type
ProxyFactory
Spring encapsulates the above two dynamic proxy technologies, and the encapsulated class is called ProxyFactory
Represents a factory for creating proxy objects, which is more convenient to use.
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; public class Test { public static void main(String[] args) { UserService target = new UserService(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(target); proxyFactory.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before..."); Object result = invocation.proceed(); System.out.println("after..."); return result; } }); \t\t UserInterface userService = (UserInterface)proxyFactory.getProxy(); userService.test(); } } public class UserService implements UserInterface { @Override public void test() { System.out.println("test"); } } public interface UserInterface { public void test(); }
ProxyFactory will automatically determine whether to use cglib or jdk dynamic proxy
If the class implements the interface, then the bottom layer of ProxyFactory will use jdk dynamic proxy
If the interface is not implemented, cglib technology will be used
Classification of Advice
1. Before Advice: executed before the method
2. After returning advice: executed after the method returns
3. After throwing advice: Execute after the method throws an exception
4. After (finally) advice: The method is executed after finally. This is the last one, later than return.
5. Around advice: This is the most powerful Advice, and the execution order can be customized.
Advisor
An Advisor consists of a Pointcut and an Advice. Pointcut can be used to specify the logic that needs to be proxied.
You can use the Advisor to control which method to proxy
public class Test { public static void main(String[] args) { UserService target = new UserService(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(target); proxyFactory.addAdvisor(new PointcutAdvisor() { @Override public Pointcut getPointcut() { return new StaticMethodMatcherPointcut() { @Override public boolean matches(Method method, Class<?> targetClass) { return method.getName().equals("testAbc"); } }; } @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; } }; } @Override public boolean isPerInstance() { return false; } }); UserInterface userService = (UserInterface)proxyFactory.getProxy(); userService.test(); } }
How to create proxy objects
ProxyFactoryBean
public class UserService { public void test() { System.out.println("test"); } } @ComponentScan(value = "com.gax") public classAppConfig { @Bean public ProxyFactoryBean userService() { UserService userService = new UserService(); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(userService); proxyFactoryBean.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("before..."); Object result = invocation.proceed(); System.out.println("after..."); return result; } }); return proxyFactoryBean; } } public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userServiceProxy = (UserService)applicationContext.getBean("userService"); userServiceProxy.test(); } }
The above method can only target a certain Bean
ProxyFactoryBean also has additional functions. For example, you can define an Advise or Advisor as a Bean and then set it in ProxyFactoryBean.
public class UserService { public void test() { System.out.println("test"); } } @ComponentScan(value = "com.gax") public classAppConfig { @Bean public MethodInterceptor gaxAroundAdvise() { 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; } }; } @Bean public ProxyFactoryBean userService() { UserService userService = new UserService(); ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(userService); proxyFactoryBean.setInterceptorNames("gaxAroundAdvise"); return proxyFactoryBean; } } public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userServiceProxy = (UserService)applicationContext.getBean("userService"); userServiceProxy.test(); } }
BeanNameAutoProxyCreator
ProxyFactoryBean needs to specify the object to be proxied; BeanNameAutoProxyCreator can proxy a bean by specifying the name of the bean.
Through BeanNameAutoProxyCreator, you can perform AOP on batches of beans, and specify the proxy logic and an InterceptorName, which is an Advise. The prerequisite is that the Advise must also be a Bean so that Spring can find it. However, the shortcomings of BeanNameAutoProxyCreator are obvious. It can only specify the bean it wants to proxy based on the beanName.
@Component public class UserService { public void test() { System.out.println("test"); } } @ComponentScan(value = "com.gax") public classAppConfig { @Bean public MethodInterceptor gaxAroundAdvise() { 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; } }; } @Bean public BeanNameAutoProxyCreator beanNameAutoProxyCreator() { BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator(); beanNameAutoProxyCreator.setBeanNames("userSe*"); beanNameAutoProxyCreator.setInterceptorNames("gaxAroundAdvise"); beanNameAutoProxyCreator.setProxyTargetClass(true); return beanNameAutoProxyCreator; } } public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userServiceProxy = (UserService)applicationContext.getBean("userService"); userServiceProxy.test(); } }
DefaultAdvisorAutoProxyCreator
@Component public class UserService { public void test() { System.out.println("test"); } } @ComponentScan(value = "com.gax") public classAppConfig { @Bean public MethodInterceptor gaxAroundAdvise() { 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; } }; } @Bean public DefaultPointcutAdvisor defaultPointcutAdvisor() { NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.addMethodName("test"); DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(); defaultPointcutAdvisor.setPointcut(pointcut); defaultPointcutAdvisor.setAdvice(gaxAroundAdvise()); return defaultPointcutAdvisor; } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); return defaultAdvisorAutoProxyCreator; } } public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class); UserService userServiceProxy = (UserService)applicationContext.getBean("userService"); userServiceProxy.test(); } }
Through DefaultAdvisorAutoProxyCreator will directly find all Advisor type beans, and determine the beans to be proxied and the proxy logic based on the PointCut and Advice information in the Advisor.
Simplified into annotation method:
@Aspect @Component public class GaxAspect { @Before("execution(public void com.gax.service.UserService.test())") public void gaxBefore(JoinPoint joinPoint) { System.out.println("gaxBefore"); } }
Class to proxy: expression
Proxy logic: methods modified by @Before
Spring only needs to parse these annotations. After parsing, it gets the corresponding Pointcut object and Advice object, generates an Advisor object, throws it into the ProxyFactory, and then generates the corresponding proxy object. The specific way to parse these annotations is the@EnableAspectJAutoProxy annotation< /strong>Things to do