Введите код в метод - Java

Существует ли способ автоматически ввести код в метод?

У меня есть следующее типичное поле с методом считывания и методом set, и я хотел бы ввести обозначенный код в метод установщика, который записывает, если поле было изменено также для вставки обозначенного "isFirstNameModified" поля, чтобы также отследить, если поле было изменено или нет.

 public class Person {

      Set<String> updatedFields = new LinkedHashSet<String>();

      String firstName;
      public String getFirstName(){
           return firstName;
      }

      boolean isFirstNameChanged = false;           // This code is inserted later
      public void setFirstName(String firstName){       
           if( !isFirstNameChanged ){               // This code is inserted later
                isFirstNameChanged = true;          // This code is inserted later
                updatedFields.add("firstName");     // This code is inserted later
           }                                        // This code is inserted later
           this.firstName = firstName;
      }
 }

Я также не уверен, могу ли я подмножество имени метода как строка из самого метода, как обозначено на строке, где я добавляю имя поля как строку в набор обновленных полей: updatedFields.add("firstName");. И я не уверен, как вставить поля в класс, где я добавляю булево поле, которое отслеживает, если поле было изменено или не прежде (чтобы эффективность предотвратила необходимость управлять Набором): boolean isFirstNameChanged = false;

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

Править:::::::::

Я Должен был использовать этот более простой код вместо примера выше. Все, что это делает, добавляет название поля как строка к набору.

 public class Person {

  Set<String> updatedFields = new LinkedHashSet<String>();

  String firstName;
  public String getFirstName(){
       return firstName;
  }
  public void setFirstName(String firstName){       
       updatedFields.add("firstName");        // This code is inserted later
       this.firstName = firstName;
  }

}

7
задан Chris Dutrow 13 June 2010 в 06:20
поделиться

3 ответа

AspectJ позволяет изменять методы и поля с помощью рекомендаций.

Мой пример написан с синтаксисом @AspectJ , который изменяет код во время компиляции или загрузки. Если вы хотите внести изменения во время выполнения, вы можете использовать Spring AOP, который также поддерживает этот синтаксис @AspectJ .

Пример с простым классом Person и репозиторием-заглушкой. Вся информация о том, какие поля обновляются, обрабатывается аспектом SetterAspect. Он отслеживает, какие поля обновляются при записи в поля.

Другой совет в этом примере касается метода обновления в репозитории. Это необходимо для получения данных, собранных с первого аспекта.

Класс Person:

public class Person {

    private String firstName;

    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }   

    public static void main(String[] args) {
        Person person = new Person();
        person.setFirstName("James");
        person.lastName = "Jameson";

        DtoRepository<Person> personRepository = new DtoRepository<Person>();
        personRepository.update(person);
    }
}

Репозиторий-заглушка:

public class DtoRepository<T> {

    public void update(T t) {
        System.out.println(t.getClass().getSimpleName() + " updated..");
    }

    public void updatePerson(T t, Set<String> updatedFields) {
        System.out.print("Updated the following fields on " +
            t.getClass().getSimpleName() + " in the repository: "
            + updatedFields);       
    }
}

Результат выполнения метода main () в классе Person с AspectJ:

Обновлены следующие поля в Person в репозитории: [lastName, firstName]

Здесь важно отметить, что метод main () вызывает DtoRepository.update (T t) , но DtoRepository.update (T t, Set updatedFields) запускается из-за рекомендации в аспекте репозитория.

Аспект, который отслеживает все записи в закрытые поля в демонстрационном пакете:

@Aspect
public class SetterAspect {

    private UpdatableDtoManager updatableDtoManager = 
        UpdatableDtoManager.INSTANCE;

    @Pointcut("set(private * demo.*.*)")
    public void setterMethod() {}

    @AfterReturning("setterMethod()")
    public void afterSetMethod(JoinPoint joinPoint) {
        String fieldName = joinPoint.getSignature().getName();
        updatableDtoManager.updateObjectWithUpdatedField(
                fieldName, joinPoint.getTarget());      
    }
}

Аспект репозитория:

