undisp
undisp

Reputation: 721

AspectJ - Use reflection to instanciate object of not public class

I am trying to instanciate an object of a non public class in AspectJ.

I have this class:

package ca1.business;

public class Bill {

    int id;
    String idOperator;
    String idClient;

    Bill(int id, String idOperator, String idClient) { /* (...) */ }
    public String toString() { /* (...) */ }
    public boolean equals(Object o) { /* (...) */ }
    public int getId() { /* (...) */ }
    public String getOperator() { /* (...) */ }
    public String getClient() { /* (...) */ }
}

In the aspects class I wanted to be able to do:

Bill b = new Bill(currInvoice, idOperator, idClient);

The problem is that I get an error:

The constructor Bill(int, String, String) is not visible

So I investigated and tried to use reflection like it's explained in this post.

try {
    Constructor<Bill> cons = Bill.class.getDeclaredConstructor(null);
    cons.setAccessible(true);
    Bill invoice = cons.newInstance(null);
    invoice.id = 1;
    invoice.idOperator = "foo";
    invoice.idClient = "bar";
    // etc...
} catch (SecurityException e) {
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    e.printStackTrace();
} catch (IllegalArgumentException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

But I get an error in the lines:

invoice.id = 1;
invoice.idOperator = "foo";
invoice.idClient = "bar";

The error is:

The field Bill.X is not visible.

Does anyone know if there is any workaround?

Upvotes: 0

Views: 206

Answers (1)

kriegaex
kriegaex

Reputation: 67457

Why use reflection?

Let us assume the Bill class looks like this:

package ca1.business;

public class Bill {
  int id;
  String idOperator;
  String idClient;

  Bill(int id, String idOperator, String idClient) {
    this.id = id;
    this.idOperator = idOperator;
    this.idClient = idClient;
  }

  @Override
  public String toString() {
    return "Bill [id=" + id + ", idOperator=" + idOperator + ", idClient=" + idClient + "]";
  }

  public static Bill instance;

  public static void main(String[] args) {
    System.out.println(instance);
  }
}

We want our aspect to populate the static member before main is executed (silly example, just for demo):

Option A: privileged aspect

package de.scrum_master.aspect;

import ca1.business.Bill;

public privileged aspect MyAspect {
  before() : execution(public static void main(String[])) {
    Bill.instance = new Bill(11, "operator", "client");
  }
}

Option B: put aspect into package ca1.business

The constructor is package-private, i.e. other classes in the same package can access it.

package ca1.business;

public aspect MyAspect {
  before() : execution(public static void main(String[])) {
    Bill.instance = new Bill(11, "operator", "client");
  }
}

Option C: put factory class into protected package as a helper

package ca1.business;

public class BillFactory {
  public static Bill create(int id, String idOperator, String idClient) {
    return new Bill(id, idOperator, idClient);
  }
}
package de.scrum_master.aspect;

import ca1.business.Bill;
import ca1.business.BillFactory;;

public aspect MyAspect {
  before() : execution(public static void main(String[])) {
    Bill.instance = BillFactory.create(11, "operator", "client");
  }
}

Console log for each option A, B, C

Bill [id=11, idOperator=operator, idClient=client]

Upvotes: 1

Related Questions