Face tangent (Dynamic proxy)

Source: Internet
Author: User

Turn Http://blog.163.com/[email protected]/blog/static/105107513201442101336680/
   download Lofter My photo book   |  first: The idea of object-oriented programming is to divide functional requirements into different, relatively independent classes, and let them own their own behavior, It also relies on inheritance and polymorphism to define each other's relationships. The aspect-oriented programming idea is that you want to be able to separate common requirements functions from irrelevant classes, enabling many classes to share the same behavior, and once the behavior changes, you don't have to modify a lot of classes, just one place.   For example, many programs need to log logs to track the execution of the method, usually in each aspect, the beginning of the printing of a log, the end of the time to print a log, it is obvious that if there are n multiple methods, you have to write n many of this print statement, at the same time, If you want the format of the printed statement to change, then you need to modify all the places.   Here, aspect-oriented programming is very useful, through the use of proxy mode, we can in the proxy object, the first to add print logs, and then execute the business logic method calls,   java has two proxy modes, static and dynamic, The difference is that the compiler knows who the object is to proxy. Static proxy, is an interface, a proxy class, if you need to proxy the interface too much, obviously this time-consuming and power.  java Dynamic Agent, is based on the implementation of the interface, otherwise, you can only use Cglib to do dynamic agent. The code is as follows:  JDK-based dynamic agent: Package Pattern.practise;import Java.lang.reflect.method; import Java.lang.reflect.proxy;import java.util.arrays;interface IPrint { public void print ();} Class Business1 implements IPrint {  @Override  public void print () {  System.out.println ("Business1 do Print "); }  @Override  public String toString () {  return" Business1 ";  }}class Business2 Implements IPrint {  @Override  public VOID print () {  System.out.println ("Business2 do print"); }  @Override  public String toString () {   return "BUSINESS2";  }}/** * proxy class  * @author Mguo * */public class Logpoxy implements Java.lang.reflect.InvocationHandler { //object to be dynamically proxied  private objects target = null; public Logpoxy (object Target) {  this.target = target; }  @Override  public object Invoke (Object proxy, method, Object[] args) throws Throwable {  System.out.println (target + "begin to executing" + method.getname () + "with Arg S "+ arrays.tostring (args));  Object result = Method.invoke (target, args);  System.out.println (target +" end t o Executing "+ method.getname ());  return Result; } public object GetProxy () { //Get proxy object  //This section Two parameters, which means which interfaces need to be proxied, that is, they bind the interface, so the dynamic agent is based on the interface, and cglib compensates for this defect   return Proxy.newproxyinstance (Target.getclass (). getClassLoader (), Target.getclass (). Getinterfaces (), this); &NBSP;}  public static void Main (string[] args) {  logpoxy logpoxy = new Logpoxy (new Business1 ());  IPrint Business 1 = (IPrint) logpoxy.getproxy ();  business1.print (); }}   We use the Open source CGLib class library to proxy classes without interfaces, which makes up for the lack of JDK. CGLib Dynamic proxy class This is how to play: 01Public class Cglibdynamicproxy implements Methodinterceptor {02  03private static Cglibdynamicproxy instance = new Cglibdynamicproxy ();04  05Private Cglibdynamicproxy () {06    }07  08Public static Cglibdynamicproxy getinstance () {09return instance;10    }11  12@SuppressWarnings ("unchecked")13Public <T> T getproxy (class<t> cls) {14return (T) enhancer.create (CLS, this);15    }16  17@Override18Public Object Intercept (object target, Method method, object[] args, Methodproxy proxy) throws Throwable {19before ();20Object result = Proxy.invokesuper (target, args);21stAfter ();22return result;23    }24  25private void before () {26System.out.println ("before");27    }28  29private void After () {30System.out.println ("after");31    }32 }The Singleton mode in the above code makes it easier for client calls: 1Public class Client {2  3Public static void Main (string[] args) {4Greeting greeting = cglibdynamicproxy.getinstance (). GetProxy (Greetingimpl.class);5Greeting.sayhello ("Jack");6    }7 }So far, all we can do, the problem seems to have been solved.     a very good article on the Web: &NBSP;AOP oriented aspect programming AOP (aspect-oriented programming, tangent-oriented programming), It is a technology that can dynamically and uniformly add functionality to a program without modifying the source code, by pre-compiling and running-time dynamic proxy implementations.   It is a new methodology that complements traditional OOP programming. OOP is concerned with dividing the requirements function into different and relatively independent, well-encapsulated classes, and letting them have their own behavior, relying on inheritance and polymorphism to define each other's relationships; AOP is the ability to separate common requirements functions from unrelated classes, enabling many classes to share a single behavior, once changed   , you do not have to modify many classes, but you only need to modify this behavior. AOP is the use of facets (aspect) to modularize crosscutting concerns, and OOP is the use of classes to modularize state and behavior. In the world of OOP, programs are organized through classes and interfaces, and it is appropriate to use them to implement the core business logic of the program. However, it is difficult to achieve crosscutting concerns (functional requirements spanning multiple modules of the application), such as logging and validation.  /* Calculator Interface */public interface calculator{    Public double Add (double NUM1, double num2) throws Exception;&nb Sp   Public Double sub (double NUM1, double num2) throws exception;    public double div (double num1, double nu m2) throws exception;    public double mul (double num1, double num2) throws Exception;}   /* Calculator Interface Implementation Class */public class Arithmeticcalculator implements calculator{    @Override     Public double Add (double NUM1, double num2)     {    &NBSP   Double result = Num1 + num2;        return result;   }     @Override & nbsp   Public Double sub (double NUM1, double num2)     {        double result = num1-num2;& nbsp       return result;   }    /* Schematic code temporarily does not consider the case of divisor 0 */    @Override   &N Bsp Public double div (double num1, double num2)     {        Double result = num1/num2;  & nbsp     return result;   }     @Override     public double mul (double num1, DOUBL E num2)     {        Double result = NUM1 * num2;        return RESULT;&N Bsp  }}  Most applications have a common need to keep track of what is happening while the program is running. In order to add the log function to the computer, the Arithmeticcalculator class changes as follows:  /* Calculator Interface Implementation class, add logging function */public class Arithmeticcalculator implements calculator{    @Override     Public double Add (double nuM1, double num2)     {        SYSTEM.OUT.PRINTLN ("The method [Add ()]" + "begin with Args (" +num 1+ "," +num2+ ")");        Double result = Num1 + num2;        System.out.println ("th e method [Add ()] "+" End With result ("+result+");                return ResU lt;   }     @Override     public double sub (double NUM1, double num2)     {&NB Sp       SYSTEM.OUT.PRINTLN ("The method [sub ()]" + "begin with Args (" +num1+ "," +num2+ ")");        Double result = num1-num2;        SYSTEM.OUT.PRINTLN ("The method [sub ()]" + "End With result (" +result+ ")";                return result;   }   &nbsp ; /* Schematic code temporarily does not consider the case of divisor 0 */    @Override     public double div (double num1, double num2)     {        System.out.println ("The method [Div ()]" + "begin with Args (" +num1+ "," +num2+ ")");        double result = num1/num2;        SYSTEM.OUT.PRINTLN ("The method [Div ()]" + "End With result (" +result+ ")");                return result;   }     @Override   &NB Sp Public double Mul (double num1, double num2)     {        SYSTEM.OUT.PRINTLN ("The method [Mul ( )] "+" begin with Args ("+num1+", "+num2+") ");        Double result = NUM1 * num2;      &N Bsp System.out.println ("The method [Mul ()]" + "End With result (" +result+ ")");            & nbsp   Return result;   }}  if the Arithmeticcalculator stipulates that only positive numbers can be calculated, you need to add the parameter verification method:  /* The implementation class of the Calculator interface, add the logging function */ public class Arithmeticcalculator implements calculator{    @Override     Public double add (double num 1, double num2) throws exception    {        This.argsvalidatior (NUM1);        This.argsvali Datior (num2);                 /* Ibid */   }     @Override     Public double sub (double NUM1, double num2) throws exception    {      &NB Sp This.argsvalidatior (NUM1);        This.argsvalidatior (num2);                  /* Ibid */   }    /* Schematic code temporarily does not consider the case of divisor 0 */    @Override &nb Sp   Public Double div (double num1, double num2) throws exception    {        This.argsval Idatior (NUM1);        This.argsvalidatior (num2);                 /* Ibid */   }     @Override     public double mul (double NUM1, double num2) throws exception    {        This.argsvalidatior (NUM1);        This.argsvalidatior (num2);  & nbsp            /* Ibid */   }        private void Argsvali Datior (double arg) throws exception    {        if (Arg < 0)         &NB Sp   Throw new Exception ("parameter cannot be negative");   }}  The above program a very intuitive feature is that there are a lot of duplicated code, and when adding more and more non-business requirements (such as logging and parameter Validation), The original Calculator method has become bloated and lengthy. There is a very painful thing to do, and it is not possible to use the original programming methods to get them out of the core business.  For example, logging and parameter validation, AOP calls them crosscutting concerns (crosscutting concern), and their system-wide requirements often need to span multiple modules. In the case of modular crosscutting concerns that cannot be idealized with traditional object-oriented programming, programmers cannot help but be able to place these crosscutting concerns in each of the modules intertwined with the core logic, which will cause crosscutting concerns to exist everywhere in each module. Using a non-modular approach to achieve crosscutting concerns will result in code clutter, code fragmentation, and code duplication. Think about it. If the log records need to be displayed in a different way, how much code you want to change, and if you miss one (high probability), it will cause the log record to be inconsistent. This kind of code is very maintainable.  A variety of reasons indicate that the module needs only to focus on its own functional requirements and need a way to extract the crosscutting focus punch module. The endurance of the Daniel proposed AOP, it is a concept, a specification, itself does not set specific language implementation, it is this feature makes it very popular, now there are many open-source AOP implementation framework. This is not an introduction to these frameworks, we will not use these frameworks, but instead use the underlying code to implement the most basic AOP to solve the above example problems. AOP is actuallyThe continuation of the design pattern of gof, the design pattern pursues the decoupling between the caller and the callee, and AOP can be said to be an implementation of this goal. AOP can be implemented using the "proxy mode". The principle of   proxy mode is to use a proxy to wrap the object, and then replace the original object with the proxy object, and any call to the original object must first be proxied. The proxy object is responsible for deciding whether and when to forward the method call information to the original object. At the same time, the proxy object can perform some extra work around the invocation of each method.   You can see that the proxy mode is ideal for implementing crosscutting concerns. Because I only know Java, so I think there are two ways to implement the proxy mode, one is static proxy, and the other is a dynamic agent. The difference is that they know who the agent is at compile time. In the more modular system, the static proxy is not appropriate and very inefficient, because the static proxy needs to specifically for each interface to design a proxy class, the system is relatively large and hundreds of interfaces is normal, static proxy mode is too labor-intensive. The dynamic agent is the proxy mode supported by the JDK, and it can implement the crosscutting concerns very well.  /* using dynamic proxy requires implementing Invocationhandler interface */public class Arithmeticcalculatorinvocationhandler implements invocationhandler{   /* objects to be proxied, dynamic proxies only know who the agent is at run time, so it is defined as object type, can proxy arbitrary object */    Private Object target = null;        * Pass through the constructor to the original object */    public Arithmeticcalculatorinvocationhandler (Object target)     {        This.target = target;& nbsp  }    /*invocationhandler interface method, proxy represents the agent, method indicates that the original object is called methods, args represents the method parameters */    @ override    Public Object Invoke (object proxy, Methodmethod, object[] args)             throws throwable    {        /* Process log information before the original object method call */        SYSTEM.OUT.PRINTLN ("The Method [" +method.getname () + "]" + "begin with Args ( "+arrays.tostring (args) +") ");                Object result = Method.invoke ( This.target, args);               /* Raw Object method call post processing log information */      &N Bsp System.out.println ("The Method [" +method.getname () + "]" + "End With result (" +result+ ")");                 return result;   }       /* Get proxy class */    pub Lic Object GetProxy ()     {        return proxy.newproxyinstance (This.target.getClass (). getClassLoader (), This.getclass (). Getinterfaces (), this);   }}  scene class Call:  public class client{     public static void main (string[] ARGS) throws exception    {       /* Get agent */        Calculator ARITHMETICC Alculatorproxy = (Calculator) New Arithmeticcalculatorinvocationhandler (                 new Arithmeticcalculator ()). GetProxy ();        /* Call the Add method */      & nbsp Arithmeticcalculatorproxy.add (Ten);   }}  Console output: The method [Add]begin with Args ([10.0, 10.0]) the method [Add]end with result (20.0) can see that the crosscutting concerns are implemented using dynamic proxies.   If you need to add a parameter verification function, you only need to create a parameter verification Agent:  public class Arithmeticcalculatorargsinvocationhandler implements        invocationhandler{   /* The object to be proxied, the dynamic agent only knows who the agent is at runtime, so it is defined as object type and can proxy arbitrary objects */    Private object target = null;        * pass through constructor to original object */    public Arithmeticcalculatorargsinvocationhandler (Object target)     {        This.target = target ;   }    /*invocationhandler interface method, proxy represents the agent, method indicates that the original object is called, and args represents the method's parameter */    @Override     Public object invoke (object proxy, method, object[] args)             TH Rows throwable    {        SYSTEM.OUT.PRINTLN ("Begin valid method [" +method.getname () + "] Wi th args "+arrays.tostring (args));                for (Object Arg:args)         {            This.argvalidtor ((Double) arg);        }                Object result = Method.invoke (This.target, args);  &nb Sp             return result;   }       /* Get proxy class */&nb Sp   Public Object GetProxy ()     {        return proxy.newproxyinstance ( This.target.getClass (). getClassLoader (), This.tarGet.getclass (). Getinterfaces (), this);   }        private void Argvalidtor (double Arg) throws exception    {        if (Arg < 0)             TH Row new Exception ("parameter cannot be a negative number!") ");   }}  scene class Call:  public class client{    public static void main (string[] args) throws Excep tion    {       /* Get agent */        Calculator arithmeticcalculatorproxy = (Calculator) New Arithmeticcalculatorinvocationhandler (                 new Arithmeticcalculator ()). GetProxy ();                Calculator Argvalidatorproxy = (Calculator) new Arithmeticcalculatorargsinvocationhandler (arithmeticcalculatorproxy). GetProxy ();        /* Call the Add method */        Argvalidatorproxy.add (ten, Ten);   } }  console output: Begin valid metHod [Add] with args [10.0, 10.0]the method [Add]begin with Args ([10.0, 10.0]) the method [Add]end with result (20.0) Enter a negative number Data:  public class client{    public static void main (string[] args) throws exception    {  &N Bsp    /* Get agent */        Calculator arithmeticcalculatorproxy = (Calculator) New Arithmeticcalculatorinvocationhandler (                 new Arithmeticcalculator ()). GetProxy ();                Calculator Argvalidatorproxy = (Calculator) new Arithmeticcalculatorargsinvocationhandler (arithmeticcalculatorproxy). GetProxy ();        /* Call the Add method */        Argvalidatorproxy.add ( -10, ten);    }}  Console Output:  begin valid method [Add] with args [ -10.0, 10.0]exception in thread "main" java.lang.Exception: parameter cannot be negative Number!     at Com.beliefbetrayal.aop.ArithmeticCalculatorArgsInvocationHanDler.argvalidtor (arithmeticcalculatorargsinvocationhandler.java:46)     at Com.beliefbetrayal.aop.ArithmeticCalculatorArgsInvocationHandler.invoke ( arithmeticcalculatorargsinvocationhandler.java:29)     at $Proxy 0.add (Unknown Source)     at Com.beliefbetrayal.aop.Client.main (client.java:14)      don't know if you've ever used Struts2, This structure and Struts2 is very similar to the interceptor, one action object is like our original object business core, one interceptor like the agent here, the general function to implement into interceptors, so that action can be shared, Struts2 interceptor is also an excellent implementation of AOP.      there is a more NB introduction: http://my.oschina.net/huangyong/blog/161338

Facet-oriented (dynamic proxy)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.