Прокси -модель является обычно используемой моделью дизайна Java. Его характеристика заключается в том, что класс прокси и класс делегатов имеет одинаковый интерфейс. Класс прокси несет главным образом ответственность за предварительные сообщения, фильтрацию сообщений, пересылку сообщений в класс делегатов и обработку сообщений после события. Обычно существует связь между прокси -классом и классом делегатов. Объект прокси -класса связан с объектом класса делегатов. Сам объект класса прокси по -настоящему не реализует Сервис, но предоставляет конкретные услуги, вызывая соответствующие методы объекта класса делегата.
Сравнение различных динамических прокси -реализаций Java
интерфейс
Интерфейс addInterface {int add (int a, int b);} интерфейс subinterface {int sub (int a, int b);} Класс реализации
класс арифметические реализации addInterface, subinterface {@override public int sub (int a, int b) {return ab; } @Override public int add (int a, int b) {return a+b; }}Метод 1: Динамический прокси, встроенный в JDK
1. Метод реализации
Динамический прокси -механизм, введенный Java после JDK1.3, позволяет нам динамически создавать классы прокси во время выполнения. Внедрение AOP с использованием Dynamic Proxy требует четырех ролей: класс прокси, интерфейс прокси -класса, ткацкое устройство и InvocationHandler. Устройство ткачества использует механизм отражения интерфейса для генерации класса прокси, а затем вплетать код в этот класс прокси. Класс прокси - это цель, упомянутая в AOP. InvocationHandler - это раздел, который содержит советы и точка.
2. Реализация интерфейса VinvocationHandler
класс jdkdpqueryhandler реализует vocotichandler {private arithmetic Real; public jdkdpqueryhandler (арифметическая реальная) {this.real = real; } @Override public Object voloke (Object Proxy, Method Method, Object [] args) бросает Throwable {string methodname = method.getName (); System.out.println (метод); System.out.println («Метод:» + Methodname + »start, параметры:" + arrays.aslist (args)); Object result = method.invoke (real, args); System.out.println («Метод:»+methodname+»ends, результат:"+result); результат возврата; }}3. Создайте класс прокси и вызовите класс прокси
открытый класс Main {частный статический int a = 4, b = 2; Public Static Object CreateJDKProxy (Arithmetic Real) {Object Proxyarithmetic = proxy.newProxyInstance (real.getClass (). getClassloader (), Real.getClass (). getInterfaces (), new jdkdpqueryhandler (Real)); вернуть проксиарифметику; } public static void main (string [] args) {arithmetic Real = new Arithmetic (); Object Proxyarithmetic = createJdkProxy (Real); ((AddInterface) Proxyarithmetic) .Add (a, b); ((Subinterface) Proxyarithmetic) .sub (a, b); }}Метод 2: Генерация динамического байт -кода (CGLIB)
1. Метод реализации
Enhancer и Methodinterceptor. Enhancer может использоваться для динамического генерации класса, который может наследовать указанный класс и реализовать некоторые указанные интерфейсы. В то же время Enhancer должен указать обратный вызов, прежде чем генерировать класс. Когда метод класса вызывается, выполнение метода назначается этому обратному обращению. MethodInterceptor - это более широко используемый интерфейс, унаследованный от обратного вызова, и он имеет только одно объявление метода.
2. Сравнение интерфейса vlocationhandler (в JDK) и интерфейса MethodInterceptor (в CGLIB)
Метод публичного интерфейса Extends Callback {public Object Intercept (Object obj, java.lang.reflect.method Метод, объект [] args, methodproxy proxy) бросает бросание; } public interface vlocationhandler {public Object invoke (объект прокси, метод метода, объект [] args) бросает Throwable; } С точки зрения композиции параметров, входные параметры метода Interceptor на 1 больше, чем vocationHandler. Фактически, значение первых трех объектов параметров совпадает с значением vocationHandler.
Первый параметр указывает, от какого объекта метод вызова происходит;
Второй параметр представляет объект метода, который вызывает метод;
Третий параметр представляет список входных параметров для этого вызова;
Дополнительные параметры имеют тип метода, который должен быть объектом, сгенерированным CGLIB для замены объекта метода. Использование MethodProxy повысит эффективность, чем напрямую вызов собственного метода JDK.
3. осознавать 1
Реализация интерфейса метода Interceptor
Class cglibdpqueryInterceptor реализует MethodInterceptor {Private Arithmetic Real; public cglibdpqueryInterceptor (arithmetic Real) {this.Real = Real; } @Override public Object Intercept (цель объекта, метод метода, объект [] args, methodproxy proxy) бросает Throwable {String methodname = method.getName (); System.out.println (метод); System.out.println («Метод:» + Methodname + »start, параметры:" + arrays.aslist (args)); // объект result = method.invoke (real, args); // оба метода могут получить объект result = proxy.invoke (real, args); System.out.println («Метод:»+ methodname+ »ends, результат:"+ result); результат возврата; }}Создайте класс прокси и позвоните в класс прокси -сервера
открытый класс Main {частный статический int a = 4, b = 2; Public Static Object CreateCglibProxy (Arithmetic Real) {Enhancer Enhancer = new Enhancer (); Enhancer.SetCallback (новый cglibdpqueryInterceptor (Real)); Enhancer.SetInterfaces (real.getClass (). getInterfaces ()); return Enhancer.create (); } public static void main (string [] args) {arithmetic Real = new Arithmetic (); Object Proxyarithmetic = createCglibProxy (Real); ((AddInterface) Proxyarithmetic) .Add (a, b); ((Subinterface) Proxyarithmetic) .sub (a, b); }}Обратите внимание, что MethodProxy предоставляет 2 метода при выполнении функций.
Общедоступный объект invoke (Object obj, Object [] args) бросает бросаемый публичный объект infokesuper (Object obj, Object [] args) бросает бросание
Среди них Javadoc говорит, что этот метод inloke () может использоваться для выполнения других объектов в одном классе, то есть OBJ в этом методе должен передать в другой объект одного и того же класса (приведенный выше метод состоит в том, чтобы перейти в различные объекты арифметического класса), в противном случае он вступит в бесконечный реконсурсивный петлей (на самом деле на тестирование). Если вы тщательно подумаете об этом, вы обнаружите, что цель в общедоступном объектном перехвате (цель объекта, метод метода, объект [] args, methodproxy proxy) является реализованным прокси -объектом. Когда метод add () вызывается через цель, метод Intercept () будет вызван для вызова. Если Method.invoke (Target, Args) вызывается в методе Intercept (), он эквивалентен вызову метода add (), что приводит к бесконечному рекурсивному циклу. Но если вы выполняете Method.invoke (Real, Args), он не будет, потому что реальная и цель - это разные объекты в одном классе, Real - это настоящая логическая тема, а цель - прокси для реальной темы реальной.
Вот пример моделирования:
Интерфейс solveInterface {void solve ();} класс реальные реализации solveInterface {public void solve () {System.out.println ("Real Reolve!"); }} класс Target расширяет Real {Private Object obj; public void setObject (Object obj) {this.obj = obj; } private void Invoke () {try {method method = solveInterface.class.getMethod ("solve", new Class [] {}); method.invoke (obj, новый класс [] {}); } catch (Exception e) {e.printstackTrace (); }} public void solve () {System.out.println ("Target Reolve!"); Invoke (); }} открытый класс main {public static void main (string [] args) бросает исключение {target target = new Target (); target.setObject (new Real ()); // правильно // target.setObject (target); // циклический вызов происходит target.solve (); }}Фактически, метод метода inloke () будет вызывать соответствующий метод Solve () в соответствии с типом OBJ, то есть полиморфизм.
4. Реализуйте 2
Реализация интерфейса метода Interceptor
Class cglibdpqueryInterceptor реализует MethodInterceptor {@Override public Object Intercept (цель объекта, метод метода, объект [] args, methodproxy proxy) бросает Throwable {string methodname = method.getName (); System.out.println (метод); System.out.println (метод); System.out.println («Метод:» + Methodname + »start, параметры:" + arrays.aslist (args)); // Печать Информация класса: target.getClass (); Опустить объект result = proxy.invokesuper (target, args); System.out.println («Метод:»+methodname+»ends, результат:"+result); результат возврата; }}Создайте класс прокси и позвоните в класс прокси -сервера
открытый класс main {private static int a = 4, b = 2; public static object createcglibproxy () {Enhancer Enhancer = new Enhancer (); Enhancer.SetCallback (новый cglibdpqueryInterceptor ()); Enhancer.setSuperClass (arithmetic.class); return Enhancer.create (); } public static void main (string [] args) {arithmetic Real = new Arithmetic (); Object Proxyarithmetic = createCglibProxy (); ((AddInterface) Proxyarithmetic) .Add (a, b); ((Subinterface) Proxyarithmetic) .sub (a, b); }}Обратите внимание, что Enhancer не устанавливает интерфейс в реализации 2, поскольку устанавливается SuperClass (то есть родительский класс класса прокси является арифметикой), наш класс прокси унаследует его, и арифметика реализовала наш интерфейс. Чтобы доказать это, вы можете распечатать информацию класса Target.getClass () в методе перехвата метода Interceptor. Вы обнаружите, что родительский класс прокси -класса CGLIB отличается. следующее:
Реализация 1:
открытый класс com.test.arithmetic $$ EnhancerBycglib $$ 4FA786EB расширяет java.lang.object
Реализация 2:
открытый класс com.test.arithmetic $$ EnhancerBycglib $$ 4FA786EB расширяет com.test.arithmetic
Метод 3: Джавасист генерирует динамический прокси (создание фабрики агента или создание динамического кода)
Javassist - это структура для редактирования Bytecode, которая позволяет вам очень просто работать. Он может определить или изменить класс во время выполнения. Принцип реализации AOP с использованием Javassist заключается в том, чтобы напрямую изменять метод, который необходимо врезать до того, как будет загружен байт -код. Это более эффективно, чем использование CGLIB для реализации AOP, и нет никаких ограничений. Принцип реализации заключается в следующем:
Реализация 1:
Реализация интерфейсов
класс javassistdpqueryhandler реализует метод kandler {@override public object invoke (цель объекта, метод метода, метод прокси, объект [] args) бросает Throwable {String methodname = method.getName (); System.out.println (метод); System.out.println (метод); System.out.println («Метод:» + Methodname + »start, параметры:" + arrays.aslist (args)); Object result = proxy.invoke (Target, args); System.out.println («Метод:»+methodname+»ends, результат:"+result); результат возврата; }}Создайте класс прокси и позвоните в класс прокси -сервера
открытый класс main {private static int a = 4, b = 2; public static object createjavassistproxy () вызывает исключение {proxyfactory factory = new ProxyFactory (); factory.setsuperclass (arithmetic.class); factory.sethandler (new javassistdpqueryhandler ()); return factory.createClass (). newInstance (); } public static void main (string [] args) бросает исключение {arithmetic real = new arithmetic (); Object Proxyarithmetic = createJavassistProxy (); ((AddInterface) Proxyarithmetic) .Add (a, b); ((Subinterface) Proxyarithmetic) .sub (a, b); }}ПРИМЕЧАНИЕ. Определение метода Invoke в интерфейсе MethodHandler заключается в следующем:
Public Object Invoke (цель объекта, метод метода, метод прокси, объект [] args)
Метод представляет объект метода, который вызывает метод, прокси - это объект, который генерирует и заменяет метод классом Proxy. В противном случае, используя метод.invoke (Target, Args), генерирует бесконечный цикл.
Реализация 2:
Общий прокси -процесс использования динамического явасиста немного отличается от предыдущего метода. Внутри Javassist можно сгенерировать динамический код Java для генерации байт -кода. Динамический прокси, созданный таким образом, может быть очень гибким и может даже генерировать бизнес -логику во время выполнения.
// Пользовательский интерфейс Interceptor InterceptorDler { /*** Вызов метода динамического прокси -объекта будет отражать этот метод. Вы можете добавить AOP-подобные предварительные и после операции к реализации этого метода. Только если к этому телу метода добавлен следующий код* метод прокси -сервиса будет выполнен, а возвратное значение будет возвращено в прокси и, наконец, возвращен в программу* @param obj объект Proxy объект* @param Метод метода Proxy* @param Args объект объекта Praxy объекта @THORTIO obj, метод метода, объект [] args) бросает бросание; } // Реализация класса Interceptor InterceptorMlerImpl реализует InterceptorDler {@Override public Object invoke (Object obj, метод метода, Object [] args) бросает Throwable {String methodname = method.getName (); System.out.println (метод); System.out.println (метод); System.out.println («Метод:» + Methodname + »start, параметры:" + arrays.aslist (args)); Object result = method.invoke (obj, args); System.out.println («Метод:»+methodname+»ends, результат:"+result); результат возврата; }} класс myProxyImpl { / ** Имя класса суффикс динамического прокси -класса* / private final Static String proxy_class_name_suffix = "$ myproxy_"; / ** Интерфейс Interceptor*/ private final Static String Interceptor_Handler_Interface = "com.test.InterceptorDhandler"; / ** Индекс имени класса динамического класса прокси, чтобы предотвратить дублирование имен классов*/ private static int proxyclassIndex = 1; /*** Динамический прокси -интерфейс, подвергающийся воздействию пользователя, возвращает динамический прокси -объект определенного интерфейса. Обратите внимание, что реализация этого прокси должна использоваться с помощью com.cuishen.myaop.interceptordrhandler receptor*, то есть, если пользователь хочет использовать этот динамический прокси, он должен сначала реализовать интерфейс com.cuishen.myaop.interceptorhandler интерфейс* @paramflassname String. @param classtoproxy string Название класса класса реализации интерфейса для динамически прокси, eg test.studentinfoserviceimpl * @param interceptordlerimplclassnam AllodalAccessException * @Throws notFoundException * @Throws не может NotFoundException, невозможностькомпилеэкспений, ClassNotFoundException {Class InterfaceClass = class.forname (urfaceCalssName); Class receptormandlerimplclass = class.forname (InterceptorDlerIrmplassName); return DynamicImplessInterface (ClasStoproxy, Interfaceclass, InterceptorDlerlerImplClass); } /** * Dynamic implementation of the interface to be proxyed* @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param interfaceClass Class Interface class to be dynamically proxyed, eg test.StudentInfoService * @param interceptorHandlerImplClass Class Предоставленный пользователем класс реализации интерфейса Interceptor * @return Объект возвращает динамический прокси-объект определенного интерфейса * @Throws notFoundException * @Throws не ContompileException * @Throws instantiationException * @Throws allogalAccessExexexexection */ private statice dynamicimplementsInter-interscepr NotFoundException, невозможностькомпилеэкспений, instantiationException, allogalaccessexception {classpool cp = classpool.getDefault (); String interfacename = interfaceclass.getName (); // Динамично указать имя класса строки Proxy Class ProxyClassName = InterfacEname + proxy_class_name_suffix + ProxyClassIndex ++; // Имя пакета интерфейса, которое будет реализовано + Имя интерфейса string interfacenamepath = Interfacename; Ctclass ctinterface = cp.getctclass (Interfacenamepath); Ctclass cc = cp.makeclass (proxyclassname); cc.AddInterface (ctinterface); Method [] methods = interfaceclass.getMethods (); for (int i = 0; i <methods.length; i ++) {method method = method [i]; DynamicImplementsMethodsFromInterface (ClasStoproxy, CC, Method, InterceptorDlerRIMPlass, I); } return (object) cc.toclass (). newInstance (); } /*** Метод в динамической реализации метода* @param classtoproproxy String Название класса класса реализации интерфейса для динамического прокси, например, test.studentinfoserviceimpl* @param Реализация CTClass Метод Dynable -Methonide* @param Pparam Pparam pparam Pparam Pparam Pparam Pparam Pparam Pparam. Класс InterceptorClass. Класс реализации Interceptor, предоставленный пользователем* @param methodindex int index метода, который будет реализован* @Throws не concompileException*/ private static void dynamiCimplementsmethodsfromInterface (string clasStoproxy, ctclass inleviter, метод метод, класс intercepsclass, int methodIndexy). generatemethodcode (classtoproxy, methodtoimpl, receptorclass, methodindex); Ctmethod cm = ctnewmethod.make (метод -код, реализатор); refferer.addmethod (cm); } /*** Динамически собрать тело метода. Конечно, реализация метода в прокси не является простой копией метода, но отражает метод Invoke в перехватчике и передает полученные параметры* @param classtoproproxy String Название класса класса реализации интерфейса, чтобы быть динамичным прокси, EG Test.StudentinfoServiceImpl* @param метод, метод, метод, внедренная методическим методом. @Param InterceptorClass класс. Класс реализации Interceptor Interceptor* @param methodindex int Индекс метода, который будет реализован* @return String Строка метода динамической сборки*/ private static String Generatemethodcode (String classstoproxy, метод метода кедоризации, класс receptorclass, int methodindex) {string methodname methodmplass); String methodreturntype = methodtoImpl.getReturnType (). GetName (); Class [] parameters = methodtoImpl.getParameterTypes (); Class [] exceptionTypes = methodToImpl.getExceptionTypes (); StringBuffer ExceptionBuffer = new StringBuffer (); // Объявление исключения метода сборки if (exceptionTypes.length> 0) ExceptionBuffer.Append ("Throws"); for (int i = 0; i <excemantypes.length; i ++) {if (i! = exceptionTypes.length - 1) exception buffer.append (exceptionTypes [i] .getName ()). Append (","); else excectionbuffer.append (exceptionTypes [i] .getName ()); } StringBuffer parameterBuffer = new StringBuffer (); // Список параметров метода сборки для (int i = 0; i <parameters.length; i ++) {class parameter = параметры [i]; String parameterType = parameter.getName (); // Динамическое указание имени переменной параметра метода string refname = "a" + i; if (i! = parameters.length - 1) parameterbuffer.append (parametertype) .append (" + refname) .append (", "); else parameterbuffer.append (parametertype) .append (" + refname); } StringBuffer methodDeclare = new StringBuffer (); // Объявление метода, поскольку это метод, который реализует интерфейс, это публичный метод adeclare.append ("public") .append (methodReturntype) .append ("") .append (methodname) .append ("("). Append (parameterbuffer) .append (")"). Append (ExceptionBuffer). String receptorImplname = receptorClass.getName (); // MethodBody methoddeclare.append (receptor_handler_interface) .append ("receptor = new") .append (receptorimplname) .append ("();/n"); // Отражающий вызов пользователя интерфейса интерфейса MethodDeclare.append ("object returnobj = receptor.invoke (class.forname (/" " + classtoproxy +"/"). Newinstance (), class.forname (/" + classtoproxy + "/"). GetMethods () [/"/метод, метод,"//"/"/"/") if (parameters.length> 0) methoddeclare.append ("Новый объект [] {"); + i + ","); else methoddeclare.append ("null);/n"); // Оберните возвращаемое значение перехватчика вызова if (methodtoImpl.getReturnType (). Isprimity ()) {if (methodToImpl.getReturnType (). Equals (boolean.type)) methodDeclare.append («return ((boolean) returnobj) .booleanValue ();/n"); else if (methodtoImpl.getReturnType (). equals (integer.type)) methoddeclare.append ("return ((integer) returnobj) .intvalue ();/n"); иначе if (methodtoImpl.getReturnType (). equals (long.type)) methoddeclare.append ("return ((long) returnobj) .longvalue ();/n"); иначе if (methodToImpl.getReturnType (). equals (float.type)) methodDeclare.append ("return ((float) returnObj) .floatvalue ();/n"); else if (methodtoImpl.getReturnType (). equals (double.type)) methodDeclare.append ("return (" return ("return (" return ("return ();/n"); else if (methodToImpl.getReturnType (). Equals (Double.Type))) MethodDeclare.append ("return (" return ("return (" return ("return (" return ();/n "); else if (methodToImpl.getReturnType (). equals (double.type)) methoddeclare.append (" return ("return (" return ("return (" returntoImpler MethodDeclare.append ("return (" return ("return (" return ("return (" methodtoImpl.getReturnType (). equals (double.type))) methoddeclare.append ("return (" return ("return (" return ("methodToImpl.getRetRurntype (). methodDeclare.append ("return (" return ("return (" return ("methodtoImpl.getReturnType (). ровный ((двойной) returnObj) .doublevalue ();/n"); els ((Символ) returnobj) .charvalue ();/n "); else if (methodtoImpl.getReturnType (). Equals (byte.type))) methoddeclare.append (" return ((byte) returnobj) .bytevale ();/n "); else if (methodToImpl.getRepRENTYPE (). methodDeclare.append ("return ((byte) returnobj) .bytevalue ();/n"); MethodTurntype + ") returnobj;/n"); Класс, название класса класса прокси требуется, пользовательский реализация, определяемая пользователем, имя класса класса Proxyarithmetic = myProxyImpl.newProxyInstance ("com.test.arithmeticInterface", "com.test.arithmetic", "com.test.interceptormplefface", (ArithtecticeNitemetrice ",« Com.Test.InterceptorMpl »); б);Распечатайте код для динамической реализации интерфейса следующим образом:
public int add (int a0, int a1) {com.test.interceptordlorhandler reterceptor = new com.test.interceptorhandlerimpl (); object returnobj = receptor.invoke (class.forname ("com.test.arithmetic"). newinstance (), class.forname ("com.test.arithmetic"). Object [] {($ w) a0, ($ w) a1}); return ((integer) returnobj) .intvalue ();} public int sub (int a0, int a1) {com.test.interceptordhandl receptor.invoke (class.forname ("com.test.arithmetic"). newinstance (), class.forname ("com.test.arithmetic"). getmethods () [1], новый объект [] {($ w) a0, ($ w) a1}; return (integer) returnobj) .int Выше приведено подробное введение в механизм внедрения динамического прокси в Java, и я надеюсь, что это будет полезно для каждого обучения.