raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.22k stars 804 forks source link

Use OnMethodEnter can not access complex object #1436

Open MaLuxray opened 1 year ago

MaLuxray commented 1 year ago

In my java code

class KnowledgeSmsAdvice{
       private static final WechatMiniappFeature wechatMiniappFeature = new WechatMiniappFeatureImpl();

    @Advice.OnMethodEnter
    public static void onMethodEnter(@Advice.Origin Method method,
                                     @Advice.Argument(value = 0, optional = true, readOnly = false, typing = Assigner.Typing.DYNAMIC) Object argument) {
       if (argument == null) return;

       ***

       final String wechatMiniappName = wechatMiniappFeature.getWechatMiniappName(hospitalId);

       ***
    }
}

In my premain class


    public class PremainClass {
    public static void premain(String args, Instrumentation instrumentation) {
        Profile.inti(args);

        //
        AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, module, protectionDomain) ->
                builder.method(ElementMatchers.named("sendLoopMessageTask")).intercept(Advice.to(KnowledgeSmsAdvice.class));

        new AgentBuilder.Default()
                .type(ElementMatchers.named("com.companies.message.interfaces.controller.MessageTaskController"))
                .transform(transformer)
                .installOn(instrumentation);
         }
    }

but when i access sendLoopMessageTask this method get a exception


Handler dispatch failed; nested exception is java.lang.IllegalAccessError: tried to access field com.ipharmacare.mvp.agent.message.center.advice.KnowledgeSmsAdvice.wechatMiniappFeature from class com.companies.message.interfaces.controller.MessageTaskController
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.IllegalAccessError: tried to access field com.ipharmacare.mvp.agent.message.center.advice.KnowledgeSmsAdvice.wechatMiniappFeature from class com.companies.message.interfaces.controller.MessageTaskController

why goto access com.companies.message.interfaces.controller.MessageTaskController.wechatMiniappFeature

FrankChen021 commented 1 year ago

I think this is because your advice class and the field wechatMiniappFeature are not declared as public.

raphw commented 1 year ago

As pointed out, the code is more or less copy pasted. You can change this if you set inline = false by delegation. But you are responsible to make sure that the targeted code is visible, or that the accessed code is visible from the inlined block.

MaLuxray commented 1 year ago

PremainClass java code

   public class PremainClass {
       public static void premain(String args, Instrumentation instrumentation) {
           Profile.inti(args);

           AgentBuilder.Transformer transformer = (builder, typeDescription, classLoader, module, protectionDomain) ->
                  builder.method(ElementMatchers.named("sendLoopMessageTask")).intercept(Advice.to(KnowledgeSmsAdvice.class));

           new AgentBuilder.Default()
                   .type(ElementMatchers.named("com.companies.message.interfaces.controller.MessageTaskController"))
                   .transform(transformer)
                   .installOn(instrumentation);
        }
    }

KnowledgeSmsAdvice java code

    public class KnowledgeSmsAdvice {
        private static final String knowledgeSceneCode = "HOSPITAL_KNOWLEDGE_SMS";
        private static final WechatMiniappFeature wechatMiniappFeature = new WechatMiniappFeatureImpl();

        @Advice.OnMethodEnter
        public static void onMethodEnter(@Advice.Origin Method method,
                                        @Advice.Argument(value = 0, optional = true, readOnly = false, typing = Assigner.Typing.DYNAMIC) 
                                        Object argument) {
             if (argument == null) return;

             JSONObject reqJSON = JSON.parseObject(JSON.toJSONString(argument));

             String sceneCode = StringUtils.trimToNull(reqJSON.getString("sceneCode"));
             if (!StringUtils.equals(sceneCode, knowledgeSceneCode)) return;

             Long hospitalId = reqJSON.getLong("hospitalId");
             if (hospitalId == null) return;

             final String wechatMiniappName = wechatMiniappFeature.getWechatMiniappName(hospitalId);
             if (wechatMiniappName == null) return;

             final String targetSceneCode = WechatMiniappName.generateMessageScene(wechatMiniappName, 
                        knowledgeSceneCode);
             reqJSON.put("sceneCode", targetSceneCode);
        }
    }

agent method MessageTaskController java code

   @RequestMapping({"/provider/message/task/client"})
   @RestController
   public class MessageTaskController {
        private static final Logger log = LoggerFactory.getLogger(MessageTaskController.class);
        @Autowired
        MessageTaskService messageTaskService;
        @Autowired
        MessagePushWithJiGuangService messagePushWithJiGuangService;

        @PostMapping({"/sendLoopMessageTask"})
        public ServerResponse<String> sendLoopMessageTask(@RequestBody MessageTaskLoopVO messageTaskLoopVO) throws Exception {
             return ServerResponse.createBySuccess(this.messageTaskService.saveLoopMessageTask(messageTaskLoopVO));
        }
    }  

when i access sendLoopMessageTask method get a exception

@raphw

MaLuxray commented 1 year ago

As shown in the code above. Accessing the knowledgeSceneCode field was successful, but the wechatMiniappFeature field would result in an error

@raphw

raphw commented 1 year ago

This is because final strings (and primitives) are inlined. The field access is eliminated by javac.

RTxin commented 1 year ago

You should change the annotation @advice.onMethodenter to@advice.onMethodente (inline = false)