JavaEE---------简单使用JDK动态代理模拟Spring的AOP
Spring的面向切面编程可以使用AspectJ来实现
那如何实现的呢?在有接口的被委托类可以实现其接口,没有借口的用CGLIB可以动态生成
下面就来模拟一下用接口实现
先创建一个接口:
UserDAO.java
package com.bq; public interface UserDAO { public void addUser(); }
实现它的UserDAOImpl.java
package com.bq; public class UserDAOImpl implements UserDAO { @Override public void addUser() { System.out.println("用户已保存"); } }
编写委托类:LogInterceptor.java
package com.bq; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class LogInterceptor implements InvocationHandler { //目标对象 private Object target; public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } //创建一个方法以便在注入前调用 public void beforeMethod(Method m) { System.out.println("在执行方法之前调用"); System.out.println(m.getName() + " start"); } /* * 重写接口里的方法 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) * */ /*相关说明,理解每个参数的意思 * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * * @param method the {@code Method} instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the {@code Method} object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or {@code null} if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * {@code java.lang.Integer} or {@code java.lang.Boolean}. * * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * {@code null} and the interface method's return type is * primitive, then a {@code NullPointerException} will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a {@code ClassCastException} will be thrown by the method * invocation on the proxy instance. * * @throws Throwable the exception to throw from the method * invocation on the proxy instance. The exception's type must be * assignable either to any of the exception types declared in the * {@code throws} clause of the interface method or to the * unchecked exception types {@code java.lang.RuntimeException} * or {@code java.lang.Error}. If a checked exception is * thrown by this method that is not assignable to any of the * exception types declared in the {@code throws} clause of * the interface method, then an * {@link UndeclaredThrowableException} containing the * exception that was thrown by this method will be thrown by the * method invocation on the proxy instance. * * @see UndeclaredThrowableException */ @Override public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { //注入前的调用方法 beforeMethod(m); //注入 m.invoke(target, args); return null; } }
编写测试类:Index.java
package com.bq; import java.lang.reflect.Proxy; public class Index { public static void main(String[] args) { // 创建委托和被委托类对象 UserDAO userDAO = new UserDAOImpl(); LogInterceptor li = new LogInterceptor(); // 给委托者设置目标 li.setTarget(userDAO); /* * 注意下面这个对象的生成过程,给出参数说明 第一个参数是ClassLoader,直接使用被委托类的ClassLoader即可 * 第二个参数是被委托类的接口名称 第三个参数是委托对象 * * @param loader the class loader to define the proxy class * * @param interfaces the list of interfaces for the proxy class to * implement * * @param h the invocation handler to dispatch method invocations to * * @return a proxy instance with the specified invocation handler of a * proxy class that is defined by the specified class loader and that * implements the specified interfaces */ UserDAO userDAOProxy = (UserDAO) Proxy.newProxyInstance(userDAO .getClass().getClassLoader(), userDAO.getClass() .getInterfaces(), li); // 打印出对象的类名称 System.out.println(userDAOProxy.getClass().getName()); /* * 调用被委托的方法 他会先调用beforeMethod(Method m); */ userDAOProxy.addUser(); } }
至于如果你的类没有实现接口,Spring也可以帮你生成一个代理
它使用CGLIB,这个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
Hibernate用它来实现PO字节码的动态生成。