美文网首页
springMVC+maven+mybatis+mysql入门

springMVC+maven+mybatis+mysql入门

作者: Mark_Du | 来源:发表于2018-07-18 01:27 被阅读61次

    (三)DI,IOC,AOP等基本概念

    本文简单讲解一下DI,IOC,AOP,spring的基本理念就是这几个,想要用好springMVC,多少需要了解一些。本文主要参考《Spring实战》(第四版)

    首先想说的是,这年头国外的那些开发人员,开始有点走上邪教的道路,不知道是为了体现技术的独特,还是为了让别人不太容易懂,喜欢取出各种各样稀奇古怪的名字,加上中英文翻译的问题,中文名字看起来就更玄了。

    不要拘泥于对概念名称的理解,只要能理解概念的具体含义就可以了。


    1.准备工作
    根据上一篇 (二)创建maven工程 创建好一个Maven工程,我们叫做 DIdemo ,创建好之后,目录结构大致如下:

    目录结构.png
    jdk版本不用在意,每个人的不同
    打开com.springdemo.DIdemo下的App.java,里面已经写好了一个main函数,我们改一改打印输出
    package com.springdemo.DIdemo;
    
    /**
     * Hello world!
     *
     */
    public class App 
    {
        public static void main( String[] args )
        {
            System.out.println( "Hello World!Wa ha ha!!" );
        }
    }
    
    

    在代码区,点右键,找到 Run As,选择 Java Application

    run.png
    如果Myeclipse配置没有问题的话,在下边功能窗口中,Console一栏,会输出main函数中我们打印的 "Hello World!Wa ha ha!!"
    这样就表明,我们的工程建立成功。
    然后,修改pom.xml文件,添加spring依赖包
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>com.springdemo</groupId>
      <artifactId>DIdemo</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    
      <name>DIdemo</name>
      <url>http://maven.apache.org</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>5.0.7.RELEASE</spring.version>
      </properties>
    
     <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        
        <!-- spring start -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        
    
        <!-- spring end -->
        
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>
        
    
      </dependencies>
    </project>
    
    

    只需要关注 <dependencies></dependencies> 之间的依赖包配置即可,其他部分根据不同版本的maven,不同版本的Myeclipse,都会有不同,不用在意。

    2.依赖注入
    依赖注入DI(Dependency Injection) 要说清这个概念,靠文字理论来说明,不直观,不好理解,我们直接用代码来说明。
    我们这里举一个例子,可能不是最适合的,但是基本可以说清这个概念。

    首先,我们创建学生,学生的一个任务就是完成作业...

    package com.springdemo.DIdemo;
    
    public interface Student {
        void finishHomeWork(); 
    }
    

    所以,我们还得有作业

    package com.springdemo.DIdemo;
    
    public interface Homework {
        
        void doHomework();
    }
    

    好啦,假如我们这里有个一年级学生

    package com.springdemo.DIdemo;
    
    public class GradeOneStudent implements Student {   
        
        public void finishHomeWork() {      
    
        }
    
    }
    

    然后,这个学生有数学作业

    package com.springdemo.DIdemo;
    
    public class MathHomework implements Homework {
    
        public void doHomework() {
            System.out.println("Wa ha ha,the math homework is finished!!");
        }
    
    }
    

    现在,我们要让这个一年级的学生,做数学作业,早些时候的实现方式是这样的:

    package com.springdemo.DIdemo;
    
    public class GradeOneStudent implements Student {   
        
        private MathHomework todayMathWork;
        
        public GradeOneStudent(){
            this.todayMathWork = new MathHomework();
        }
        
        public void finishHomeWork() {      
            todayMathWork.doHomework();
        }
    
    }
    

    然后我们在main函数中让学生做作业

    package com.springdemo.DIdemo;
    
    public class App 
    {
        public static void main( String[] args )
        {
            GradeOneStudent oneStudent = new GradeOneStudent();
            oneStudent.finishHomeWork();
        }
    }
    

    这是早些时候比较常见的用法,需要用那个类,就创建一个类的对象,然后使用它。
    学生和作业之家,是一个依赖关系。
    在功能上,这样写没有问题,而且我们平时可能也写了不少这样的代码,但是,这样写,学生作业就成了一个紧密耦合关系,这样不利于类的扩展,维护等,例如,现在小学生要做语文作业了,我们只能修改GradeOneStudent,在里面创建一个语文作业的对象,然后使用。
    为了解决这个问题,我们怎么办呢,修改一下GradeOneStudent

    package com.springdemo.DIdemo;
    
    public class GradeOneStudent implements Student {   
        
        private Homework todayWork;
        
        public GradeOneStudent(Homework todayWork){
            this.todayWork = todayWork;
        }
        
        public void finishHomeWork() {      
            todayWork.doHomework();
        }
    
    }
    
    

    修改后,我们没有自己创建作业,而是在构造器中将作业做为构造器参数传入进来,这就是 依赖注入,这是依赖注入的一种方式,构造器注入。
    这样修改之后,我们可以完成各种作业,在main中,传入作业对象即可。

    package com.springdemo.DIdemo;
    
    public class App 
    {
        public static void main( String[] args )
        {
            MathHomework todayMathWork = new MathHomework();
            GradeOneStudent oneStudent = new GradeOneStudent(todayMathWork);
            oneStudent.finishHomeWork();
        }
    }
    
    

    这样,学生作业之间,就是一个松耦合关系,这就是依赖注入的一个主要目的。
    通过上面的讲解,可以简单理解依赖注入的意思了。
    3.控制反转
    控制反转(Inversion of Control,IOC)从字面上理解,就是把控制反转了,正常的控制是怎么样的呢?就是我们上面的例子,学生要写作业,在main中创建一个作业,注入给学生,这个过程,是我们自己控制的。那么把控制反转了,在代码里怎么体现呢?
    这个就是Spring的主要理念了,让我们把控制交给它,我们自己不管,在需要的时候,Spring自动把需要的对象注入进来,这就是控制反转了。
    继续用上面的代码作为示例。
    这里,我们增加Spring配置文件,在 com.springdemo.DIdemo 包下面创建一个 student.xml(放在这个位置不严谨,但是方便使用和说明),这样我们的工程目录结构大致是这样的

    目录结构.png
    student.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean id="student" class="com.springdemo.DIdemo.GradeOneStudent">
        <constructor-arg ref="homework" />
      </bean>
    
      <bean id="homework" class="com.springdemo.DIdemo.MathHomework">
      </bean>
    
    </beans>
    

    其中有两段是关键配置:

      <bean id="student" class="com.springdemo.DIdemo.GradeOneStudent">
        <constructor-arg ref="homework" />
      </bean>
    
      <bean id="homework" class="com.springdemo.DIdemo.MathHomework">
      </bean>
    

    首先,java Bean这个概念,我自己理解就是可以重用的一个组件,更深刻的理解还需要读者自行研究。 这个配置理解起来也比较简单, bean id 类似于这个bean的名字,后面别的bean使用的时候,直接用这个名字,这里,我们配置了两个bean,一个是我们的GradeOneStudent,一个是MathHomework,其中GradeOneStudent 多了一个配置

    <constructor-arg ref="homework" />` 
    

    这个 constructor-arg配置的就是构造器参数,ref="homework"就是指,将 homework这个bean作为参数传进GradeOneStudent的构造器,而我们的GradeOneStudent的构造器确实需要一个homework参数

        public GradeOneStudent(Homework todayWork){
            this.todayWork = todayWork;
        }
    

    student.xml文件写好后,我们修改main函数:

    package com.springdemo.DIdemo;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    
    public class App 
    {
        public static void main( String[] args )
        {
            ClassPathXmlApplicationContext context = 
                    new ClassPathXmlApplicationContext("com/springdemo/DIdemo/student.xml");
                Student gradeOneStudent = context.getBean(Student.class);
                gradeOneStudent.finishHomeWork();
                context.close();
        }
    }
    
    

    通过Spring框架的ClassPathXmlApplicationContextstudent.xml加载进去,它会自动识别bean的配置,这样,我们就可以获取到Student bean,直接执行finishHomeWork(),Spring会自动注入我们需要的依赖。
    这个由Spring来控制在需要的时候注入依赖的过程,就是控制反转IOC


    这里增加一些说明,我们上面的示例里面,在配置文件里,只配置了一个Student类型的bean,所以,我们这样写

    Student gradeOneStudent = context.getBean(Student.class);
    

    可以直接找到我们定义的GradeOneStudent bean,但是,如果有多个Student类型的bean,那么就需要我们自己指定bean的id来获取了。例如,我们创建一个二年级学生

    package com.springdemo.DIdemo;
    
    public class GradeTwoStudent implements Student {
    
        private Homework todayWork;
        public GradeTwoStudent(Homework todayWork){
            this.todayWork = todayWork;
            
        }
        public void finishHomeWork() {
            System.out.println("I am grade Two");
            todayWork.doHomework();
        }
    
    }
    

    student.xml中配置好这个bean

    <bean id="studentOne" class="com.springdemo.DIdemo.GradeOneStudent">
        <constructor-arg ref="homework" />
      </bean>
      
      <bean id="studentTwo" class="com.springdemo.DIdemo.GradeTwoStudent">
        <constructor-arg ref="homework" />
      </bean>
    
      <bean id="homework" class="com.springdemo.DIdemo.MathHomework">
      </bean>
    

    这样,我们就有两个Student类型的bean了,如果main还是按照之前的方式执行,会报错找不到具体的bean

    Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.springdemo.DIdemo.Student' available: expected single matching bean but found 2: studentOne,studentTwo
    

    这种情况,需要我们在加载bean的时候,指定bean id

    package com.springdemo.DIdemo;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    
    public class App 
    {
        public static void main( String[] args )
        {
            ClassPathXmlApplicationContext context = 
                    new ClassPathXmlApplicationContext("com/springdemo/DIdemo/student.xml");
                Student gradeOneStudent = context.getBean("studentOne",Student.class);///这里,需要指定bean id
                gradeOneStudent.finishHomeWork();;
                context.close();
        }
    }
    
    

    关于spring框架下的bean的其他相关问题,后面我会单独写一篇文章。


    4.AOP
    面向切面编程(Aspect Oriented Programming ,AOP)在我的理解,就是基于依赖注入DI的一种编程方式,利用DI这种概念来实现减轻耦合,降低重复代码量,便于coder维护代码的一种编程方式。
    讲一堆概念,还是不如来个栗子。

    还是我们前面学生写作业的工程。
    大部分小盆友读一年级的时候,还需要家长的鼓励支持,开始写作业的时候,妈妈会表扬一下小盆友的自觉性,作业做完了,再表扬一下,以此增加小盆友写作业的积极性,我们来模拟这个场景。
    先增加一个妈妈类。

    package com.springdemo.DIdemo;
    
    public class Mather {
        public void beforeHomework(){
            System.out.println("Good!! that's great.");
        }
        
        public void afterHomework(){
            System.out.println("Nice work!!!");
        }
    }
    
    
    

    然后,按照上面依赖注入的方式,加入到GradeOneStudent

    package com.springdemo.DIdemo;
    
    public class GradeOneStudent implements Student {   
        
        private Homework todayWork;
        private Mather myMather;
        
        public GradeOneStudent(Homework todayWork,Mather myMather){
            this.todayWork= todayWork;
            this.myMather = myMather;
        }
        
        public void finishHomeWork() {  
            myMather.beforeHomework();
            todayWork.doHomework();
            myMather.afterHomework();
        }
    
    }
    
    

    最后,到student.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean id="studentOne" class="com.springdemo.DIdemo.GradeOneStudent">
        <constructor-arg ref="homework" />
        <constructor-arg ref="mom" />
      </bean> 
    
      <bean id="mom" class="com.springdemo.DIdemo.Mather">
      </bean>
    
      <bean id="homework" class="com.springdemo.DIdemo.MathHomework">
      </bean>
    
    </beans>
    

    这样,我们就可以在main中运行啦。
    但是。。。但是。。。有点不太对的地方,从代码语义上来看,妈妈由学生来控制了,写作业这个方法中,不仅写了作业,还去找了妈妈两次,这个就不太合适了。
    这里,我们引入切面的概念,妈妈类作为一个切面,切点就是学生做作业,在做作业前后,切面切入进来,执行它的动作。我们如何来实现呢?
    修改一下student.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:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="http://www.springframework.org/schema/aop 
          http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/beans 
          http://www.springframework.org/schema/beans/spring-beans.xsd">
    
      <bean id="studentOne" class="com.springdemo.DIdemo.GradeOneStudent">
        <constructor-arg ref="homework" />
      </bean> 
    
      <bean id="mom" class="com.springdemo.DIdemo.Mather">
      </bean>
    
      <bean id="homework" class="com.springdemo.DIdemo.MathHomework">
      </bean>
      
      <aop:config>
        <aop:aspect ref="mom">
          <aop:pointcut id="doHomework"
              expression="execution(* *.finishHomeWork(..))"/>
            
          <aop:before pointcut-ref="doHomework" 
              method="beforeHomework"/>
    
          <aop:after pointcut-ref="doHomework" 
              method="afterHomework"/>
        </aop:aspect>
      </aop:config>
    
    
    </beans>
    

    注意,我们这里引入了<aop>标签,所以xml开头的beans声明有变动。
    我们这里增加了一段aop的配置,<aop:aspect>引入了妈妈类的beanmom,然后通过<aop:pointcut>定义了一个切点执行finishHomeWork的时候,最后通过<aop:before>声明了前置通知,<aop:after>声明了后置通知。这部分是采用的AspectJ的切点表达式语言。
    接着,我们就可以把学生类中调用妈妈的方法去掉。

    package com.springdemo.DIdemo;
    
    public class GradeOneStudent implements Student {   
        
        private Homework todayWork;
        
        public GradeOneStudent(Homework todayWork,Mather myMather){
            this.todayWork= todayWork;      
        }
        
        public void finishHomeWork() {  
            todayWork.doHomework();
    
        }
    
    }
    
    

    在main中运行一下,就可以实现前面学生自己调用妈妈方法的效果。
    这个简单是示例,基本体现了AOP的编程思想。

    对于上面这些基本概念的简单理解,有助于我们后面Spring MVC的开发过程中,对于各种配置操作的理解,不至于懵懵懂懂死记配置。

    上面使用的工程源码,SpringStart github仓库 DIdemo,请自行下载。

    相关文章

      网友评论

          本文标题:springMVC+maven+mybatis+mysql入门

          本文链接:https://www.haomeiwen.com/subject/icdgpftx.html