很多知识点的细节就不整理了,网上资源很多,这里为了简单起见,例子不使用任何第三方的AOP FrameWork,也不使用Java语言本身自带的动态代理,而是自行通过模拟动态代理来实现日志记录。
让我们先回到AOP本身,AOP主要应用于日志记录、性能统计、安全控制、事务处理能方面。它的主要意图就是要将日志记录、性能统计等等代码从业务逻辑代码中清除地划分出来,我们可以把这些行为一个一个单独看做系统所要解决的问题,就是所谓的面向切面编程。通过对这些行为的分离,我们希望可以将它们独立地配置到业务逻辑方法中,而要改变这些行为也不需要影响业务逻辑方法。
假设BusinessObject完成业务逻辑功能,现在要求在每一次业务逻辑处理时做日志记录LoggerHandler,如下:
//IBusinessObject.java
package testaop;
public interface IBusinessObject {
void method1();
void method2();
void method3();
void method4();
void method5();
void method6();
}
//BusinessObject.java
package testaop;
public class BusniessObject implements IBusinessObject {
@Override
public void method1() {
LoggerHandler.begin();
System.out.println("method1");
LoggerHandler.end();
}
@Override
public void method2() {
LoggerHandler.begin();
System.out.println("method2");
LoggerHandler.end();
}
@Override
public void method3() {
LoggerHandler.begin();
System.out.println("method3");
LoggerHandler.end();
}
@Override
public void method4() {
LoggerHandler.begin();
System.out.println("method4");
LoggerHandler.end();
}
@Override
public void method5() {
LoggerHandler.begin();
System.out.println("method5");
LoggerHandler.end();
}
@Override
public void method6() {
LoggerHandler.begin();
System.out.println("method6");
LoggerHandler.end();
}
}
//LoggerHandler.java
package testaop;
public class LoggerHandler{
public static void begin(){
System.out.println(new Date().toLocaleString()+" begin ...");
}
public static void end(){
System.out.println(new Date().toLocaleString()+" end ...");
}
}
从上面可以看出,BusinessObject中的业务逻辑代码void methodN( )和日志记录代码完全混合在一起,若是想要更改日志记录的功能,则必须在BusinessObject中的业务逻辑代码中进行修改,这给日后的维护带来一定的困难,而且,最明显的是,造成了代码重复,完全相同的LoggerHandler代码将出现在系统中的每一个BusinessObject中。
现在要做的就是将日志记录代码分离出来。分离就涉及到一个问题,我们必须知道业务逻辑代码何时被调用,我们好插入日志记录代码。所以,对上面的代码做如下更改:
//IHandler.java
package testaop;
public interface IHandler {
void invoke(String methodName) throws Exception;
}
//LoggerHandler.java
package testaop;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
public class LoggerHandler implements IHandler{
public static void begin(){
System.out.println(new Date().toLocaleString()+" begin ...");
}
public static void end(){
System.out.println(new Date().toLocaleString()+" end ...");
}
private IBusinessObject ibo;
public LoggerHandler(IBusinessObject ibo){
this.ibo=ibo;
}
public void invoke(String methodName) throws NoSuchMethodException, SecurityException{
Class cl=IBusinessObject.class;
Method method=cl.getDeclaredMethod(methodName);
begin();
try {
method.invoke(ibo);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
end();
}
}
//BusinessObjectProxy.java
package testaop;
import java.lang.reflect.InvocationTargetException;
public class BusinessObjectProxy implements IBusinessObject {
/**
*BusinessObject的代理类
*/
private IBusinessObject ibo;
private IHandler handler;
public BusinessObjectProxy(IBusinessObject ibo,IHandler handler){
this.ibo=ibo;
this.handler=handler;
}
@Override
public void method1() {
String methodName=new Throwable().getStackTrace()[0].getMethodName();
//这里的methodName可以直接用字符串“method1(-6)”代替
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void method2() {
String methodName=new Throwable().getStackTrace()[0].getMethodName();
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void method3() {
String methodName=new Throwable().getStackTrace()[0].getMethodName();
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void method4() {
// TODO Auto-generated method stub
String methodName=new Throwable().getStackTrace()[0].getMethodName();
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void method5() {
String methodName=new Throwable().getStackTrace()[0].getMethodName();
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void method6() {
String methodName=new Throwable().getStackTrace()[0].getMethodName();
try {
handler.invoke(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
其中核心代码在于:LoggerHandler中的invoke函数
public void invoke(String methodName) throws NoSuchMethodException, SecurityException{
Class cl=IBusinessObject.class;
Method method=cl.getDeclaredMethod(methodName);
begin();
//try-catch省略
method.invoke(ibo);
}
通过代理类BusinessObjectProxy中的方法void methodN( ),向LoggerHandler的invoke传入对应的函数名,然后通过Java的反射机制,实现函数的回调。因为代理类BusinessObjectProxy实现了IBusiness接口,所以函数的回调实际上就是对BusinessObject中的方法的调用。
但是,问题又来了:有人会说,你的代理类只能实现一种业务的代理,而且在LoggerHandler中耦合了被代理的业务,写死了,要是想换被代理的业务怎么办呢?
好吧,继续修改。
//IHandler2.java
package testaop;
import java.lang.reflect.Method;
public interface IHandler2 {
//这里传入的不再是方法名,而是Method对象
void invoke(Method method);
}
//LoggerHandler2.java
package testaop;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
public class LoggerHandler2 implements IHandler2 {
private Object proxied;
public LoggerHandler2(Object proxied){
this.proxied=proxied;
}
public static void begin2(){
System.out.println(new Date().toLocaleString()+" begin2 ...");
}
public static void end2(){
System.out.println(new Date().toLocaleString()+" end2 ...");
}
public void invoke(Method method) {
begin2();
try {
method.invoke(proxied);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
end2();
}
}
//压轴的Proxy.java
package testaop;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Proxy {
/**
*
* @param Interface:表示被代理类接口对应的Class对象
* @param handler:IHandler2接口的对象
* @return
* @throws IOException
* @throws NoSuchMethodException
* @throws SecurityException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
* @throws ClassNotFoundException
*/
public Object newProxyInstance(Class<?> Interface,IHandler2 handler) throws IOException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException{
String rt="\r\n";
String src="package testaop;"+rt
+"import java.lang.reflect.Method;"+rt
+"public class Proxy1 implements "+Interface.getName()
+rt+"{"
+rt+"private IHandler2 handler;"
+rt+"public Proxy1(IHandler2 handler)"
+rt+"{this.handler=handler;}"
+rt+getMethodsString(Interface)
+rt+"}";
String fileName="C:/ws/myproject/SpringAOP/src/testaop/Proxy1.java";
//创建实际的代理类Proxy1.java
File f=new File(fileName);
FileWriter fw=new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//编译Proxy1.java
JavaCompiler comp=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager=comp.getStandardFileManager(null, null, null);
Iterable units=fileManager.getJavaFileObjects(fileName);
CompilationTask t=comp.getTask(null, fileManager, null, null, null, units);
t.call();
fileManager.close();
//加载Proxy1.class到内存并创建实例
URL[] urls=new URL[]{new URL("file:/C:/ws/myproject/SpringAOP/src/")};
URLClassLoader loader=new URLClassLoader(urls);
Class clazz=loader.loadClass("testaop.Proxy1");
Constructor cons=clazz.getConstructor(Class.forName("testaop.IHandler2"));
//System.out.println(handler.getClass().getSimpleName());
return cons.newInstance(handler);
}
public String getMethodsString(Class<?> Interface){
String rt="\r\n";
java.lang.reflect.Method[] methods=Interface.getMethods();
StringBuilder methodString=new StringBuilder();
for(java.lang.reflect.Method m:methods){
methodString.append("public void ")
.append(m.getName())
.append("()").append("{").append(rt)
.append("try{")
.append("Method method=")
.append(Interface.getName())
.append(".class.getMethod(\""+m.getName()+"\");")
.append(rt)
.append("handler.invoke(method);")
.append(rt)
.append("}")
.append("catch(Exception e){e.printStackTrace();}")
.append(rt)
.append("}")
.append(rt);
}
return methodString.toString();
}
}
最后我们编写一个测试代码:
package testaop;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test {
public void hello(){
System.out.println(new Throwable().getStackTrace()[0].getMethodName());
}
public static void main(String[] args) {
//这里多实现了一个功能
//类似于Struts2中的拦截器,在被代理的业务逻辑代码外层包裹多个系统代码
//为了简单起见,LoggerHandler3与LoggerHandler2只有对应的方法名不同,其余一样
String[] handlers=new String[]{"LoggerHandler3","LoggerHandler2"};
IBusinessObject ibo=new BusniessObject();
Proxy p=new Proxy();
try {
for(int i=0;i<handlers.length;i++){
Constructor cons=Class.forName("testaop."+handlers[i]).getConstructor(Object.class);
IHandler2 handler=(IHandler2) cons.newInstance(ibo);
ibo=(IBusinessObject) p.newProxyInstance(IBusinessObject.class, handler);
}
} catch (Exception e) {
e.printStackTrace();
}
ibo.method2();
}
}
其中,Proxy.java动态生成的真正的代理类Proxy1.java如下:
package testaop;
import java.lang.reflect.Method;
public class Proxy1 implements testaop.IBusinessObject {
private IHandler2 handler;
public Proxy1(IHandler2 handler) {
this.handler = handler;
}
public void method5() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method5");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void method6() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method6");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void method4() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method4");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void method1() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method1");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void method3() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method3");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
public void method2() {
try {
Method method = testaop.IBusinessObject.class.getMethod("method2");
handler.invoke(method);
} catch (Exception e) {
e.printStackTrace();
}
}
}