I am new to aspectJ. I would like to understand field read access and field write access pointcuts. Assume I have a private static string variable "name" in my class "Field".
private static String name;
I need to assign a value when name is read and throw exception when it is set.
Below is the aspectJ code.
package main.java.testaop.field;
public aspect FieldAspect {
pointcut getName() : get(private static String Field.name);
pointcut setName() : set(private static String Field.name);
before() : getName() {
"john";}
before() : setName() {
throw new Exception(); }
I am getting errors when compiling. I knew this is wrong. Can anyone help to make me understand how this works.
You want to access private fields (which is possible, but a smell conceptually). In order to do that declare your aspect as privileged:
public privileged aspect FieldAspect { ... }
Related
I have the following POJO using Immutables+Jackson under the hood:
#JsonInclude(JsonInclude.Include.NON_NULL)
abstract class AbstractQueryRequest {
#JsonProperty("reqid")
public abstract String reqid();
#JsonProperty("rawquery")
public abstract String rawquery();
}
At some point I need to build another object based on the fields of the POJO, something along this line:
final HttpUrl.Builder urlBuilder = HttpUrl.parse(cfg.baseUrl()).newBuilder();
urlBuilder.addQueryParameter("reqid", request.reqid())
.addQueryParameter("rawquery", request.rawquery());
It's quite annoying to keep the POJO and this call aligned upon changes, I was wondering if it was possible to access programmatically each JsonProperty instead of typing the string manually.
Note that it is fine to write the getters by hand as I can easily refactor and I have the compiler double checking, but for strings I am worried for people down the line and I would like to "read" them from the POJO class somehow.
You can do it via reflection. You need to take method annotation values which annotated with JsonProperty. But I recommend you to use JsonProperty on fields, not methods.
Here is an example for your current requirement :
public class Main {
public static void main(String[] args) {
AbstractQueryRequest someType = new SomeType();
for(Method method : x.getClass().getSuperclass().getDeclaredMethods()) {
if (method.isAnnotationPresent(JsonProperty.class)) {
JsonProperty annotation = method.getAnnotation(JsonProperty.class);
System.out.println(annotation.value());
}
}
}
}
class SomeType extends AbstractQueryRequest {
#Override
public String reqid() {
return null;
}
#Override
public String rawquery() {
return null;
}
}
Output is :
rawquery
reqid
I have some class with constant to test:
public class SomeClass {
private static final String SOME_CONST = "blabla";
And I have to use it in test, so I make (with Spring ReflectionUtils and java.lang native arsenal) smth like:
findField(SomeClass.class, "SOME_CONST").setAccessible(true);
or
makeAccessible(findField(SomeClass.class, "SOME_CONST"));
Running test
#Test
public void someTest() throws Exception {
String s = SomeClass.SOME_CONST;
I see my Intellij Idea stops running with error related with trying to access private field. Suppose I should use some kind of #SuppressWarnings or how to solve such an issue better?
Testing private methods/fields is usually sign of bad design.
If you don't mind to show it around, set it public or put a getter to retrieve that variable, since it's final you should not have any trouble unless it has sensitive data or it's a mutable class (not the case of a String).
Or using your approach, do
Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
String someConst = (String) field.get(new SomeClass());
following this hint
As were mentioned above we can (or even should) change private fields of tested class to public, but in case you are not able to do so you can use my soulution (I tried to avoid extra fields in test class, but nobody suggested solution to my question):
private String EXTRA_FIELD_TO_HOLD_CONSTANT_VALUE_FROM_TESTED_CLASS;
#Before
public void setUp() throws Exception {
EXTRA_FIELD_TO_HOLD_CONSTANT_VALUE_FROM_TESTED_CLASS =
getPrivateStringConstantValue("SOME_CONST");
}
private String getPrivateStringConstantValue(String fieldName) throws IllegalAccessException {
Field field = findField(CtcFraudIPAddressListServiceImpl.class, fieldName);
field.setAccessible(true);
return field.get(testedService).toString();
}
I'm trying to write a pointcut which will intercept getters for annotated members.
public class MyClass {
private String volume;
#MyAttribute
private Validity validity;
public void setValidity( Validity obj ){
validity = obj;
}
public Validity getValidity(){
return validity;
}
}
Is there a way to write a pointcut that will intercept all calls to getValidity() based on validity being annotated with #MyAttribute? Written differently, I'm looking to create a pointcut for any getter of a member field that is annotated with #MyAttribute.
A simple getter pointcut can advise any getter method:
pointcut embeddedGetter() : execution( public * com.ia.domain..get*());
but that won't specify that the field it is getting has to be annotated. And if I put a modifier in front of public that would specify that the getter method has to be annotated, which isn't the case.
Is this even feasible?
After playing around with AspectJ, I finally rediscovered the join point I was looking for:
pointcut embeddedGetter() : get( #MyAnnotation Validity *..* );
The key is not to use the execution pointcut but rather the get.
I created a CGLib dynamic proxy of a class, but when I try to access any field declared in the original class, I obtain java.lang.NoSuchFieldException. I need to obtain the field in order to change its value.
By the way, this is the class the proxy is based on:
public class Person {
private String name;
....
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
...
}
And this is the code snippet (inside the "intercept" method of the "MethodInterceptor") that is raising the mentioned exception (more specifically the first line):
public Object intercept(Object instance, Method jdkMethod, Object[] args, MethodProxy method) throws Throwable {
...
Field field = instance.getClass().getField("name");
field.setAccessible(true);
field.set(instance, "foo");
....
Do you know any other way to access the needed field or change its value?
Thanks.
Apparently, a CGLib proxy is a subclass of the original class. So, the following code worked well:
Field field = instance.getClass().getSuperclass().getDeclaredField("name");
Try:
Field field = instance.getClass().getDeclaredField("name");
As mentioned in this SO answer, getField only works for public fields, but applies to the entire class hierarchy. You can think of it as inspecting the public interface of the class. getDeclaredField works for private fields, and will not inspect the class hierarchy; you can think of it as resolving the implementation of the class.
Even though you already figured out how to fix your problem, here is a short explanation of how cglib works and what is causing you problems. Considering your Person class, cglib creates another class at runtime which is representing your proxy. This class would approximately look like the following in Java source code, however, many of the instances used are cached which is why cglib adds several other fields. Furthermore, the MethodInterceptor is injected by using different static fields:
public class Person$EnhancedByCglib extends Person {
private static class GetNameMethodProxy extends MethodProxy {
#Override
public Object invokeSuper(Object instance,
Object[] arguments) {
return ((Person$EnhancedByCglib) instance).getNameSuper();
}
// ...
}
// ...
private static MethodInterceptor methodInterceptor;
#Override
public String getName() {
return (String) methodInterceptor.intercept(this,
getClass().getDeclaredMethod("getName"),
new Object[0],
new GetNameMethodProxy());
}
private String getNameSuper() {
return super.getName();
}
#Override
public void setName(String name) {
methodInterceptor.intercept(this,
getClass().getDeclaredMethod("setName", String.class),
new Object[] {name},
new SetNameMethodProxy());
}
private void setNameSuper(String name) {
super.setName(name);
}
// ...
}
As you can see, the interception is implemented by overriding any method. This way, your MethodInterceptor is invoked instead of the original method which is still invokable by using the MethodProxy. Due to the interception, calling getMethod or getDeclaredMethod works as expected when using cglib. Fields are however not inherited which is why you need to browse the class hierarchy one class up. This is why:
instance.getClass().getSuperclass().getDeclaredField("name");
works. Note that cglib is not longer maintained. Have a look at my library Byte Buddy in case that you are looking for an alternative. Note however that I am releasing a fully stable version sometime next week. The current v0.1 release contains some premature features.
How can I set or get a field in a class whose name is dynamic and stored in a string variable?
public class Test {
public String a1;
public String a2;
public Test(String key) {
this.key = 'found'; <--- error
}
}
You have to use reflection:
Use Class.getField() to get a Field reference. If it's not public you'll need to call Class.getDeclaredField() instead
Use AccessibleObject.setAccessible to gain access to the field if it's not public
Use Field.set() to set the value, or one of the similarly-named methods if it's a primitive
Here's an example which deals with the simple case of a public field. A nicer alternative would be to use properties, if possible.
import java.lang.reflect.Field;
class DataObject
{
// I don't like public fields; this is *solely*
// to make it easier to demonstrate
public String foo;
}
public class Test
{
public static void main(String[] args)
// Declaring that a method throws Exception is
// likewise usually a bad idea; consider the
// various failure cases carefully
throws Exception
{
Field field = DataObject.class.getField("foo");
DataObject o = new DataObject();
field.set(o, "new value");
System.out.println(o.foo);
}
}
Class<?> actualClass=actual.getClass();
Field f=actualClass.getDeclaredField("name");
The above code would suffice .
object.class.getField("foo");
Unfortunately the above code didn't work for me , since the class had empty field array.