Java实现AOP —— 模拟动态代理

本文介绍了如何使用动态代理技术将业务逻辑代码与日志记录代码分离,通过代理类实现了日志记录功能的独立配置与维护,提高了代码的复用性和可维护性。进一步通过动态生成代理类,使得日志记录功能可以根据需要动态切换,增强了系统的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      很多知识点的细节就不整理了,网上资源很多,这里为了简单起见,例子不使用任何第三方的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();
        }
    }
 
}



内容概要:该研究通过在黑龙江省某示范村进行24小时实地测试,比较了燃煤炉具与自动/手动进料生物质炉具的污染物排放特征。结果显示,生物质炉具相比燃煤炉具显著降低了PM2.5、CO和SO2的排放(自动进料分别降低41.2%、54.3%、40.0%;手动进料降低35.3%、22.1%、20.0%),但NOx排放未降低甚至有所增加。研究还发现,经济性和便利性是影响生物质炉具推广的重要因素。该研究不仅提供了实际排放数据支持,还通过Python代码详细复现了排放特征比较、减排效果计算和结果可视化,进一步探讨了燃料性质、动态排放特征、碳平衡计算以及政策建议。 适合人群:从事环境科学研究的学者、政府环保部门工作人员、能源政策制定者、关注农村能源转型的社会人士。 使用场景及目标:①评估生物质炉具在农村地区的推广潜力;②为政策制定者提供科学依据,优化补贴政策;③帮助研究人员深入了解生物质炉具的排放特征和技术改进方向;④为企业研发更高效的生物质炉具提供参考。 其他说明:该研究通过大量数据分析和模拟,揭示了生物质炉具在实际应用中的优点和挑战,特别是NOx排放增加的问题。研究还提出了多项具体的技术改进方向和政策建议,如优化进料方式、提高热效率、建设本地颗粒厂等,为生物质炉具的广泛推广提供了可行路径。此外,研究还开发了一个智能政策建议生成系统,可以根据不同地区的特征定制化生成政策建议,为农村能源转型提供了有力支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值