• Spring:IOC/DI和AOP的解析原理和代码


    在spring中经常被提及的概念,一个是控制反转IOC,一个是依赖注入DI。还有一个面向切面编程AOP。

    浅谈控制反转(IOC):

          在任何一个有请求作用的系统当中,至少需要有两个类互相配合工作,在一个入口类下使用new关键字创建另一个类的对象实例,这就好比在面向对象编程的思想下,“我“充当一个入口类,在这个入口类中,我每次吃饭的时候都要买一双一次性筷子(每一次使用都要new一次),在这样的关系下,是”我“(即调用者)每次都要”主动“去买一次性筷子(另一个类),我对筷子说你老老实实的过来我的手上,是我控制了筷子,那好,在这种控制正转的关系下,放在现实生活当中,肯定是不现实的,他总会去创造出更加方便自己生活的想法,更确切的做法是,买一双普通的筷子(非一次性),把他放在一个容器当中(在Spring中叫做IOC容器),你需要使用的时候就对容器说:IOC我想要用筷子(向容器发出请求),接着筷子就会”注入“到的手上,而在这个过程当中,你不再是控制方,反而演变成一名请求者(虽然本身还是调用者),依赖于容器给予你资源,控制权坐落到了容器身上,于是这就是人们俗称的控制反转

    依赖注入(DI):在控制反转中获取资源的过程叫做依赖注入【由容器动态的将某个依赖关系注入到组件之中】,那么这里代码实现也是专注于依赖注入。

    依赖注入有3种方式:构造函数方法注入,setter方法注入,接口注入(侵入性,淘汰)。

    IOC: 体现了工厂模式,通过BeanFactory注入实例,将对象交给容器管理,在配置文件定义相应的bean,以及设置相关的属性,让spring容器来生成实例对象以 及管理对象。


    说白了其实就是由我们平常的new转成了使用反射来获取类的实例,相信任何人只要会用java的反射机制,比如:

        public ObjectgetInstance(String className) throws Exceptio {
          Object obj = Class.forName(className).newInstance(); //获取传入类的实例 
          Method[] methods = obj.getClass().getMethods(); //获取传入类的所有方法
          for (Method method : methods) {
            if (method.getName().intern() == "setString") {
              method.invoke(obj, "hello world!");
            }
          }
        }

    2.关于Spring  AOP :

     AOP:面向切面编程,在不修改源代码的情况下(通过预编译方式和动态代理实现)给程序动态统一添加功能。比如说加日志,权限。

      jdk动态代理:用拦截器(拦截器必须实现InvocationHanlder接口)加上反射机制生成一个实现代理接口的匿名类,在调用方法前调用 InvokeHandler来处理。使用Proxy.newProxyInstance产生代理对象。

      cglib动态代理:必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法。是一种继承。针对接口编程的环境下推荐使用JDK的代理。

      静态代理模式:是由程序员创建或特定工具自动生成源代码,在对其进行编译。在程序运行之前,代理类的.class文件就已经存在。

    注意: 如果目标对象有实现接口,使用jdk代理。如果目标对象没有实现接口,则使用cglib代理。

    比如:我们现在要开发的一个应用里面有很多的业务方法,但是,我们现在要对这个方法的执行做全面监控,就会在要一些方法前去加上一条日志记录。

    springboot项目demo:

     applicationContext.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
     
      <!-- 配置扫描的包 -->
        <context:component-scan base-package="com.road.unifiedpay.test2"/>
       <!-- 切面的声明 -->
        <bean id="transaction" class="com.road.unifiedpay.test2.Transaction"/>
          <!--aop配置  --> 
        <aop:config>
             <!-- 切点, 配置aop的切入点id; 是切入点的标识 ;expression 为切入点的表达式 -->
             <aop:pointcut expression="execution(* com.road.unifiedpay.test2..*(..))" id="perform"/>
             <!-- 切面,配置切面(切面里面配置通知)—— ref 指向声明切面的类 -->
             <aop:aspect ref="transaction">
             <!--     前置通知pointcut-ref 引用一个切入点 -->
             <aop:before method="beginTransaction" pointcut-ref="perform"/>
             <!--     后置通知 -->
             <!-- <aop:after-returning method="commit" pointcut-ref="perform" returning="val"/> -->
             </aop:aspect>
        </aop:config>
      
    </beans>
    package com.road.unifiedpay.test2;
    public class Person {
         private Long pid;
            private String pname;
     
            public Long getPid() {
                return pid;
            }
     
            public void setPid(Long pid) {
                this.pid = pid;
            }
     
            public String getPname() {
                return pname;
            }
     
            public void setPname(String pname) {
                this.pname = pname;
            }
    }
    package com.road.unifiedpay.test2;
    import java.util.List;
     
     
    /**
     * 目标对象和代理对象都实现的接口
     */
    public interface PersonDao {
             void deletePerson();
            List<Person> getPerson() throws Exception;
            void savePerson();
            void updatePerson();
    }
    package com.road.unifiedpay.test2;
    import java.util.ArrayList;
    import java.util.List;
     
    import org.springframework.stereotype.Service;
     
    /**
     * 目标对象:实现目标接口
     */
    @Service("personDao")
    public class PersonDaoImpl implements PersonDao{
        
         @Override
        public void deletePerson() {
            System.out.println("delete perosn");
        }
     
        @Override
        public List<Person> getPerson() throws Exception {
            List<Person> personList = new ArrayList<Person>();
            Person person1 = new Person();
            person1.setPid(1L);
            person1.setPname("person1");
            System.out.println("get person");
            personList.add(person1);
            return personList;
        }
     
        @Override
        public void savePerson() {
            System.out.println("save perosn");
        }
     
        @Override
        public void updatePerson() {
            System.out.println("update perosn");
        }
    
    }
    package com.road.unifiedpay.test2;
    import java.util.ArrayList;
    import java.util.List;
     
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
     
     
    /**
     * 切面(spring aop 就不需要拦截器啦)
     * (模拟hibernate里面保存数据要打开事物,然后各种增删改之后,再提交事物。)
     */
    public class Transaction {
        
        //前置通知
        public void beginTransaction() {
            System.out.println("begin Transaction");//打开事物
        }
     
        /**
         * @param joinPoint 通过joinPoint可以得到目标类和目标方法的一些信息
         * @param val       目标方法的返回值
         *                  和<aop:after-returning returning="val"/>中returning的值保质一致
         */
        //后置通知
        public void commit(JoinPoint joinPoint, Object val) {
            String methodName = joinPoint.getSignature().getName();
            System.out.println("后置通知:"+methodName);
            //提交事物
            System.out.println("commit");
            List<Person> personList = (ArrayList<Person>) val;
            for (Person person : personList) {
                System.out.println(person.getPname());
            }
        }
     
        public void finalMethod() {
            System.out.println("最终通知");
        }
     
        public void aroundMethod(ProceedingJoinPoint joinPoint) {//环绕通知
            try {
                System.out.println("around method");
                joinPoint.proceed();//调用目标类的目标方法
            } catch (Throwable e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
     
        /**
         * 异常通知
         */
        public void throwingMethod(Throwable except) {
            System.out.println(except.getMessage());
        }
    
    }

    测试:

    package com.road.unifiedpay.test2;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
     
     
    public class TestAop {
        
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            PersonDao personDao=(PersonDao) context.getBean("personDao");
            try {
                personDao.savePerson();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    结果:

     说明:在applicationContext.xml中申明了切面,切点,通知类型,切点配置了调用personDaoImpl实现类的所有方法只要执行就会先打印前置通知“begin Transcation” 。

     总结一句话就是:AOP 在不修改源代码的情况下给程序动态统一添加功能。  这样就能够在一个项目及时要在中途需要这么一个功能,那也就只需修改配置文件和加一个类,而没有该已经写好的类的代码。aop明显增加了代码的复用性,也省去了重新测试。

    上面的三条红色的竖向框就是经常说的切面,在这个切面里面有很多的方法,可以把a()看做上面的说道的前置通知,b()看做后置通知,c()看做最终通知等等。总而言之,这些方法都不需要我们去写的,而是aop自动帮我们做好的。我们只要触动了我们的比如“保存方法”就会执行切面里的一系列方法。这样就省去了很多开发时间,也精简了代码。

  • 相关阅读:
    Spring Freamwork 开发初体验
    mybooklist.cn 书单de故事六月十六日
    mybooklist 日志5.12
    我的书单mybooklist
    Bison
    Java学习日记——基础篇(一)常识
    Python学习日记(十)—— 杂货铺(全局变量补充、Python参数传递、字符串格式化、迭代器、生成器)
    Python学习日记(九)—— 模块二(logging、json&pickle、xml、requests、configparser、shutil、subprocess)
    Python学习日记(八)—— 模块一(sys、os、hashlib、random、time、RE)
    Python学习日记(七)——装饰器
  • 原文地址:https://www.cnblogs.com/lgg20/p/11188743.html
Copyright © 2020-2023  润新知