java-动态代理-spring AOP由浅到深

一、理解:

1.动态代理解释:无入侵的代码扩展,在不修改源码的情况下增强方法功能。客户不想或者不能直接引用另一个对象,这时客户端和目标对象中间的媒介就是动态代理。在程序运行时,通过反射机制生成。动态代理事先不知道会代理什么,运行时才知道。简单的举个小例子:把所有类中的方法前后打日志,此时想到的就是动态代理。

 

2.静态代理解释:在学习动态代理之前,简单理解一下静态代理。静态代理是每个类都需要创建一个代理对象,每个方法都有一个委托。动态代理是用反射动态来反射的方法。下面是静态代理的简单小案例:(建议学完动态代理再来看静态代理代码,更容易看出区别)

package TestProtected;

public class StaticProxyTest {

    //-------------BaseService 接口-----------------
        static interface BaseService {
            void say();
        }
//-------------RealService 实现了 BaseService 接口-----------------
        
        static class RealService implements BaseService {
            @Override
            public void say() {
                System.out.println("i am realService!");
            }
        }
//-------------代理ProxySerice 实现了 BaseService 接口-----------------

        static class ProxySerice implements BaseService {

            private BaseService realService;//属性变量,是实际对象

            //构造函数,带属性的构造函数
            public ProxySerice(BaseService baseService) {
                this.realService = baseService;
            }

            //实现公共接口
            @Override
            public void say() {
                System.out.println("say before");
                realService.say();
                System.out.println("say after");

            }
        }   

        public static void main(String[] args) {
//            BaseService realService = new RealService();
//            BaseService ProxySerice = new ProxySerice(realService);
//            ProxySerice.say();
            new ProxySerice(new RealService()).say();
            
        }  
}

3.动态代理分两类:JDK基于接口代理;第三方CGLIB代理类库;jdk动态代理比CGLIB速度快,但性能CGLIB更好一些

4.JDK动态代理:

代码解析:1.代理类分为4部分:代理对象实现InvocationHandler接口;代理类中有一个成员属性变量;代理对象在构造函数中初始化;实现公共接口;

               2.调用代理类:在调用时使用Proxy类中的newProxyInstance()方法动态创建类。

代码:

1.接口Subject

package TestProtectesOne;

public interface Subject {
    
        void request();
        void hello();

}

2.接口实现类

package TestProtectesOne;

public class RealSubject implements Subject{

    @Override
    public void request() {
          System.out.println("real subject execute request");
        
    }

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

}

3.代理类 实现了InvocationHandler接口,并且实现了其中的invoke方法

package TestProtectesOne;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkProxySubject implements InvocationHandler{
    //成员变量
    private Object realSubject;
    
    //构造器
    public JdkProxySubject(Subject subject) {
        this.realSubject = subject;
    }
    //问:在代理类里面没有实现类的任何东西出现?  答:对的

    
    /*
    *invoke方法方法参数解析
    *Object proxy:指被代理的对象。 
    *Method method:要调用的方法 
    *Object[] args:方法调用时所需要的参数 
    *InvocationHandler接口的子类可以看成代理的最终操作类。
    */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        
        
        System.out.println("befor");
        
        
        try {
            //利用反射动态的来反射方法,这就是动态代理和静态代理的区别
           /*
            * Object obj :代理的接口对象
            * Object... args :接口对象方法的参数
            * */
            method.invoke(realSubject, args);        
            
        } catch (Exception e) {
            System.out.println("ex:"+e.getMessage());
            throw e;
        } finally {
            System.out.println("after+++++++");
        }
        //问和返回null有什么区别?
        return null;

    }

}

4.调用主函数

package TestProtectesOne;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

//调用者
public class Client {

      /*
        *newProxyInstance方法参数解析
        *ClassLoader loader:类加载器 
        *Class<?>[] interfaces:得到全部的接口 
        *InvocationHandler h:得到InvocationHandler接口的子类实例 
        */
    
    public static void main(String[] args) {
    
        //在没有扩展之前,调用继承类中的方法
//        Subject subject = new RealSubject();
//        subject.hello();
//        subject.request();
        
        //现在想在这两个方法之前打日志
        //Subject subject1  = new RealSubject();
        //new 一个代理类     传入的参数是实体类
        
        InvocationHandler handler =  new JdkProxySubject(new RealSubject1());
        
        //Proxy.newProxyInstance(Client.class.getClassLoader(), subject1, handler);
        
        Subject subject2 = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(),
                new Class[]{Subject.class}, handler);
        
        subject2.request();
        subject2.hello();   
    }
}

注:其中有一个参数是类加载器,下一个文章是类加载器

jdk动态代理项目总结:jdk首先必须要有一个接口A1(Subject),实现了A1 的实现类 B1(RealSubject)。在之前的项目中直接可以用调用方法的类C1(Client)调用实现类里面的方法。但是现在有一个需求:在B1方法前后打日志。所以引入了动态代理的思想,创建一个代理类(JdkProxySubject),这个代理类指向了接口A1。在调用类C1通过Proxy.newProxyInstance()方式调用。

 

5.CGLIB第三方动态代理:

在类没有实现接口时可以使用,就像上面的,在B1没有实现A1的情况下,要求对B1的方法前后打日志,在这个时候代理类可以使用这个方法。实现原理是:加载代理的是class文件,修改字节码生成子类。所以,被代理的类不能是final类型的。

代码解析:

   引用包cglib-xxx.jar

   非Maven项目还需要手动引用包asm-xxx.jar

  1. 业务类(不需要定义接口)
  2. cglib代理类(实现接口MethodInterceptor

代码:

1.业务类

package com.wzq.demo02;

/**
 * 业务类
 * 
 * 没有实现接口
 * 
 * 如果类是final的,则无法生成代理对象,报错
 * 
 * 如果方法是final的,代理无效
 * 
 * @author Muscleape
 *
 */
public class UserServiceImpl {
    public void addUser() {
        System.out.println("增加一个用户。。。");
    }

    public void editUser() {
        System.out.println("编辑一个用户。。。");
    }
}

2、cglib代理类,需要实现接口MethodInterceptor

package com.wzq.demo02;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class UserServiceCglib implements MethodInterceptor {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 设置回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    /**
     * 实现MethodInterceptor接口中重写的方法
     * 
     * 回调方法
     */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("事务开始。。。");
        Object result = proxy.invokeSuper(object, args);
        System.out.println("事务结束。。。");
        return result;
    }

}

调用类

package com.wzq.demo02;

public class TestCglib {
    public static void main(String[] args) {
        UserServiceCglib cglib = new UserServiceCglib();
        UserServiceImpl bookFacedImpl = (UserServiceImpl) cglib.getInstance(new UserServiceImpl());
        bookFacedImpl.addUser();
    }
}

这段参考了:https://www.cnblogs.com/muscleape/p/9018308.html

 

6.spring aop 代理模式

①如果bean 实现接口,默认是jdk 代理模式

如果bean 没有实现接口,默认是CGLIB代理模式

也可以强行使用CGLIB模式,修改配置文件。

②使用场景:日志、监控、事务、权限、异常等

③不管是那种代理模式,都不能用private和final修饰

 

 

posted @ 2020-03-04 21:29  旺旺a  阅读(235)  评论(0编辑  收藏  举报