@Aspect
public class UpdatableDtoRepositoryAspect {

    private UpdatableDtoManager updatableDtoManager = 
        UpdatableDtoManager.INSTANCE;

    @Pointcut("execution(void demo.DtoRepository.update(*)) " +
            "&& args(object)")
    public void updateMethodInRepository(Object object) {}

    @Around("updateMethodInRepository(object)")
    public void aroundUpdateMethodInRepository(
            ProceedingJoinPoint joinPoint, Object object) {

        Set<String> updatedFields = 
            updatableDtoManager.getUpdatedFieldsForObject(object);

        if (updatedFields.size() > 0) {
            ((DtoRepository<Object>)joinPoint.getTarget()).
                updatePerson(object, updatedFields);
        } else {

            // Returns without calling the repository.
            System.out.println("Nothing to update");
        }
    }   
}

Наконец, два вспомогательных класса, используемые аспектами:

public enum UpdatableDtoManager {

    INSTANCE;

    private Map<Object, UpdatedObject> updatedObjects = 
        new HashMap<Object, UpdatedObject>();

    public void updateObjectWithUpdatedField(
            String fieldName, Object object) {
        if (!updatedObjects.containsKey(object)) {
            updatedObjects.put(object, new UpdatedObject());
        }

        UpdatedObject updatedObject = updatedObjects.get(object);
        if (!updatedObject.containsField(fieldName)) {
            updatedObject.add(fieldName);
        }
    }

    public Set<String> getUpdatedFieldsForObject(Object object) {
        UpdatedObject updatedObject = updatedObjects.get(object);

        final Set<String> updatedFields;
        if (updatedObject != null) {
            updatedFields = updatedObject.getUpdatedFields();
        } else {
            updatedFields = Collections.emptySet();
        }

        return updatedFields;
    }
}

и

public class UpdatedObject {

    private Map<String, Object> updatedFields = 
        new HashMap<String, Object>();

    public boolean containsField(String fieldName) {
        return updatedFields.containsKey(fieldName);
    }

    public void add(String fieldName) {
        updatedFields.put(fieldName, fieldName);        
    }

    public Set<String> getUpdatedFields() {
        return Collections.unmodifiableSet(
                updatedFields.keySet());
    }
}

Мой пример выполняет все обновить логику с аспектами. Если бы все DTO реализовали интерфейс, который возвращает Set , вы могли бы избежать последнего аспекта.

Надеюсь, это ответ на ваш вопрос!

3
ответ дан 7 December 2019 в 01:16
поделиться

Да, можно, один из подходов - для использования какой-либо формы манипулирования байтовым кодом (например, javassist , ASM , BCEL) или библиотеки AOP более высокого уровня, расположенной поверх одного из этих инструментов, например AspectJ , JBoss AOP.

Примечание: большинство библиотек JDO делают это для обработки сохраняемости.

Вот пример использования javassist:

public class Person {

    private String firstName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

public static void rewritePersonClass() throws NotFoundException, CannotCompileException {
    ClassPool pool = ClassPool.getDefault();
    CtClass ctPerson = pool.get("Person");
    CtClass ctSet = pool.get("java.util.LinkedHashSet");

    CtField setField = new CtField(ctSet, "updatedFields", ctPerson);
    ctPerson.addField(setField, "new java.util.LinkedHashSet();");

    CtMethod method = ctPerson.getDeclaredMethod("setFirstName");
    method.insertBefore("updatedFields.add(\"firstName\");");

    ctPerson.toClass();
}


public static void main(String[] args) throws Exception {
    rewritePersonClass();

    Person p = new Person();
    p.setFirstName("foo");

    Field field = Person.class.getDeclaredField("updatedFields");
    field.setAccessible(true);
    Set<?> s = (Set<?>) field.get(p);

    System.out.println(s);
}
6
ответ дан 7 December 2019 в 01:16
поделиться

Вы можете использовать динамические прокси-классы и получить событие до вызова setFirstName и других методов set ... , определить имя поля с помощью method.substring (3) => "FirstName", и поместите его в setFirstName .

1
ответ дан 7 December 2019 в 01:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: