赞
踩
目录
3、cglib 代理(Code Generation Library Proxy)
业务场景中出现过使用内部类获取属性的时候获取不到,导致业务逻辑执行异常,观察代码后发现原因是最底层有@Async的方法导致Spring代理方式变更,特此梳理一下。
如下代码结果展示
类代码
-
- /**
- * @author YY-帆S
- * @Date 2024/4/16 21:16
- */
- @Service
- @Slf4j
- public class TestProxy {
-
- @Value("${test.boolean:true}")
- public boolean aBoolean;
-
- public void testAQuery() {
- log.info("testAQuery:{}", aBoolean);
- TestProxy bean = SpringContextUtils.getBean(TestProxy.class);
- bean.testBQuery();
- bean.testCQuery();
- }
-
- public void testBQuery() {
- log.info("testBQuery:{}", aBoolean);
- }
-
- private void testCQuery() {
- log.info("testCQuery:{}", aBoolean);
- }
-
- @Component
- public class InnerClass {
- public void testInnerQuery() {
- log.info("testInnerQuery:{}", aBoolean);
- }
- }
-
- //第一次 不加上
- // @Async
- //第二次加上
- @Async
- public void testAsync() {
- }
- }
调用代码
- @Resource
- TestProxy testProxy;
-
- @Resource
- TestProxy.InnerClass innerClass;
-
- @GetMapping("testQuery")
- public CommonResult<Object> testQuery() {
- testProxy.testAQuery();
- innerClass.testInnerQuery();
- return new CommonResult<>();
- }
不加上@Asnyc的结果
加上@Async的结果
加Async之前的bean属性,内部aBoolean=true
加Async之后的bean,内部aBoolean=false,(默认值)
可以观察到,加@Async或@Transcational 这类的注解时,会导致spring切换对类切换代理方式,为cglib的代理模式,翻了spring源码的话会发现,spring生成代理对象的时候使用了Objenesis来创建,Objenesis可以绕过构造方法以及相关的初始化来创建对象,所以生成的代理类中所有的属性全部都是空的。
参考文档:
spring——事务动态代理造成属性为null_切面生成的cglb代理类里面的属性为null-CSDN博客
1)减少直接读写属性,而是调用其中的方法
- //类代码
-
- @Service
- @Slf4j
- @Data
- public class TestProxy {
-
- @Value("${test.boolean.b:true}")
- public Boolean bBoolean;
-
- @Async
- public void testAsync() {
- }
- }
-
- //……调用代码
- @Resource
- TestProxy testProxy;
-
- @GetMapping("testQuery")
- public CommonResult<Object> testQuery() {
- log.info("user func:{}", testProxy.getBBoolean());
- log.info("direct use: {}", testProxy.bBoolean);
- return new CommonResult<>();
- }
-
结果:使用方法获取属性则正常返回配置后的结果true,直接使用属性的为null 空
2)多自测,兄弟们,必现的case,测一下就知道了
静态代理在编译时确定代理类,代理类和目标类都实现相同的接口。代理类在调用目标类的方法之前或之后添加一些额外的操作。
优点
缺点
- public interface UserService {
- void addUser(String name);
- }
-
- public class UserServiceImpl implements UserService {
- @Override
- public void addUser(String name) {
- System.out.println("Adding user: " + name);
- }
- }
-
- public class UserServiceProxy implements UserService {
- private UserServiceImpl userService;
-
- public UserServiceProxy(UserServiceImpl userService) {
- this.userService = userService;
- }
-
- @Override
- public void addUser(String name) {
- System.out.println("Before adding user");
- userService.addUser(name);
- System.out.println("After adding user");
- }
- }
-
- // 使用代理
- public class Main {
- public static void main(String[] args) {
- UserServiceImpl userService = new UserServiceImpl();
- UserServiceProxy proxy = new UserServiceProxy(userService);
- proxy.addUser("Alice");
- }
- }
JDK动态代理基于Java的反射机制,通过在运行时生成代理类来实现。代理类必须实现一个或多个接口。JDK动态代理使用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口来创建代理实例。
优点
缺点
- public interface UserService {
- void addUser(String name);
- }
-
- public class UserServiceImpl implements UserService {
- @Override
- public void addUser(String name) {
- System.out.println("Adding user: " + name);
- }
- }
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
- public class UserServiceProxy implements InvocationHandler {
- private Object target;
-
- public UserServiceProxy(Object target) {
- this.target = target;
- }
-
- public Object getProxy() {
- return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("Before method");
- Object result = method.invoke(target, args);
- System.out.println("After method");
- return result;
- }
- }
-
- // 使用代理
- UserService target = new UserServiceImpl();
- UserService proxy = (UserService) new UserServiceProxy(target).getProxy();
- proxy.addUser("Alice");
CGLIB代理通过生成目标类的子类并覆盖其方法来实现代理。它使用字节码增强技术。
优点
缺点
final
类和方法:由于CGLIB通过生成子类来实现代理,final
类和方法无法被代理。- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- import java.lang.reflect.Method;
-
- public class ProductService {
- public void addProduct(String name) {
- System.out.println("Adding product: " + name);
- }
- }
-
- public class ProductServiceProxy implements MethodInterceptor {
- private Object target;
-
- public ProductServiceProxy(Object target) {
- this.target = target;
- }
-
- public Object getProxy() {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(target.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
-
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- System.out.println("Before method");
- Object result = proxy.invokeSuper(obj, args);
- System.out.println("After method");
- return result;
- }
- }
-
- // 使用代理
- ProductService target = new ProductService();
- ProductService proxy = (ProductService) new ProductServiceProxy(target).getProxy();
- proxy.addProduct("Laptop");
总结:
final
类和方法。根据具体需求和应用场景,可以选择合适的代理模式。Spring默认选择JDK动态代理,如果目标类没有实现接口,则使用CGLIB代理。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。