• Spring学习笔记之aop动态代理(3)


    Spring学习笔记之aop动态代理(3)
    1.0 静态代理模式的缺点:
    1、在该系统中有多少的dao就的写多少的proxy,麻烦
    2、如果目标接口有方法的改动,则proxy也需要改动。
    PersonDao.java

    public interface PersonDao {
        public void savePerson();
    }
    
    PersonDaoImpl.java
    public class PersonDaoImpl implements PersonDao{
        public void savePerson() {
            System.out.println("save person");
        }
    }

    PersonDaoProxy.java

    public class PersonDaoProxy implements PersonDao{
        private PersonDao personDao;
        private Transaction transaction;
        public PersonDaoProxy(PersonDao personDao,Transaction transaction){
            this.personDao = personDao;
            this.transaction = transaction;
        }
        public void savePerson() {
            this.transaction.beginTransaction();
            this.personDao.savePerson();
            this.transaction.commit();
        }
    }   

    Transaction.java

    public class Transaction {
        public void beginTransaction(){
            System.out.println("begin transaction");
        }
    
        public void commit(){
            System.out.println("commit");
        }
    }

    ProxyTest.java

    /*
     * 静态代码模式:
     *    1、在该系统中,有多少dao,就得写多少proxy
     *    2、如果目标接口有方法的改动,则proxy也得做相应的修改
     */
    public class ProxyTest {
        @Test
        public void testProxy(){
            PersonDao personDao = new PersonDaoImpl();
            Transaction transaction = new Transaction();
            PersonDaoProxy proxy = new PersonDaoProxy(personDao, transaction);
            proxy.savePerson();
        }
    }

    2.0 动态代理模式–jdkproxy
    (优点:动态的产生代理对象,所以只需要一个拦截器就可以了。
    缺点:如果invoke方法做事务的判断,将很复杂。
    程序员还是写拦截器了,写拦截器的invoke方法了,所以invoke方法还需要修改)
    问题:
    1、拦截器的invoke方法在什么时候被调用的
    在代理对象调用方法的时候,进入了拦截器中的invoke方法。
    2、拦截器总的method参数是什么?在什么时候由实参传递给形参的。
    代理对象的方法的名称是什么,method的参数的名称就是什么。
    代理对象调用方法的时候进入了拦截器的invoke方法,这个时候传递参数。
    3、生成的代理对象实现了接口,代理对象的方法体的内容是什么?
    方法体的内容就是拦截器中的invoke方法体的内容。
    说明:目标类和代理类实现了共同的接口。

    MyInterceptor.java

    /**
     * 1、引入personDao和Transaction
     * 2、完成invoke方法
     * @author zd
     *
     */
    public class MyInterceptor implements InvocationHandler{
        private Object target;
        private Transaction transaction;
        public MyInterceptor(Object target,Transaction transaction){
            this.target = target;
            this.transaction = transaction;
        }
    
    
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            if(method.getName().equals("savePerson")
                    ||method.getName().equals("updatePerson")){
                this.transaction.beginTransaction();
                method.invoke(this.target, args);//调用目标类的目标方法
                this.transaction.commit();
            }else{
                method.invoke(this.target, args);//调用目标类的目标方法
            }
            return null;
        }
    }   

    PersonDao.java

    public interface PersonDao {
        public void savePerson();
    }   
    
    PersonDaoImpl.java  
    public class PersonDaoImpl implements PersonDao{
        public void savePerson() {
            System.out.println("save person");
        }
    }   
    /*
     *
     *   问题:
     *      1、拦截器中的invoke方法在什么时候被调用的
     *         在代理对象调用方法的时候,进入了拦截器中的invoke方法
     *      2、拦截器中的method参数是什么?在什么时候由实参传递给形参的
     *         代理对象的方法的名称是什么,method参数就是什么
     *         代理对象调用方法的时候,进入了拦截器中的invoke方法,这个时候,传递参数
     *      3、生成的代理对象实现了接口,代理对象的方法体的内容是什么?
     *         方法体的内容就是拦截器中的invoke方法体的内容
     */

    Transaction.java

    public class Transaction {
        public void beginTransaction(){
            System.out.println("begin transaction");
        }
        public void commit(){
            System.out.println("commit");
        }
    }

    ProxyTest.java

    /**
     * jdkproxy的优点
     *     动态的产生代理对象,所以只需要用一个拦截器就可以了
     * jdkproxy的缺点
     *     如果在invoke方法中做事务的判断,将是一件很复杂的事情
     *     程序员还是写拦截器了,写拦截器中的invoke方法了,所以invoke方法还需要修改
     *     
     *  说明:
     *      目标类和代理类实现了共同的接口
     * @author zd
     *
     */
    public class ProxyTest {
        @Test
        public void testProxy(){
            PersonDao target = new PersonDaoImpl();
            Transaction transaction = new Transaction();
            MyInterceptor interceptor = new MyInterceptor(target, transaction);
            /**
             * 第一个参数  目标类的类加载器
             * 第二个参数  目标类实现的所有的接口
             * 第三个参数  拦截器
             */
            PersonDao personDao = (PersonDao)Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                        target.getClass().getInterfaces(), interceptor);
            personDao.savePerson();
        }
    }

    3.0 动态代理模式–cglib
    jar包cglib-nodep-2.1.3
    说明:目标类是代理类的父类。

    PersonDaoImpl.java

    public class PersonDaoImpl implements PersonDao{
        public void savePerson() {
            System.out.println("save person");
        }
    }
    
    
    
    Transaction.java
    public class Transaction {
        public void beginTransaction(){
            System.out.println("begin transaction");
        }
    
        public void commit(){
            System.out.println("commit");
        }
    }

    MyInterceptor.java

    /**
     * 1、引入personDao和Transaction
     * 2、完成invoke方法
     * @author zd
     *
     */
    public class MyInterceptor implements MethodInterceptor{
        private Object target;
        private Transaction transaction;
        public MyInterceptor(Object target,Transaction transaction){
            this.target = target;
            this.transaction = transaction;
        }
    
        public Object createProxy(){
            Enhancer enhancer = new Enhancer();
            enhancer.setCallback(this);//this代表拦截器对象
            enhancer.setSuperclass(target.getClass());//设置代理类的父类为目标类
            return enhancer.create();
        }
        /**
         * 该方法的内容和jdkpoxy中的invoke方法的内容是一样的
         */
        public Object intercept(Object arg0, Method method,  Object[] args,
                MethodProxy arg3) throws Throwable { 
            this.transaction.beginTransaction();
            method.invoke(this.target, args);
            this.transaction.commit();
            return null;
        }
    
    }
    
    PersonDao.java
    public interface PersonDao {
        public void savePerson();
    }

    ProxyTest.java

    /**
     * 目标类是代理类的父类
     * @author zd
     *
     */
    public class ProxyTest {
        @Test
        public void testProxy(){
            PersonDaoImpl target = new PersonDaoImpl();
            Transaction transaction = new Transaction();
            MyInterceptor interceptor = new MyInterceptor(target, transaction);
            PersonDaoImpl proxy = (PersonDaoImpl)interceptor.createProxy();
            proxy.savePerson();
        }
    }   

    4.0 spring aop的概念
    切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事物管理是J2EE应用中一个横切关注点的很好的例子。
    在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。
    连接点(joinpoint):在程序执行过程中某个特定的点,比如某方法调用时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。
    通知(Advice):在切面的某个特定连接点执行的动作。其中包括了“around”、“before”和“after”等不同类型的通知。(通知的类型将在后面部分进行讨论)。
    许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
    切入点(Piontcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行。(例如,当执行某个特定名称的方法时)。
    切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用Aspect切入点语法。
    引入(Introduction):用来给一个模型声明额外的方法或属性(也被称为连接类型声明)(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)
    到任何被代理的对象。例如,你可以使用引入来使用一个bean实现IsModified接口,以便简化缓存机制。
    目标对象(Target Object):被一个或者多个切面所通知的对象。也被称做通知(adviced)对象。既然Spring AOP是通过运行代理实现的,这个对象永远是一个被代理(Proxied)对象。
    AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
    织入(Weaving):把切面连接到其他的应用程序类型或者对象,并创建一个被通知的对象。这些可以编译时(例如使用AspectJ编译器),类加载时和运行时完成。
    Spring和其他纯Java AOP框架一样,在运行时完成织入。

    JDKProxy代理                          SpringAop
    目标对象                                目标对象
    拦截器类                                切面
    拦截器类中的方法                        通知
    被拦截到的目标类中方法的集合              切入点
    在客户端调用的方法(目标类目标方法)       连接点
    代理类                                 AOP代理
    代理类的代理方法生成的过程               织入
    
    通知根据拦截目标类中的目标方法的位置不一样可以分为:前置通知、后置通知、最终通知、环绕通知、异常通知
    
    说明:
    1、通知就是切面中的方法;
    2、代理对象的方法=通知+目标方法
    3、连接点就是目标接口中的一个方法而已
    4、拦截器中的invoke方法就是代理对象的方法=通知+目标方法
    5、在现实的开发过程中,通知和目标方法时完全松耦合的
    
    通知:就是切面的方法
    织入:形成代理对象的方法的过程就是织入
    连接点:客户端调用哪个方法,该方法就是连接点。
    只有符合切入点,才让通知和目标方法结合在一起。
    
  • 相关阅读:
    Logstash使用mongodb插件报错: ArgumentError: wrong number of arguments (given 2, expected 1)
    Vim注释行的方法
    设计模式之单例模式
    ssh命令的常用使用场景
    Flask 和Django
    Git 常用命令大全
    实例 静态 类
    python的各种推导式(列表推导式、字典推导式、集合推导式)
    MongoDB
    MySql变量说明
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6467979.html
Copyright © 2020-2023  润新知