Java: ввод кода до и после вызова метода - PullRequest
2 голосов
/ 31 января 2020

Я рискую ответить на этот вопрос, зная, что было задано немало других вопросов , которые каким-то образом касаются той же проблемы. Но они либо довольно стары, либо очень специфичны c, либо сложны для подражания, а ответы также очень специфичны c и, похоже, неприменимы к моему варианту использования.

Чего я хочу добиться, так это довольно просто описать, речь идет о внедрении кода до и после вызова метода в Java. Вот простой пример. Я хочу что-то вроде этой строки кода:

method(p1, @ANNOTATION(type=String) p2, p3);

для десугарации до

Wrapper<String> w = new Wrapper<>(p2);
method(p1, w, p3);
p2 = w.get();

в контексте большего блока операторов с предопределенным классом Wrapper. У меня ограниченный контроль над средой сборки, которая используется для компиляции кода, поэтому любое решение должно быть реализовано с помощью плагинов / зависимостей Gradle.

То, на что я смотрел до сих пор (без глубокого понимания каких-либо из них):

  • Java Аннотации : никогда серьезно не рассматриваются в ответах на аналогичные вопросы, поэтому я думаю, что они не способны достичь этого
  • Фреймы Aspect , такие как AspectJ или Spring Aspect: упоминаются чаще всего, но, похоже, они способны только обернуть целые методы, а не указывать c их вызовы
  • Фреймворки манипулирования байт-кодом как asm или cglib: кажется, что в целом способны на это, но довольно тяжелые и требуют некоторой сантехники, особенно на стороне сборки, чтобы достичь этого

Итак, что может быть решением для этого?

Ответы [ 2 ]

0 голосов
/ 03 февраля 2020

Как всегда на SO, ответы предлагаются в комментариях, а не в ответах. Поэтому я выбираю из приведенных выше комментариев то, что мне кажется как минимум наиболее перспективным подходом для дальнейшего развития:

Используйте библиотеку манипулирования байт-кодом, например ASM или cglib .

0 голосов
/ 02 февраля 2020

Вы можете использовать аспектj, чтобы достичь того, что вы хотите. Допустим, у нас есть скомпилированный jar с методом, аналогичным тому, который вы описали: вы хотите перехватывать вызовы метода logStuff из определенного местоположения (-ей) в вашем приложении и изменять logi c для переноса аргумента в DummyContainer объект. Вот как может выглядеть ваш код.



    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface DummyAnnotation {
        Class type();
    }

    //that is your target method
    public static void logStuff(@DummyAnnotation(type = String.class) Object data) {
        if(data instanceof String) {
            System.out.println("String: " + data);
        } else {
            System.out.println("Not a string " + data);
        }
    }

Класс фиктивного контейнера:



    public static class DummyContainer {
        private String string;
        private Class clazz;

        public DummyContainer(String string, Class clazz) {
            this.clazz = clazz;
            this.string = string;
        }
        @Override
        public String toString() {
            return "DummyContainer=[clazz: " + clazz + ", string: " + string + "]";
        }
    }

Аспект:



    @Aspect
    public class WrapperAspect {

        @Pointcut("call(* "
        /**method to intercept*/
        + "com.yourpackage.YourType.logStuff(Object)) && args(param) && "
        /**only calls made from within that location 
        * will be intercepted. Remove it to intercept 
        * calls from everywhere*/
        + "within(test.Runner)")
        public void logStuffPointcut(Object param) {}


    @Around("logStuffPointcut(param)")
    public void simpleWrap(Object param, ProceedingJoinPoint jp) throws Throwable {
        String calledMethodName = jp.getSignature().getName();
        Class type =  jp.getSignature().getDeclaringType();
        Method method = type.getDeclaredMethod(calledMethodName, Object.class);
        DummyAnnotation annotation = method.getParameters()[0].getAnnotation(DummyAnnotation.class);
        DummyContainer wrapped = new DummyContainer((String) param, (Class)annotation.type());
        //code before method call
        jp.proceed(new Object[] {wrapped});
        //code after method call
    }
    }

Метод в test.Runner классе, который вызывает logStuff



    public static void main(String[] args) {
        Object instance = "thats a string";
        System.out.println("Obj type : " + instance.getClass());
        YourType.logStuff(instance);
        System.out.println("Obj type : " + instance.getClass());
    }

Вывод:

Obj type : class java.lang.String
Not a string DummyContainer=[clazz: class java.lang.String, string: thats a string]
Obj type : class java.lang.String

Для построения проекта я использую Maven. Вот как выглядит раздел сборки моего pom.xml:

    ...
    ...
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.6.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>aspectj-maven-plugin</artifactId>
                    <version>1.11</version>
                    <dependencies>
                        <dependency>
                            <groupId>org.aspectj</groupId>
                            <artifactId>aspectjrt</artifactId>
                            <version>1.8.13</version>
                        </dependency>
                        <dependency>
                            <groupId>org.aspectj</groupId>
                            <artifactId>aspectjtools</artifactId>
                            <version>1.8.13</version>
                        </dependency>
                    </dependencies>
                    <configuration>
                        <Xajruntimetarget>1.8</Xajruntimetarget>
                        <complianceLevel>1.8</complianceLevel>
                        <weaveDependencies>
                            <weaveDependency>
                                <groupId>your.jar.group.id</groupId>
                                <artifactId>artifactid</artifactId>
                            </weaveDependency>
                        </weaveDependencies>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    ...
    ...
...