Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KeyGenerator #46

Closed
amjad1210 opened this issue Apr 29, 2018 · 11 comments
Closed

KeyGenerator #46

amjad1210 opened this issue Apr 29, 2018 · 11 comments

Comments

@amjad1210
Copy link
Contributor

amjad1210 commented Apr 29, 2018

Hi,

I'm moving over from Spring cache to Jetcache but can't seem to figure out the best way to specify a global key generator.

In spring cache this was achieved by the code below:

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (o, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(method.getDeclaringClass().getSimpleName());
            sb.append("_");
            sb.append(method.getName());
            for (Object param : params) {
                sb.append("_");
                sb.append(param.toString());
            }
            return sb.toString();
        };
    }

The reason i need this is because i have some subclasses which need to have a different cache name hence the usage of method.getDeclaringClass().getSimpleName().

Can you maybe tell me what the easiest way to achieve this in Jetcache would be?

Thanks

@areyouok
Copy link
Collaborator

areyouok commented Apr 30, 2018

I do not understand.

If you use @Cached annotation and do not specified the key attribute, simply say the final key is cacheName+keyConvertor.convert(methodArguments).

The cacheName is specified by name attribute. A default name is used if you do not give one. The default name include all information of class and method :

    @Override
    public String generateCacheName(Method method) {
        String cacheName = cacheNameMap.get(method);

        if (cacheName == null) {
            final StringBuilder sb = new StringBuilder();

            String className = method.getDeclaringClass().getName();
            sb.append(ClassUtil.getShortClassName(removeHiddenPackage(hiddenPackages, className)));
            sb.append('.');
            sb.append(method.getName());
            sb.append('(');

            for(Class<?> c : method.getParameterTypes()){
                getDescriptor(sb, c , hiddenPackages);
            }

            sb.append(')');

            String str = sb.toString();
            cacheNameMap.put(method, str);
            return str;
        } else {
            return cacheName;
        }
    }

@areyouok
Copy link
Collaborator

areyouok commented Apr 30, 2018

If you have 2 beans implements same method in same interface, and want to seperate them in different Cache, you can add @Cached annotation on the concrete method with different name attribute.

@amjad1210
Copy link
Contributor Author

amjad1210 commented Apr 30, 2018

This is exactly what i needed. Could you possibly tell me which class in Jetcache the generateCacheName is declared inside? I think the issue i have is related to method.getDeclaringClass().getName() as the superclass methods i'm annotating are creating the key using the superclass name instead of the subclass.

public abstract class SuperClass {

    @Cache
    public void test() {
        System.out.println("Hello world");
    }

}
public class Subclass extends SuperClass {
}

When calling the method test from Subclass i'm expecting the cache name to be Subclass.test() but instead it's SuperClass.test().

@areyouok
Copy link
Collaborator

areyouok commented Apr 30, 2018

The method is passed to the org.aopalliance.intercept.MethodInterceptor(which is JetCacheInterceptor in jetcache) instance by Spring AOP. If you annotate on an abstract class, the method may be SubClass.test(). If you annotate on an interface, the method may be IntfaceClass.test().

However, you can seperate Cache explicitly using these code:

public abstract class SuperClass {
    public void test() {
        System.out.println("Hello world");
    }
}

public class Subclass1 extends SuperClass {
    @Cache(name="c1")
    public void test() {
        super.test();
    }
}

public class Subclass2 extends SuperClass {
    @Cache(name="c2")
    public void test() {
        super.test();
    }
}

Annotations with same area and name will point to same Cache instance.

@amjad1210
Copy link
Contributor Author

amjad1210 commented Apr 30, 2018

Hey @areyouok thank you for all the help. The issue is I didn't want to resort to the solution you posted above as i'd have to write boiler plate code for multiple sub classes. Would there be a way to achieve my solution in a more efficient way such as letting the abstract class @Cache annotation figure out the key using the subclass name?

@areyouok
Copy link
Collaborator

I think you need fork jetcache and modify these methods or class:

  • JetCacheInterceptor.invoke (add invocation.getThis() to CacheInvokeContext)
  • CacheInvokeContext
  • CacheContext.createCacheInvokeContext
  • CacheContext.createCacheByCachedConfig

@amjad1210
Copy link
Contributor Author

I've created #47 to solve this problem. It now takes the target class which the method was executed from as the className value.

@areyouok
Copy link
Collaborator

areyouok commented May 4, 2018

I had implement it on master branch. You can build it youself or use 2.5.0-SNAPSHOT:

    <repositories>
        <repository>
            <id>sonatype-nexus-snapshots</id>
            <name>Sonatype Nexus Snapshots</name>
            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

To supply you own NameGenerator, use this if you are using spring boot:

    @Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider(){
                public CacheNameGenerator createCacheNameGenerator(String[] hiddenPackages) {
                    return new YourCacheNameGenerator(........){.......};
                }
        }
    }

Without spring boot, invoke globalCacheConfig.setConfigProvider when you construct globalCacheConfig.

If no problem occurs I will release 2.5.0 in next few days.

@amjad1210
Copy link
Contributor Author

Hi @areyouok thank you very much! I'll give this a test and get back with any issues!

@amjad1210
Copy link
Contributor Author

amjad1210 commented May 5, 2018

@areyouok I've tested the changes and it's working as expected. I've also created #51 as i want to reuse most of the DefaultCacheGenerator functionality and need access to the fields. Other than that everything is perfect! Thank you.

For anyone with the same issue here's a full example with @areyouok solution.

    @Bean
    public SpringConfigProvider springConfigProvider() {
        return new SpringConfigProvider() {
            public CacheNameGenerator createCacheNameGenerator(String[] hiddenPackages) {
                return new RedisCacheNameGenerator(hiddenPackages);
            }
        };
    }
public class RedisCacheNameGenerator extends DefaultCacheNameGenerator {

    public RedisCacheNameGenerator(String[] hiddenPackages) {
        super(hiddenPackages);
    }

    @Override
    public String generateCacheName(Method method, Object targetObject) {
        String cacheName = cacheNameMap.get(method);

        if (cacheName == null) {
            final StringBuilder sb = new StringBuilder();

            String className = targetObject.getClass().getName();
            sb.append(ClassUtil.getShortClassName(removeHiddenPackage(hiddenPackages, className)));
            sb.append('.');
            sb.append(method.getName());
            sb.append('(');

            for(Class<?> c : method.getParameterTypes()){
                getDescriptor(sb, c , hiddenPackages);
            }

            sb.append(')');

            String str = sb.toString();
            cacheNameMap.put(method, str);
            return str;
        }

        return cacheName;
    }

}

@amjad1210 amjad1210 reopened this May 5, 2018
@areyouok
Copy link
Collaborator

areyouok commented May 6, 2018

PR 51 is merged and 2.5.0 is released.
See https://github.com/alibaba/jetcache/releases/tag/v2.5.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants