• Save
Java 1.5 - whats new and modern patterns (2007)
Upcoming SlideShare
Loading in...5
×
 

Java 1.5 - whats new and modern patterns (2007)

on

  • 1,894 views

Presentation of all the new stuff in Java 1.5 from 2007.

Presentation of all the new stuff in Java 1.5 from 2007.

Statistics

Views

Total Views
1,894
Views on SlideShare
1,894
Embed Views
0

Actions

Likes
3
Downloads
0
Comments
1

0 Embeds 0

No embeds

Accessibility

Upload Details

Uploaded via as OpenOffice

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • good one
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Java 1.5 - whats new and modern patterns (2007) Java 1.5 - whats new and modern patterns (2007) Presentation Transcript

  • JDK 1.5 and modern patterns Peter Antman, CTO, 2007 Mogul
  • Whats on in the Java space?
    • A refresher on JDK 1.5
      • Although old – not yet fully utilized
      • The base to using J2EE effectively
      • New Language Feature, new libraries, new methods, new runtime
    • Some important new patterns
      • POJO
      • Interceptors
      • Dependency Injection/IoC
      • AOP
  • New features in JDK 1.5
    • Generics
    • New for loop
    • Autoboxing
    • Varargs
    • Enums
    • Static import
    • Annotations
    • New concurrency libraries
    • New stuff in java.lang.System, java.util.*
    • New management API and tools (JMX)
    • Better runtime (gc)
    • And more...
  • Generics – stronger typing
    • Generics: a new language feature that makes Java more strongly typed
    • Makes it possible to write classes that handles any type logically, but bind it to a specific type when programming
    public interface List<E> extends Collection <E> {}
    • <E> - whats that?
    • A type parameter
    • Generics is parameterized classes (and methods)
    • Much like you may create new class instances with different values at runtime
      • ToBe notToBe = new ToBe(false);
    • You can create a specialized typed instance programmatically
      • List<String> myStrings = new List<String>();
  • Generics and Collections
    • Most widely used with Collections
    • No more typecasts
    • Only use arrays for performance critical work
    • Cleaner code for simple cases
  • Generics – compile time check
    • Old runtime checked code
    List strings = new ArrayList(); strings.add(new Integer(1));// Ok String s = (String)strings.get(0);// Runtime error
    • New safe code
    List<String> strings = new ArrayList<String>(); strings.add(new Integer(1));// Compile error String s = strings.get(0);// No typecast
  • Generics - map
    • Iterating a Map of Maps – non generic
    Iterator it = topMap.entrySet().iterator(); while(it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); Iterator itSub = ( (Map) e.getValue()).entrySet().iterator(); while(itSub.hasNext()) { Map.Entry eSub = (Map.Entry) itSub.next(); String k = (String) eSub.getKey(); } }
    • Iterating a Map of Maps – generic
    Iterator <Map.Entry<String, Map<String, String>>> it = topMap.entrySet().iterator(); while(it.hasNext()) { Map.Entry <String, Map<String, String>> e = it.next(); Iterator <<Entry.Set<String, String>> itSub = e.getValue().entrySet().iterator(); while(itSub.hasNext()) { Map.Entry <String, String> eSub = itSub.next(); String k = eSub.getKey(); } }
  • Generics – erasure
    • Generics works by erasure
    • The generic type informations is lost in byte code
    • Source code/Byte code roundtripping not possible
    List<String> strings = new ArrayList<String>(); strings.add(&quot;hello&quot;); String h = strings.get(0);
    • Becomes
    ArrayList arraylist = new ArrayList(); arraylist.add(&quot;hello&quot;); String s = (String)arraylist.get(0);
  • Generics – defining a class
    • Creating a generic interface
    interface Factory<P> { P create(); }
    • Creating a generic class
    static class FactoryImpl<T> implements Factory<T> { FactoryImpl(Class<T> c) {...}; public T create() {...} } Factory<My> f1 = new FactoryImpl<My>(My.class); My m1 = f.create();
  • Generic methods
    • Possible to make methods generic
    • Done by prepending a type parameter
    • Lets redo javax.rmi.PortableRemoteObject
    public class PortableRemoteObject { static <T> T narrow(Object o, Class<T> t) {...} }
    • The type is inferred during call
    My m2 = narrow(new My(), My.class);
    • When inferred type is not clear one may have to help, calling
    static <T> List<T> trouble(T t1, T t2)
    • like this:
    List<Object> lo1 = GenericsTest. <Object> trouble(new Integer(1), &quot;hello&quot;); //Must be a “.” before!
  • Generics – inheritance
    • Inheritance is a little bit surprising
    • Parameterizing a class with “compatible” types does not make the classes type compatible
    List<Integer> does NOT inherit List<Number>
    • runtime typing system not the same as compile time
    List<Number> ln = new ArrayList<Number>(); List<Integer> li = new ArrayList<Integer>(); ln.getClass().isInstance(li);// TRUE!
    • instanceof does not work with generics because of erasure
    li instanceof List<Number> is not valid
  • Generics – inheritance example
    • List<Number> ln = new List<Number>();
    • Can put both Integer and Double
    • ln.add(new Double(3.14));
    • List<Integer> li = new List<Integer>();
    • May only take Integer and subclasses, not Double
    • ln = new List<Integer>(); // Compile error
    • Not OK, Integer can not risk being a Double
  • Generics – get principle
    • Inheritance of generics classes is done with the extends and super wildcards
    • To be able to read from an inheritable generic class one must use the extends wildcard
    • <? extends T> says that we can use any instance of the generic class that has been instantiated with the typ T or any subtypes of T
    class Reader<S> { S read(Reader<? extends S> r) { return r.read(); }
    • Without this Reader<My> would only be able to read other Reader<My> instances. With extends it may also read for example Reader<MySub>
  • Generics – put principle
    • To be able to put stuff into a “inherited” generic type one has to use the super wildcard
    • <? super T> say that any type that is a parent to T used to parameterized a generic class will make that class a subtype of the declaring class
    • Look at List<? super Number> . What is List<Object>?
    • Since Object is a supertype of Number List<Object> is a subtype of List<? super Number>!
    • When writing the limiting factor is the type to write (the thing you write into must be same type or a parent)
    static class Writer<D> { void write(Writer<? super D> w) { w.write(); }
    • Without this Writer<My2> would not be able to write to a Writer<My>
  • Generic – more complicated stuff
    • Wildcard capture
    • Wildcard not allowed at top level during instance creation
    • Bounded type variables
    • Multiple bounds
    • Unbounded generics
    • Arrays and generics
    • Exceptions
  • New rule when overriding
    • Old rule: a subclass can only override a method if it has the exact same signature
    • To override public Object clone() one had to do it like this
    class Person { public Object clone() {} } Person pc = (Person) person.clone();
    • New rule: the return type may be a subtype of the type returned by the overridden method. Its now legal to do this:
    class Person { public Person clone() {} } Person pc = person.clone();
  • Autoboxing
    • Java typesystem
      • primitives which are NOT objects: int, byte, boolean...
      • Objects inheriting from java.lang.Object
      • Primitives have no common parent
  • Autoboxing - primitives Primitives does not work well with Collections or any other class that work on Object void visit(String page) { int val = 0; if (stats.containsKey(page)) { val = ((Integer) stats.get(page)).intValue(); } Integer newVal = new Integer(++val) ; stats. put (page, newVal); }
  • Autoboxing - example
    • Autoboxing means that the compiler automatically wraps and unwraps primitives to and from their Object counterparts
    Integer[] is = new Integer[]{1,2,3};
    • An example
    static protected void visitAuto(String page) { if (!map.containsKey(page)) { map.put(page, 0); } map.put(page, map.get(page) + 1);
    • Be aware of null pointer
    Map<Integer> ints = new HashMap<Integer>(); int i = ints.get(“no”);// NullPointerException
  • Autoboxing – problems
    • Some compile check go unnoticed. Whats wrong with this class?
    public class Conference { private Collection delegates = new ArrayList(); public void add(String... names) { Collections.addAll(delegates, names); } public void removeFirst() { delegates.remove(0); } public String toString() { return &quot;Conference &quot; + delegates; } public static void main(String[] args) { Conference sun_tech_days = new Conference(); sun_tech_days.add(&quot;Herman&quot;, &quot;Bobby&quot;, &quot;Robert&quot;); sun_tech_days.removeFirst(); System.out.println(sun_tech_days); } }
  • Varags – variable length arguments
    • Variable length arguments added
    Object method(Object... o); method(“h”, “e”, “l”, “l”, “o”);
    • Must be last in argument list
    Object method(String n, Object... o);
    • Is actually an array
    Object method(new Object[] {“H”, “E”});
    • Empty invoke same as null
    Object method();
    • Works with generics
    static <T> T method(T... t);
    • Be aware of inferrence problems
    Object o = VaragsTest.<Object>method();
  • Nice new method
    • Arrays.asList: public static <T> List <T> asList(T... a)
    • Before with had to initialize lists like this:
    List l = new ArrayList(); l.add(new Integer(1));
    • Now we do it like this
    List<Integer> ints = Arrays.asList(1, 2, 3); List<Double> ds = Arrays.asList(3.2, 4.5); List<String> ss = Arrays.asList(“by”, by”);
  • Enums
    • Enumerated types introduced to be used for constants
    • Constants mostly used to look like this:
    public static final int SEASON_WINTER = 0;
    • Not type safe
    • Requires recompilation of all using classes when changed
    • Not nice to print
    • Lot of work to implements ones own enums
  • Enum - features
    • Easy to declare
    enum Rgb {RED, GREEN, BLUE};
    • Nice to print
    System.out.println(Rgb.RED); -> RED
    • May be used in switch statements
    Rgb r = Rgb.RED; switch(r) { case RED: System.out.println(&quot;OK&quot;); break; default: System.out.println(&quot;NO&quot;); }
    • Use EnumSet and EnumMap when working with collections
  • foreach lop
    • A new foreach loop called for
    • Iterates over Iteratable, enums and arrays
    • Avoids local variables
    List<Integer> ints = Arrays.asList(1,2,3); for (int i: ints) System.out.println(&quot;I: &quot; + i); int[] ints2 = new int[]{1,2,3}; for (int i: ints2) System.out.println(&quot;I: &quot; + i); for(Rgb c: Rgb.values()) System.out.println(&quot;C: &quot; +c);
  • Static import
    • Possible to import static variables from a class
    • Avoids antipattern that inherits static variable through interface
    • Use with caution
    Math.PI * 2 import static java.lang.Math.PI; PI * 2; Possible, but seldom recommended, to do wildcard import
  • Annotations
    • Annotations added
    • Metadata that can be attached to a package, class, interface, field, method, parameter, constructor or local variable
    • No inherent logic, only a placeholder for data
    • May be available in source code, byte code and at runtime
    • Is used by other tools and runtime components: these may change the behavior of a program
    • Typical usage:
      • generate code
      • generate documentation
      • generate deployment descriptor
      • read deployment info from class/at runtime,
      • add aspects
      • inject objects
  • Annotions - declaration
    • Annotations declarations consists of its name and one or more attributes ( not Bean style)
    • Attributes may have default values
    • The value() attribute is special
    • Attributes may only be primitives, String, Class, enums, annotations and arrays of these
    • Annotations always have parent java.lang.annotation.Annotation
    @interface MyAnnotation { String value(); String myDefault() default &quot;[Default Value]&quot;; }
  • Annotating annotation
    • Annotations may be annotated
    • Target tells where an annotation is valid
    • Retention tells where an annotation is available, i.e source, binary, runtime
    import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface MyAnnotation {
  • Using annotations
    • Annotation is a modifier, by convention placed before other modifier
    • Are compile time dependent on provider
    • Are not runtime dependent when not introspected
    • May be used together with package, class, interface, field, method, parameter, constructor and local variables
    @MyAnnotation(“value”) public class MyClass { @MyAnnotation(value = “value”) int var = ...; @MyAnnotation(“value”, myDefault=”noDefault”) public void method() @MyAnnotation(“value”) public MyClass(@MyAnnotation(“param”) String arg) {
  • Introspected annotations
    • Annotations may be introspected at runtime
    • If classes are not available they silently “disappear”
    @HiddenAnnotation(&quot;Ha&quot;) public static void main(final String[] args) throws Exception{ Method m = AnnotationTest.class. getMethod(&quot;main&quot;, String[].class); Annotation[] as = m.getDeclaredAnnotations() ; for (Annotation a: as) { if (a instanceof HiddenAnnotation){ System.out.println(&quot;Value=&quot; + ((HiddenAnnotation)a).value() ); } } }
  • New concurrency library
    • Doug Leas famous concurrency library has been incorporated into the sdk
    • Contains interfaces and core classes which makes it easier and safer to work with concurrency
    • Never write concurrent code without checking this library
    • Contains libraries to work with
      • Effective concurrent collections
      • Pools of Threads and other things
      • Concurrent collaboration
      • Asynchronous invocations
      • Advanced locking
      • Atomic operations on objects (not just ints)
  • Concurrent collection
    • First cut of Java “collections” was synchronized: eg Hashtable
      • Safe but ineffective
    • Second generation of Java collections was unsynchronized
      • Effective, but does not work with concurrent code
    • Either you have to guard your access to collections by writing your own synchronization
      • easy to get wrong
    • Or translate them to completely synchronized:
    Map safe = Collections.synchronizedMap(new HashMap());
    • Often problems with Iterators (not part of synchronization)
    • Failfast iterators: clone state before iterating
  • New concurrent collections
    • Not all or nothing
    • Uses new techniques and algorithms to handle different usage patterns effectively
    • Different strategies to make iterations safer
      • snapshot iterators
      • weakly consistent iterators
  • ConcurrentHashMap
    • Lock striping
    • Optimized for reads: does not block reads even when its updated
    • Does often not block on updates either
    • Weakly consistent iterators
    • Atomic check and take action methods
    V putIfAbsent(K key, V value); boolean remove(Object key, Object value); V replace(K key, V value); boolean replace(K key, V oldValue, V newValue);
  • ConcurrentHashMap - example
    • Old style
    synchronzied(map) { if(map.containsKey(key) map.put(key, value); }
    • New style
    concMap.putIfAbsent(key, value);
  • CopyOnWrite collections
    • CopyOnWriteArrayList and CopyOnWriteArraySet
    • Safe for concurrent operations but no synchronized reads
    • Use when mostly used with concurrent reads
    • Typical usecase: listeners
    • Costly to write to
    • Snapshot iterators, not possible to modify collection through them
  • Queue
    • Queue – holds element for processing in a queue specific order (priority, fifo, mm)
    • interface Queue
      • PriorityQueue
      • ConcurrentLinkedQueue
        • Weakly consistent iterator, wait-free, thread-safe
    • interface BlockingQueue
      • Producer/Consumer patterns
      • LinkedBlockingQueue – unbound by default
      • ArrayBlockingQueue – bounded queue
      • PriorityBlockingQueue – a blocking thread safe priority queue
      • DeleayQueue – entered items only available after a delay
      • SynchronousQueue – empty queue, rendevouz, two colaborating threads
  • BlockingQueue - example final BlockingQueue<String> bq = new LinkedBlockingQueue<String>(); Thread c = new Thread(new Runnable(){ public void run() { while(true) { try { String s = bq.take(); System.out.println(&quot;Took &quot; + s); } catch(InterruptedException e){} } } }); Thread p = new Thread(new Runnable(){ int counter = 0; public void run() { while(true) { try { bq.put(&quot;b&quot; + counter++); } catch(InterruptedException e){} } } }); c.start(); p.start();
  • Executor
    • Executes Runnable task according to implementation
    • Use instead of manually creating Threads
    • Not necessarily asynchronous
    • One method: void Excecute(Runnable);
    Executor exe = Executors.newFixedThreadPool(10); exe.execute(task);
  • ExecutorService
    • Extension to Executor
    • Provides management methods
    • May track submitted task and get result back
    • Uses Future and Callable
    Callable<String> worker = new Callable<String>() { int c = 0; public String call() { try {Thread.sleep(2000L);}catch(Exception ignore){} return &quot;jobb no &quot; + c++; } }; ExecutorService exe = Executors.newSingleThreadExecutor(); Future<String> result = exe.submit(worker); while(!result.isDone()) { // Do something other } String s = result.get();
  • Atomic objects
    • java.util.concurrent.atomic
    • volatile but with conditional atomicity
    • Finer granularity of locking (hardware based)
    • Non blocking
    • Built on optimistic assumptions: do it, check and redo
    • Non blocking counting
    AtomicLong at = new AtomicLong(); // one thread, no synchronzied long current = at.get(); // another thread increment, no sync at.incrementAndGet();
  • Locking
    • java.util.concurrent.locks
    • More flexible locks and mutex than builtin
    • Great to handle many reads, few writes
    ReadWriteLock locks = new ReentrantReadWriteLock(); public void read() { Lock readLock = locks.readLock(); readLock.lock(); try { // Do read, multiple threads allowed } finally { readLock.unlock(); } } public void write() { Lock writeLock = locks.writeLock(); writeLock.lock(); try { // Do write, only one thread } finally { writeLock.unlock(); } }
  • Other new stuff in java.util
    • UUID – universally unique identifier
      • UUID uuid = UUID.randomUUID();
    • Arrays.hashCode() and Arrays.deepHashCode()
    • Arrays.toString([] ) and Arrays.deepToString(Object[] )
    • Collections.reverseOrder(Comparator<T>)
    • Collections.addAll(Collection<T>, T... )
  • Other new stuff
    • java.lang.ProcessBuilder - to start and manage external processes
    • java.lang.StringBuilder – use instead of StringBuffer when no synchronization is needed
    • long n = System.nanoTime();
    • System.getEnv()
    • Thread.getStackTrace()
    • Thread.State s = t.getState ()
    • ThreadLocal.remove()
    • java.lang.instrument
  • JMX
    • javax.management (1.2) and remote (1.0) is part of the API
    • The JVM is managable with jmx both locally and remote
    • When managability is turned on one may use the builtin MbeanServer
    • Managability is turned on with system properties
    • Turn on local management:
    java -Dcom.sun.management.jmxremote MyServe r
    • Turn on remote management, no security
    java -Dcom.sun.management.jmxremote.port=4711 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false
    • Turn on remote management, password
    java -Dcom.sun.management.jmxremote.port=4711 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.password.file=FILE
  • jconsole
  • Getting the mbean server and managing an object   public static interface My { public String getMyName(); } public static class MyImpl implements My { String n; MyImpl(String s) { n = s; } public String getMyName(){return n;} } ArrayList servers = MBeanServerFactory.findMBeanServer(null); if (servers.size() > 0) { server = (MBeanServer)servers.get(0); StandardMBean b = new StandardMBean(new MyImpl(&quot;Peter&quot;), My.class); server.registerMBean(b, new ObjectName(&quot;test:name=Peter&quot;)); }
  • Manage the bean with jconsole
  • Better garbage collection algorithms
    • Better default gc
    • More detailed configuration
  • Important “new” patterns
    • Reaction against J2EE
    • Driven by two reactions
      • Small open source project
      • Head to head with .Net regarding web development
    • Lightweight
    • No dependencies on infrastructure
    • No infrastructure in code
    • Testability
  • POJO
    • POJO – Plain Old Java Object
    • Martin Fowler 2000
    • Concentrate on logic
    • No plumbing code
    • No framework dependencies
    • No special runtime requirements
    • Makes it easier to test
  • IoC
    • IoC = Inversion of Control
    • “Old” style – classes handles their dependencies for them self
      • creating a data base connection
      • looking up a properties file
      • Really hard when testing
      • DataSource ds = new DataSource();
    • EJB and Avalon first type 1 IoC containers
      • Dependencies are handled by container
      • Classes implements frameworks hooks and marker interfaces
      • Container make external resources available through these hooks
      • EJB: setSessionContext(SessionContext ctx)
      • Also hard to test: requires deployment in runtime environment
    public void service(ServiceManager sm) { ds = (DataSource)sm.lookup(“myDataSource”); }
  • IoC type 2
    • IoC type 2: setter injection
    • Must known container: Spring
    • A class declares setter for any external dependencies
    • These dependencies are handled by container
    • After object is created the dependencies are injected by invoking the correct set-methods
    • Much easier to test
    • Hard to know when an object is really “ready”, one often needs one or more life cycle callbacks!
    public void setDataSource(DataSource ds)
  • IoC – type 3
    • IoC type 3: constructor injection
    • Same as type 2, but objects always get their full dependencies at creation
    • No life cycle methods needed
    • Many dependencies lead to cluttered constructors
    • Optional dependencies harder to handle
    • Property type configuration not as straight forward
  • Dependency injection
    • IoC type 2 and 3 now has a common name
    • Dependency Injection
    • Martin Fowler 2004
    • From JDK 1.5 dependency injection primarily seems to be done with annotations
    • Several new types of injections introduced
      • attribute injection
      • thread/context injections
  • Dependency Injection - annotation @DataSource DataSource ds; MyClass(@DataSource ds) { @DataSource public void setDataSource(DataSource ds)
  • Interceptors
    • Interceptors have become popular
    • Based on the decorator pattern
    • Intercept call to a POJO and add framework logic
    • JBoss revolutionized the interceptor pattern in its EJB container
    • The servlet spec popularized it through the introduction of servlet filter
    • Plumbing logic is moved out from the POJO
    • Foundation laid with introduction of Proxy in JDK 1.3
    • Only possible on method level by default
  • Simple logging interceptor Object target; LoggingInterceptor(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(&quot;TRACE&quot;, &quot;Calling &quot; + method.getName()); return method.invoke(target, args); } My m = (My)Proxy.newProxyInstance(Thread.currentThread(). getContextClassLoader(), new Class[]{My.class}, new LoggingInterceptor(new MyImpl())); String s = m.hello();
  • ThreadLocalInjector
    • Lets finish this up with a thread local injector
    • We want to be able to have a class looking like this:
    interface My { String hello(); } class MyImpl implements My { public String msg; @InjectThreadLocal(name=&quot;msg&quot;, type=String.class) public String hello(){ return msg;} }
    • The logic is that a calling thread shall inject a thread local variable into the msg attribute
  • InjectThreadLocal annotation
    • We need an annotation
    @Retention(RetentionPolicy.RUNTIME) @interface InjectThreadLocal { String name(); Class type(); }
  • ThreadLocalInterceptor
    • And an interceptor
    • First part
    static class ThreadLocalInterceptor<T> implements InvocationHandler { Object target; Class targetClass; ThreadLocal<T> local; ThreadLocalInterceptor(Object target, ThreadLocal<T> local) { this.target = target; this.local = local; targetClass = target.getClass(); }
  • ThreadLocalInterceptor – part 2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Need to use real target class! Method targetMethod = targetClass. getDeclaredMethod(method.getName(), method.getParameterTypes()); InjectThreadLocal a = targetMethod. getAnnotation(InjectThreadLocal.class); if (a != null) { Field f = targetClass.getField(a.name()); if (f != null) { Object tl = local.get(); if (f.getType().isAssignableFrom(tl.getClass())) { f.set(target, tl); } } } return method.invoke(target, args); }
  • Inject in a stateless instance
    • We use a “stateless” instance
    Runnable r = new Runnable() { public void run() { while(true) { long id = Thread.currentThread().getId(); local.set(&quot;&quot; + id); String s = m.hello(); System.out.println(&quot;Thread &quot; + id + &quot; said hello: &quot; +s); } } }; Thread t1 = new Thread(r); t1.start(); Thread t2 = new Thread(r); t2.start();
  • Injection result
    • Output when run
    Thread 9 said hello: 9 Thread 9 said hello: 9 Thread 10 said hello: 10 Thread 9 said hello: 9 Thread 9 said hello: 9 Thread 10 said hello: 10 Thread 9 said hello: 9 Thread 9 said hello: 9 Thread 10 said hello: 10
  • AOP
    • AOP – Aspect Oriented Programming
    • Interceptors taken to a new layer
    • Cross Cutting Concerns
    • Not just method inteceptions
    • join point – where to join in
    • point cuts – pointer to join points
    • advice – aspect to join in
    • Much more...
  • mogul .slut på presentation Kontakt: [email_address]