AOP技术(1)


1、AOP介绍

​ AOP(Aspect-Oriented Programming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

​ 而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,利用了jdk动态代理技术,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

2、静态代理模式实现AOP

  1. 接口类IHello:
public interface IHello 
{
     void sayHello(String str);
     void sayBye();
}
  1. 实现类Hello:
public class Hello implements IHello{

    @Override
    public void sayHello(String str) {
        System.out.println("hello : "+str);
    }

    @Override
    public void sayBye() {
        System.out.println("bye bye");
    }

}
  1. 代理类ProxyHello:
 /* 
  * 静态代理模式实现AOP
  */
public class ProxyHello implements IHello {

    private IHello target;

    public ProxyHello(IHello target)
    {
            this.target=target;
    }

    @Override
    public void sayHello(String str) {
        // TODO Auto-generated method stub
          Logger.start();    //Logger类,打印日志信息
          target.sayHello(str);// 调用被代理对象的方法
          Logger.end();
    }

    @Override
    public void sayBye() {
        target.sayBye();
    }

}
  1. main函数:
public class TestStaticProxy {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        IHello target=new Hello();
        //target.sayHello("just a demo");
        target = new ProxyHello(target);

        target.sayHello(" kitty"); 
        System.out.println("*************************");
        target.sayBye();

    }

}

静态代理AOP输出结果

3、动态代理模式实现AOP

动态代理模式实现AOP

代理类:

public class DynDemo implements InvocationHandler  
{  
    private Object obj;// 此时OBJ是要被代理的对象
    private BeforeMethod befor;

    public DynDemo()  
    {  
    }  

    // 此时参数obj是要被代理的对象
    public DynDemo(Object obj)  
    {  
        this.obj = obj;  
    }  
    // 此时参数obj是要被代理的对象
    public DynDemo(Object obj,BeforeMethod befor)  
    {  
        this.obj = obj; 
        this.befor=befor;
    }  

    //这个方法不是我们显示的去调用  
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable  
    {  
          System.out.println(method.getName());
          if(method.getName().equals("hello"))
        {
             return method.invoke(obj, args);  //真正的调用它原对象

        }

       // System.out.println("before calling " + method);  
          if(befor!=null)
               befor.say();
      //System.out.println(this.obj);
        Object result= method.invoke(obj, args);  // 执行被代理对象的切入点
                        // obj.say(args);
        System.out.println("after calling " + method);  

        return result;  
    }

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

    public BeforeMethod getBefor() {
        return befor;
    }

    public void setBefor(BeforeMethod befor) {
        this.befor = befor;
    }  

}  

​ 该代理类的作用:通过被代理的对象,确认切入点,增加额外的操作。

客户端:

//客户端:生成代理实例,并调用了say()方法  
public class TestDemo {  

  public static void main(String[] args) throws Throwable{  
        // TODO Auto-generated method stub  

      Demo target=new Demo();//这里指定被代理类  

      InvocationHandler ds=new DynDemo(target); 

      System.out.println(ds);

     Class<?> cls=target.getClass();// 获取target对象类的字节码  

     //以下是一次性生成代理  
      IDemo subject=(IDemo)Proxy.newProxyInstance(  
                cls.getClassLoader(),cls.getInterfaces(), ds); 


        //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了IDemo接口  
        System.out.println(subject instanceof Proxy);  

        //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了IDemo接口  
     //  System.out.println("subject的Class类是:"+subject.getClass().getName());  
     //  System.out.println("subject的Class父类是:"+subject.getClass().getSuperclass()
    //           .getName());  


     /*  System.out.println("\n"+"subject实现的接口是:");  
        Class<?>[] interfaces=subject.getClass().getInterfaces();  

       for(Class<?> i:interfaces)&#123;  
           System.out.println(i.getName());  
       &#125; 

       System.out.println("subject中的属性有:");  

        Field[] field=subject.getClass().getSuperclass().getDeclaredFields();  
        for(Field f:field)
        &#123;  
            System.out.print(f.getName()+", "+f.getType()+" # ");
            f.setAccessible(true);
            System.out.println(f.get(subject)+"  #"+f.getModifiers());
        &#125; */ 


       System.out.println("subject中的方法有:");  

        Method[] method=subject.getClass().getDeclaredMethods();  

        for(Method m:method)&#123;  
            System.out.print(m.getName()+", ");  
        &#125;  

        /*
   */
       System.out.println("\n\n"+"运行结果为:");  
       subject.say();  
       subject.hello();

    &#125;  
&#125;  


​ 通过 Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), ds)方法,一次性生成代理,并通过返回的对象调用重写的方法。

Proxy.newProxyInstance()的具体实现逻辑:

public class MyProxy implements IDemo
&#123;
    private DynDemo target;

    public MyProxy(DynDemo target)
    &#123;
        this.target=target;
    &#125;

    @Override
    public void say() &#123;
        // TODO Auto-generated method stub
        try &#123;
         Method method=target.getObj().getClass().getDeclaredMethod("say", null);

           target.invoke(null, method, null);
        &#125;catch(Throwable e)
        &#123;
            e.printStackTrace();
        &#125;
    &#125;

    @Override
    public void hello() &#123;
        // TODO Auto-generated method stub

    &#125;

&#125;

4、小结

4.1 静态代理模式下的AOP实现

​ 通过一个实现接口的代理类,可以在不影响用户操作的时候添加其它功能,如记录日志等。

4.2 动态代理模式下的AOP实现

​ 代理类可以动态地获取被代理的对象,通过该对象获取切入点,并添加操作。然后通过客户端实现被代理类的接口,调用Proxy.newProxyInstance()方法获取代理实例,并通过该方法返回的实例对象调用重写后的方法。

Proxy.newProxyInstance()完成的功能:

  1. 生成一个类(Proxy的子类);Proxy$0

  2. Proxy$0类会重写被代理类的接口。

  3. 每个方法调用中间代理类的invoke。


文章作者: kilig
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 kilig !
 上一篇
AOP技术(2) AOP技术(2)
1. 通过自己的spring内核实现AOP配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans> <bean name="before" class="com.h
2020-08-01
下一篇 
NIO NIO
1、NIO概念1.1 Unix定义了五种 I/O 模型 阻塞 I/O 非阻塞 I/O I/O 复用 信号驱动 I/O 异步 I/O 1.1.1 阻塞(Block)和非阻塞(NonBlock)阻塞和非阻塞是进程在访问数据的时候,数据是否准备
  目录