Spring AOP&&动态代理(一)

AOP :面向切面的编程   是对OOP的扩展

OOP :引入封装、继承、多态等概念来建立一种对象层次结构;OOP允许开发者定义纵向关系,而不能处理横向关系;像类似日志记录、异常处理这种代码都是横向散落在代码中,它与核心业务代码无关,这种散落各处的无关代码称为横切,在OOP中它导致了大量代码的重复,且不利于各模块的复用。。

AOP:与OOP相反,采用 “横切”技术,解剖封装的对象内部,将影响多个类的公共行为封装到一个可重用模块,命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。。。

 AOP:使得代码编写顺序不再决定其运行顺序,引出了动态组件的概念(取代了传统纵向继承体系的重复性代码)。。

          主要功能:日志记录、性能统计、安全控制、事务处理、异常处理等。。。。

         主要意图:将日志记录、性能统计、安全控制、事务处理、异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,希望可将他们独立到非指导业务逻辑的方这些行为法中,进而改变这些行为时不影响业务逻辑的代码。。

 

AOP 编写代码时只需要考虑业务逻辑,而不需要考虑太多功能性代码,使用aop 可以在逻辑代码恰后加一些功能型代码,如:日志记录、性能统计、安全控制、异常处理等 ,这些功能与核心业务无关但是有时候必须存在的部分 被称为“横切关注点(跨整个系统的常用功能)”。。

如何将“横切关注点”从逻辑代码中分离出来--实现解耦??

           继承:重用时需要不断修改基类;

           委托:需要不断调用委托对象(繁琐);

           AOP:可模块化关注横向点并形成对象,称为Aspect,这样使用Aspect能够创建解耦的代码; 

 

AOP 相关概念:

  切面:封装的公共行为,可以加到核心业务前后的方法 定义切入点和通知;

  连接点:被拦截到的方法(spring只支持方法类型的切入点);

  通知(增强):切面方法 aop在特定切入点上执行的操作 有前置、后置、异常、最终、环绕五种通知;

  切入点:带有通知的连接点(书写切入点表达式),决定切入Aspect;

  目标 (Target):一个被切入的地方,它一般是核心关注,业务功能;

  代理 (Proxy):当一个advice(通知)应用到目标对象时,这时一个代理对象将被创建.;

  织入(Weaving):将切面应用到目标对象并使得代理对象被创建的过程(编译期、运行期);

  引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段;

 

AOP编程:

(定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。)

         1.定义常用功能组件;

         2.定义切入点(一个切入点可横切多个组件);

         3.定义增强处理(业务组件的织入处理);

 

Spring AOP:

spring 中的aop 离不开IOC 容器的支持,如代理的生成,管理及其依赖关系都是由IOC容器负责, AOP容器创建和管理代理对象的生命周期。因此AOP代理可直接使用容器中的其他实例 bean为目标,这种关系可以由IOC 的依赖注入完成。。

 

spring创建AOP代理:动态代理实现。。。。

 

AOP&&动态代理:

Java的动态代理机制:aop 的底层实现原理就是动态代理

       1.在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。。

       2.InvocationHandler:每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。。

        3.Proxy:这个类的作用是用来动态创建一个代理对象的类,常用 newProxyInstance () 方法--得到一个动态的代理对象。

        4.动态代理的实现:

 

1.定义一个接口Subject,声明一个rent()、一个hello();

2.定义一个类RealSubject实现Subject接口----真实对象;

3.定义一个动态代理类,必须要实现InvocationHandler接口;

public class DynamicProxy implements InvocationHandler
{
    // 这个就是我们要代理的真实对象
    private Object subject;
    
    //    构造方法,给我们要代理的真实对象赋初值
    public DynamicProxy(Object subject)
    {
        this.book= book;
    }
    @Override
    public Object invoke(Object object, Method method, Object[] args)
            throws Throwable
    {
        //  在代理真实对象前可以添加一些自己的操作
        System.out.println("before rent house");
        
        System.out.println("Method:" + method);
        
        /*    当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
              Object invoke(Object proxy, Method method, Object[] args) throws Throwable
              proxy:  指代我们所代理的那个真实对象
              method:  指代的是我们所要调用真实对象的某个方法的Method对象
              args:  指代的是调用真实对象某个方法时接受的参数
        */
        method.invoke(subject, args);
        
        //  在代理真实对象后也可以添加一些自己的操作
        System.out.println("after rent house");
        
        return null;
    }

}

4.客户端测试:

public class Client
{
    public static void main(String[] args)
    {
        //    我们要代理的真实对象
        Subject realSubject = new RealSubject();

        //    我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        InvocationHandler handler = new DynamicProxy(realSubject);

        /*
         * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
         * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
         * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了(接口是Subject类型,所以就可以将代理对象转化为Subject类型)
         * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
         */
        Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
                .getClass().getInterfaces(), handler);
        
        System.out.println(subject.getClass().getName());
        subject.rent();
        subject.hello("world");
    }
}

ps:

1.接口是什么类型,创建的代理对象就可以强转为什么类型;

2.Proxy.newProxyInstance 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号----- $Proxy0;

subject.rent();
subject.hello("world");

3.这里是通过代理对象来调用实现的那种接口中的方法,这个时候程序就会跳转到由这个代理对象关联到的 handler 中的invoke方法去执行,而我们的这个 handler 对象又接受了一个 RealSubject类型的参数,表示我要代理的就是这个真实对象,所以此时就会调用 handler 中的invoke方法去执行。。

4.当我通过代理对象来调用方法的时候,实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

 

 AOP动态代理的两种实现:JDK动态代理 & CGLIB动态代理:

  •            默认使用JDK动态代理 ,可强制设置为CGLIB动态代理方式;
  •            当类没有实现接口时 自动转换为CGLIB动态代理方式;

    1.JDK动态代理:对实现接口的类进行代理,而不是针对类,目标类型实现的接口都将被代理,可为任何接口创建代理 

                                  原理:运行期间创建一个接口的实现类来完成对目标对象的代理

         <1>某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法,如果某个类没有实现接口,那么这个类就不能同JDK产生动态代理,面向接口编程。。

        <2>创建  接口实现类的 代理对象,与实现类平级的对象 但不是真实对象  时代理对象 完成与实现类相同的功能。。

        <3>实现流程: (Java的动态代理机制

  •  定义一个实现接口InvocationHandler的类
  •  通过构造函数,注入被代理类
  • 实现invoke( Object proxy, Method method, Object[] args)方法
  •  在主函数中获得被代理类的类加载器
  •  使用Proxy.newProxyInstance( )产生一个代理对象
  • 通过代理对象调用各种方法

   

2.CGLIB动态代理:针对类的代理,不关心是否实现接口(对指定类生成一个子类,覆盖基类方法-- 继承)

                                 它的底层使用ASM在内存中动态的生成被代理类的子类。。

      <1>创建类的子类的代理对象  在子类中调用父类的方法完成增强;

      <2>实现流程:

  • 定义一个实现了MethodInterceptor接口的类
  • 实现其intercept()方法,在其中调用proxy.invokeSuper( )
 public class CglibProxy implements MethodInterceptor {  //主要的方法拦截类,它是Callback接口的子接口,需要用户实现
    @Override  
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {  
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");  
        System.out.println(method.getName());  
        Object o1 = methodProxy.invokeSuper(o, args);  
        System.out.println("++++++before " + methodProxy.getSuperName() + "++++++");  
        return o1;  
    }  
}  

 

展开阅读全文

没有更多推荐了,返回首页