Spring核心知识详细教程(已完结)

声明:

本文全文手写,代码全部手写,也希望大家,可以做一遍,最起码调试一遍,这样比看的效果好的多,本文的spring使用的是5.0.4版本,ide使用的是IntelliJ IDEA,不足和错误之处还请大家指出,谢谢!!

一、spring是什么

  • Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架

  • spring官网

  • spring百度百科
  • spring-framework

二、spring快速入门

  • 什么是spring?

    • 首先我们了解到struts是web框架(jsp/action/actionform)
    • hibernate是 orm框架处于持久层
    • spring是容器框架,用于配置bean,并维护bean之间的关系的框架

      • spring中的bean:是一个很重要的概念,这里的bean可以是Java中的任何一种对象:JavaBean/service/action/数据源/dao等等
      • spring中的ioc(inverse of control 控制反转)
      • spring中的di(dependency injection 依赖注入)
        接下来看一个层次框架图:
        这里写图片描述
        说明:

        • web层: struts充当web层,接管jsp,action,表单,主要体现出mvc的数据输入数据的处理数据的显示分离
        • model层: model层在概念上可以理解为包含了业务层,dao层,持久层,需要注意的是,一个项目中,不一定每一个层次都有
        • 持久层:体现oop,主要解决关系模型和对象模型之间的阻抗
  • 入门项目

    • 创建java项目(web中也可以使用)
    • 创建lib文件夹引入spring的开发最小包(最小配置,spring.jar(该包把最常用的包都包括),commons-logging.jar(日志包))
    • 创建配置文件,一般在src目录下
    • 配置bean
      这里写图片描述
      说明:这对标签元素的作用:当我们加载spring框架时,spring就会自动创建一个bean对象,并放入内存相当于我们常规的new一个对象,而中的value则是实现了“对象.set方法”,这里也体现了注入了概念
    • 然后在java文件(测试文件)中调用

    接下来看具体的项目:
    这是我的目录结构
    说明:这是我的目录结构,其中我使用了ide整合了jar包,如果是手动创建时只需将jar包导入到项目里即可

    User.java

package com.nuc.Bean;

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String sayHello(){
        System.out.println("hello"+ name);
        return "true";
    }
}

抛开spring框架,使用传统方式实现在测试类中调用sayHello方法:
这里写图片描述
这样,没有异议吧。
接下来使用spring调用该方法
这里写图片描述
结果为小强,是因为上面的配置文件中配置value为小强

这样一个基本的项目就完成了~
接下来是细节:

  • 创建User2这个类

    package com.nuc.Bean;
    
    public class User2 {
        private String name;
    
        public String getName() {
            return name;
        }
    
       public void setName(String name) {
            this.name = name;
        }
        public void sayBye(){
            System.out.println("bye"+name);
        }
    }
  • 在User中新增private User2 user2;并在sayHello中调用sayBye方法
    这里写图片描述
  • 执行test类报出错误,这是由于user2未注入
    这里写图片描述
  • 在配置文件中配置注入
    这里写图片描述
    注意点细节都已经在图中注释表明
  • 再次运行测试类
    这里写图片描述
    spring运行原理图
    原理图
    入门项目小结:
    spring实际上是容器框架,可以配置各种bean,并可以维护bean与bean的关系,当我们需要使用某个bean的时候,我们可以直接getBean(id),使用即可

现在我们来回答什么是spring这个问题

  • spring是一个容器框架,它可以接管web层,业务层,dao层,持久层的各个组件,并且可以配置各种bean, 并可以维护bean与bean的关系,当我们需要使用某个bean的时候,我们可以直接getBean(id),使用即可

接下来对几个重要的概念做说明:

  • ioc是什么?
    • ioc(inverse of control)控制反转:所谓反转就是把创建对象(bean)和维护对象(bean)的关系的权利从程序转移到spring的容器(spring-config.xml)
  • di是什么?
    • di(dependency injection)依赖注入:实际上di和ioc是同一个概念,spring的设计者,认为di更准确的表示spring的核心

实质上学习框架就是,最重要的就是学习各个配置

三、接口编程

  • spring就提倡接口编程,在配合di技术就可以达到层与层解耦的目的
  • 举案例说明:
    这个项目实现的是大小写转换
    基本思路:

    • 创建一个接口
    • 创建两个类实现接口
    • 配置bean
    • 使用

    下面是我的项目目录
    这里写图片描述

ChangeLetter.java

package com.nuc;

public interface ChangeLetter {
    public String change();
}

LowerLetter.java

package com.nuc;

public class LowerLetter implements ChangeLetter {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public String change(){
        //大小字母转小写
        return str.toLowerCase();
    }
}

UpperLetter.java

package com.nuc;

public class UpperLetter implements ChangeLetter {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    public String change(){
        //把小写字母转成大写
       return str.toUpperCase();
    }
}

spring-config.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="changeLetter" class="com.nuc.UpperLetter">-->
        <!--<property name="str">-->
            <!--<value>sjt</value>-->
        <!--</property>-->
    <!--</bean>-->

    <bean id="changeLetter" class="com.nuc.LowerLetter">
        <property name="str" value="SJT"/>
    </bean>
</beans>

说明:其中的两个bean id名相同是为了调试方便,可通过注释来调试
Test.java

package com.nuc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
        //调用change不使用接口
//        UpperLetter changeLetter = (UpperLetter) ac.getBean("changeLetter");
//        System.out.println(changeLetter.change());

        //使用接口
        ChangeLetter changeLetter = (ChangeLetter)ac.getBean("changeLetter");
        System.out.println(changeLetter.change());
    }
}

以上这个案例,我们可以初步体会到,di和接口编程,的确可以减少层(web层)和层(业务层)之间的耦合度,尽管看起来似乎没什么改变,而且好像麻烦了一些,但是当项目大了以后,这种耦合度的降低就显得尤为重要

四、获取Bean

  • ApplicationContext 应用上下文容器取

    ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");

    当这句代码被执行,spring-config.xml文件中配置的bean就会被实例化。(但要注意bean的生命周期要为singleton),也就是说,不管没有getBean(),使用上下文容器获取bean,就会实例化该bean

  • Bean工厂容器取

    BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));

    这句代码被执行,spring-config.xml文件中配置的bean不会被实例化,即光实例化容器,并不会实例化bean
    而是在执行以下代码时才会被实例化,即使用bean的时候
    factory.getBean("beanId");

    如何验证上述说法呢?每一个java类都有一个默认的构造方法。给这个构造方法输出一句话。具体如下

    • 创建一个类,类有一个属性,装配,该属性
    • 重写该类的构造方法,输出bean被创建
    • 创建测试类,测试

    使用ApplicationContext应用上下文容器
    这里写图片描述
    使用bean工厂
    这里写图片描述
    可以看到,这一行代码,并不能时bean实例化,接下来加factory.getBean(“student”);试试
    这里写图片描述
    这样就是bean实例化了

    那么在实际开发中选择哪种方式?

    在移动开发中,即项目运行在移动设备中使用BeanFactory(节约内存,所以,你想节约内存开发就是使用bean工厂,但速度会受影响),但大部分的项目都是使用ApplicationContext(可以提前加载,缺点是消耗一点内存)
    贴一张bean的生命周期图:
    这里写图片描述
    接下来我们验证前两种作用域:
    第一种
    这里写图片描述
    结果
    这里写图片描述
    可以看到stu1和stu2拥有相同的地址,接下来测试第二种
    这里写图片描述
    这里写图片描述
    测试结束!
    至于后三种是在web开发中才有实际意义!

五、三种获取ApplicationContext对象引用的方法

  • ClassPathXmlApplicationContext (从类路径中加载)
    • 这个不在赘述,上面所有例子都是利用这种方式加载的
  • FileSystemXmlApplicationContext (从文件系统中加载)
    这里写图片描述
    可以看到是没有问题的,需要注意的是,文件路径为绝对路径,且注意使用转义符,直接使用“C:\sjt\idea\code\spring\spring-interface\src”,会报错,需要将“\”转义,但实际开发中应用不多,了解即可
  • XmlWebApplicationContext (从web系统中加载)
    • 这种方式,注意,在tomcat启动时就会加载,此处不做说明,在web应用中说明

六、再谈Bean的生命周期

  • 生命周期是一个重点吗?答案是肯定的!!

    • 不了解生命周期难道不能开发了吗?那自然是可以的,但如果你想实现更加高级的功能,你不了解那可能是会出问题的!而在面试过程中也是经常提及的。
    • 接下里我们举例子说明

      • 生命周期分为以下几步:

        • 1、实例化
          • 当我们加载sping-config.xml文件时,bean就会被实例化到内存(前提是scope=singleton)
        • 2、设置属性值
          • 调用set方法设置属性,前提是有对应的set方法
        • 3、如果你调用BeanNameAware的set’Bean’Name()方法
          • 这是个接口,该方法可以给出正在被调用的bean的id
        • 4、如果你调用BeanFactoryAware的setBeanFactory()方法
          • 这也是个接口,该方法可以传递beanFactory
        • 5、如果你调用了ApplicationContextAeare的setApplicationContext()方法
          • 同样为接口,该方法传递一个ApplicationContext
        • 6、BeanPostProcessor的预初始化方法Before
          • 这个东西很厉害了,可以叫做后置处理器,它不是接口,具体细节,代码体现
        • 7、如果你调用了InitializingBean的afterPropertiesSet()方法
        • 8、调用自己的init方法,具体为在bean中有一个属性inin-method=”init”
        • 9、BeanPostProcessor的方法After
        • 10、使用bean,体现为调用了sayHi()方法
        • 11、容器关闭
        • 12、可以实现DisposableBean接口的destory方法
        • 13、可以在调用自己的销毁方法,类似于8

        实际开发过程中,并没有这么复杂,常见过程为,1,2,6,9,10,11

        接下来看代码
        目录结构
        MyBeanPostProcessor.java

package com.nuc.BeanLife;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第九步,postProcessAfterInitialization方法被调用");
        return null;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第六步,postProcessBeforeInitialization方法被调用");
        System.out.println("第六步,"+bean+"被创建的时间为"+new java.util.Date());
        /*
        在这里,能做的事情可就不止上面的这么简单的一句输出了,它还可以过滤每个对象的ip
        还可以给所有对象添加属性或者函数,总之就是所有对象!
        其实,这里体现了AOP编程的思想,AOP呢就是面向切成编程(针对所有对象编程)
         */

        return null;
    }
}

PersonService.java

package com.nuc.BeanLife;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class PersonService implements BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步调用set方法");
    }
    public void sayHi(){
        System.out.println("第十步,hi"+ name);
    }
    public PersonService(){
        System.out.println("第一步,实例化bean");
    }
    @Override
    public void setBeanName(String arg0){
        //该方法可以给出正在被调用的bean的id
        System.out.println("第三步,setBeanName被调用,调用的id名为:"+arg0);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        //该方法可以传递beanFactory
        System.out.println("第四步,setBeanFactory被调用,beanFactory为:"+beanFactory);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //该方法传递一个ApplicationContext
        System.out.println("第五步,调用setApplicationContext方法:"+applicationContext);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第七步,调用afterPropertiesSet()方法");
    }
    public void init(){
        System.out.println("第八步、调用我自己的init()方法");
    }

    @Override
    public void destroy() throws Exception {
        //关闭数据连接,socket,文件流,释放资源
        //这个函数的打印你看不到,应为
        System.out.println("第十步,销毁方法(但不建议使用这种方式释放资源)");
    }
    public void destory(){
//        也看到不
        System.out.println("销毁");
    }
}

Test.java

package com.nuc.BeanLife;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
        PersonService person1= (PersonService) ac.getBean("personService");
        person1.sayHi();
    }
}

spring-config.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="personService" init-method="init" destroy-method="destroy" scope="singleton" class="com.nuc.BeanLife.PersonService">
        <property name="name" value="sjt"></property>
    </bean>
    <bean id="personService2" class="com.nuc.BeanLife.PersonService">
        <property name="name" value="sjt2"></property>
    </bean>
    <!--配置自己的后置处理器,优点类似filter-->
    <bean id="myBeanPostProcessor" class="com.nuc.BeanLife.MyBeanPostProcessor">

    </bean>
</beans>

测试结果

417, 2018 4:57:26 下午 
信息: Loading XML bean definitions from class path resource [spring-config.xml]
第一步,实例化bean
第二步调用set方法
第三步,setBeanName被调用,调用的id名为:personService
第四步,setBeanFactory被调用,beanFactory为:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,调用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被调用
第六步,com.nuc.BeanLife.PersonService@2e6a8155被创建的时间为Tue Apr 17 16:57:27 CST 2018
第七步,调用afterPropertiesSet()方法
第八步、调用我自己的init()方法
第九步,postProcessAfterInitialization方法被调用
第一步,实例化bean
第二步调用set方法
第三步,setBeanName被调用,调用的id名为:personService2
第四步,setBeanFactory被调用,beanFactory为:org.springframework.beans.factory.support.DefaultListableBeanFactory@ae13544: defining beans [personService,personService2,myBeanPostProcessor]; root of factory hierarchy
第五步,调用setApplicationContext方法:org.springframework.context.support.ClassPathXmlApplicationContext@646d64ab: startup date [Tue Apr 17 16:57:26 CST 2018]; root of context hierarchy
第六步,postProcessBeforeInitialization方法被调用
第六步,com.nuc.BeanLife.PersonService@6221a451被创建的时间为Tue Apr 17 16:57:27 CST 2018
第七步,调用afterPropertiesSet()方法
第九步,postProcessAfterInitialization方法被调用
第十步,hisjt

Process finished with exit code 0

动手做一遍是最好的选择!!
使用bean工厂获取bean对象,生命周期是和上下文获取的不一样的,如下图
这里写图片描述
其中我只装配了一个bean,可见执行步骤的短缺

七、装配Bean

  • 使用xml装配

    • 上下文定义文件的根元素是,有多个子元素,每个元素定义了bean如何被装配到spring容器中
    • 对子元素bean最基本的配置包括bean的ID和它的全称类名
    • 对bean的scope装配,默认情况下为单例模式,具体情况上面已经说过,建议查看文档,更加具体,尽量不要使用原型bean,即scope设置为propotype,这样子会对性能有较大的影响
    • bean的init-methodestory-method的书写,在生命周期那一块儿已经很清楚了,此处不再赘述,需要说明的是,可以通过注解的方式来配置,而不是在bean中使用init-metho和destory-method属性
      这里写图片描述

    • 注入集合类型的数据,例如,map,set,list,数组,Properties….

      • 接下来举例子
        目录结构:
        这里写图片描述

        Department.java

        package com.nuc;
        
        import java.util.List;
        import java.util.Map;
        import java.util.Set;
        
        public class Department {
            private String name;
            private String []empName;//这里int的数组也可以注入成功
            private List<Employee> empList;
            private Map<String,Employee> empMap;
            private Properties pp;
        
            public Properties getPp() {
                return pp;
            }
        
            public void setPp(Properties pp) {
                this.pp = pp;
            }
        
            public Set<Employee> getEmpSet() {
                return empSet;
            }
        
            public void setEmpSet(Set<Employee> empSet) {
                this.empSet = empSet;
            }
        
            private Set<Employee> empSet;
            public List<Employee> getEmpList() {
                return empList;
            }
        
            public void setEmpList(List<Employee> empList) {
                this.empList = empList;
            }
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public String[] getEmpName() {
                return empName;
            }
        
            public void setEmpName(String[] empName) {
                this.empName = empName;
            }
        
            public Map<String, Employee> getEmpMap() {
                return empMap;
            }
        
            public void setEmpMap(Map<String, Employee> empMap) {
                this.empMap = empMap;
            }
        }
        

        Employee.java

        package com.nuc;
        
        public class Employee {
            private String name;
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        }
        

        Test.java

        package com.nuc;
        
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        import java.util.Iterator;
        import java.util.Map;
        import java.util.Map.Entry;
        
        public class Test {
            public static void main(String[] args) {
                ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
                Department department = (Department)ac.getBean("department");
                System.out.println(department.getName());
        //        取集合
                for(String empName:department.getEmpName()){
                    System.out.println(empName);
                }
                System.out.println("取list...");
                for (Employee e:department.getEmpList()){
                    System.out.println("name="+e.getName());
                }
                System.out.println("取set...");
                for (Employee e:department.getEmpSet()){
                    System.out.println("name="+e.getName());
                }
                System.out.println("迭代器取map...");
                //1.迭代器
                Map<String,Employee> employeeMap = department.getEmpMap();
                Iterator iterator = employeeMap.keySet().iterator();
                while (iterator.hasNext()){
                    String key = (String)iterator.next();
                    Employee employee=employeeMap.get(key);
                    System.out.println("key="+key+" "+ employee.getName());
                }
                System.out.println("entry取map...");
                //2.简洁(建议使用这种方式)
                for (Entry<String,Employee> entry:department.getEmpMap().entrySet()){
                    System.out.println(entry.getKey()+" "+entry.getValue().getName());
                }
            }
            System.out.println("通过properties取数据");
            Properties properties = department.getPp();
            for (Entry<Object,Object> entry:properties.entrySet()){
                System.out.println(entry.getKey().toString()+" "+entry.getValue());
            }
        }
        

        spring-config.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="department" class="com.nuc.Department">
                <property name="name" value="财务部"></property>
        
                <!--给数组注入-->
                <property name="empName">
                    <list>
                        <value>小明</value>
                        <value>小花</value>
                    </list>
                </property>
        
                <!--给list注入-->
                <!--list可以存放相同的对象,并当作不同对象输出-->
                <property name="empList">
                    <list>
                        <ref bean="employee1"></ref>
                        <ref bean="employee2"></ref>
                    </list>
                </property>
        
                <!--给set注入-->
                <!--set集合不可以存放相同对象-->
                <property name="empSet">
                    <set>
                        <ref bean="employee1"></ref>
                        <ref bean="employee2"></ref>
                    </set>
                </property>
        
                <!--给map注入-->
                <!--输出的对象取决于key值,key值不同,对象相同也可以打出-->
                <!--当key值相同时,对象相同或者不同都打出最后一个key所对应的对象-->
                <property name="empMap">
                    <map>
                        <entry key="1" value-ref="employee1"></entry>
                        <entry key="2" value-ref="employee2"></entry>
                        <entry key="3" value-ref="employee2"></entry>
                    </map>
                </property>
                <!--给属性集合注入-->
                <property name="pp">
                    <props>
                        <prop key="1">hello</prop>
                        <prop key="2">world</prop>
                    </props>
                </property>
            </bean>
        
            <bean id="employee1" class="com.nuc.Employee">
                <property name="name" value="北京"></property>
            </bean>
            <bean id="employee2" class="com.nuc.Employee">
                <property name="name" value="太原"></property>
            </bean>
        </beans>

        测试结果:
        这里写图片描述

      注意点,细节都已在代码中注释!

      • 内部bean
        • 具体自行了解,实际中应用不多,不符合重用度高的原则
      • 继承配置bean
        • 举例说明:
          结构图:
          这里写图片描述
          Student.java
package com.nuc.inherit;

public class Student {
    protected String name;
    protected int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Gradate.java

package com.nuc.inherit;

public class Gradate extends Student {
    private String degree;

    public String getDegree() {
        return degree;
    }

    public void setDegree(String degree) {
        this.degree = degree;
    }
}

spring-config.java

<?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.nuc.inherit.Student">
        <property name="name" value="sjt"></property>
        <property name="age" value="22"></property>
    </bean>
    <!--配置gradate对象-->
    <bean id="gradate" parent="student" class="com.nuc.inherit.Gradate">
        <!--如果子类重新赋值,则覆盖父类的-->
        <property name="name" value="小明"></property>
        <property name="degree" value="博士"></property>
    </bean>
</beans>

Test2.java

package com.nuc.inherit;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/inherit/spring-config.xml");
        Gradate gradate = (Gradate) ac.getBean("gradate");
        System.out.println(gradate.getName()+" "+gradate.getAge()+" "+gradate.getDegree());
    }
}
  • 以上我们都是用set注入依赖的,下面介绍构造函数注入依赖
<bean name="user" class="com.nuc.Bean.User">
    <!--通过constructor-arg标签完成了对构造方法的传参-->
    <!--如果是属性是类类型,则使用ref=""-->
    <constructor-arg index="0" type="java.lang.String" value="小强"></constructor-arg>
    <constructor-arg index="1" type="java.lang.String" value="男"></constructor-arg>
    <constructor-arg index="2" type="int" value="20"></constructor-arg>
</bean>

当然对应的User要有相应的构造方法。
set注入的缺点是无法清晰的表达哪个属性是必须的,哪些是可选的,构造器注入的优势,是可以通过构造强制依赖关系,不可能实例化不完全或者不能使用的bean

但其实实际开发中还是set注入较多,即property注入

  • bean的自动装配:
    这里写图片描述
    接下来是实例:
    目录图
    这里写图片描述
    Dog.java
package com.nuc.autowire;

public class Dog {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Master.java

package com.nuc.autowire;

public class Master {
    private String name;
    private Dog dog;

    private Master(Dog dog){
        //为了自动装配的constructor
        this.dog= dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }
}

Test.java

package com.nuc.autowire;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com\\nuc\\autowire\\beans.xml");
        Master master = (Master)ac.getBean("master");
        System.out.println(master.getName()+"养了只狗,它的名字叫"+ master.getDog().getName()+",他今年"+master.getDog().getAge()+"岁了");
    }
}

beans.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">
    <!--配置master对象-->
    <bean id="master" class="com.nuc.autowire.Master" autowire="constructor">
        <property name="name" value="sjt"></property>
        <!--传统方式-->
        <!--<property name="dog" ref="dog"></property>-->
    </bean>

    <!--配置dog对象,byName时使用-->
    <!--<bean id="dog" class="com.nuc.autowire.Dog">-->
        <!--<property name="name" value="小黄"></property>-->
        <!--<property name="age" value="2"></property>-->
    <!--</bean>-->

    <!--配置dog对象,byType时使用-->
    <!--<bean id="dog11" class="com.nuc.autowire.Dog">-->
        <!--<property name="name" value="小黄"></property>-->
        <!--<property name="age" value="2"></property>-->
    <!--</bean>-->

    <!--配置dog对象,constructor时使用-->
    <bean id="dog22" class="com.nuc.autowire.Dog">
        <property name="name" value="小黄"></property>
        <property name="age" value="2"></property>
    </bean>
</beans>
  • autodetect:是在constructor和byType之间选一种
  • default:这种方式在文档中没有提及,需要在beans中指定,当你在beans中指定以后,所有的bean都是你所指定的装配方式,如果没有指定,则默认为no,所以,no之所以为默认指定装配方式,其实是从beans那里来的
    这里写图片描述
    其实在实际开发中,很少用到自动装配, 一般都是手动set装配的(property),而且自动装配也是在bean中没有配置才取执行自动装配的

    • spring本身提供的bean
      • 分散配置

八、AOP编程(难点)

  • aop:aspect oriented programming(面向切面编程),它是对一类对象或所有对象编程。

    • 核心:在不增加代码的基础上,还增加新功能
    • 提醒:aop编程,实际上是开发框架本身用的多,开发中不是很多,将来会很多
    • 初步理解:面向切面:其实是,把一些公共的“东西”拿出来,比如说,事务,安全,日志,这些方面,如果你用的到,你就引入。

    接下来通过例子来理解这个抽象的概念,概念稍后再说

    步骤:
    拿前置通知打比方,后来还会有,后置通知,环绕通知,异常通知,引入通知

    • 定义接口
    • 编写对象(被代理对象=目标对象)
    • 编写通知(前置通知目标方法调用前调用)
    • 在beans.xml中配置
      • 配置被代理对象
      • 配置通知
      • 配置代理对象(是proxyFactoryBean的对象实例)
        • 配置代理接口集
        • 织入通知
        • 配置被代理对象

    接下来看代码:
    目录结构:
    这里写图片描述
    TestServiceInter.java(interface)

package com.nuc.Aop;

public interface TestServiceInter {
    public void sayHello();
}

TestServiceInter2.java(interface)

package com.nuc.Aop;

public interface TestServiceInter2 {
    public void sayBye();
}

TestService.java

package com.nuc.Aop;

public class TestService implements TestServiceInter,TestServiceInter2{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void sayHello(){
        System.out.println("hi "+name);
    }

    @Override
    public void sayBye() {
        System.out.println("bye "+name);
    }
}

MyMethodBeforeAdvice.java

package com.nuc.Aop;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    //前置通知
    @Override
    public void before(Method method, Object[] objects, Object o)
            throws Throwable {
        //method:被调用方法的名字
        //objects:给method传递的参数
        //o:目标对象
        System.out.println("记录日志。。。"+method.getName());

    }
}

beans.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="testService" class="com.nuc.Aop.TestService">
        <property name="name" value="sjt"/>
    </bean>
    <!--配置前置通知-->
    <bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
    <!--配置代理对象-->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置代理接口-->
        <property name="proxyInterfaces">
            <list>
                <value>com.nuc.Aop.TestServiceInter</value>
                <value>com.nuc.Aop.TestServiceInter2</value>
            </list>
        </property>
        <!--把通知织入到代理对象-->
        <property name="interceptorNames">
            <!--相当于把myMethodBeforeAdvice前置通知和代理对象关联起来-->
            <value>myMethodBeforeAdvice</value>
        </property>
        <!--配置被代理对象,可以指定-->
        <property name="target" ref="testService"></property>
    </bean>
</beans>

Test.java

package com.nuc.Aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("com/nuc/Aop/beans.xml");
        TestServiceInter testService = (TestServiceInter)ac.getBean("proxyFactoryBean");
        testService.sayHello();
        //当一个类继承多个接口,那么他们之间可以互转
        ((TestServiceInter2)testService).sayBye();
    }
}

测试结果
这里写图片描述

  • AOP的术语

    • 切面:要实现交叉功能,是系统模块化的一个切面领域,如记录日志
    • 连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段
      • 连接点是一个静态的概念
    • 通知: 切面的实际实现,它通知系统的新行为,如日志通知包含了实现日志功能的代码,如向日志文件写日志,通知在连接点插入应用系统中。
    • 切入点:定义了通知应该应用在哪些连接点通知可以应用到AOP框架支持的任何连接点
      • 切入点是动态概念,当通知应用了连接点,连接点就变成了切入点
    • 引入:为类添加新方法和属性
    • 目标对象:通知的对象,既可以是你编写的类,也可以是第三方类
    • 代理:通知应用到目标对象后创建后的对象,应用系统的其他部分不用为了支持代理对象而改变
      • spring的两种代理:
        • 目标对象实现了若干个接口,spring使用JDK的java.lang.reflect.Proxy类代理
        • 目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类
    • 织入:切面应用到目标对象从而创建一个新代理对象的过程,织入发生在目标对象生命周期的多个点上
      • 编译期:切面在目标对象编译时织入,这需要一个特使的编译器
      • 类装载期:切面在目标对象被载入jvm时织入,这需要一个特殊的类加载器
      • 运行期:切面在应用系统运行时切入

    接下来引入后置通知,环绕通知,异常通知,引用通知

    类似于前置通知,前三者需要继承一种接口,引用通知直接配置

MyAfterReturningAdvice.java

package com.nuc.Aop;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterReturningAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects,
                               Object o1)
            throws Throwable {
        //后置通知
        //o:前面函数的返回值
        //method:哪个方法被调用
        //objects:调用方法的参数
        //o1:目标对象
        System.out.println("后置通知:调用结束,关闭资源。");
    }
}

MyMethodInterceptor.java

package com.nuc.Aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //环绕通知
        System.out.println("环绕通知:进入函数体,调用方法前");
        Object obj = methodInvocation.proceed();
        System.out.println("环绕通知:完成调用");
        return obj;
    }
}

MyThrowsAdvice.java

package com.nuc.Aop;

import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

public class MyThrowsAdvice implements ThrowsAdvice {
    //异常通知
    //ThrowsAdvice这个接口是标识性接口,没有任何方法
   public void afterThrowing(Method m,Object[] os,Object target,Exception e){
        System.out.println("异常通知:出问题了:"+e.getMessage());
    }
}

beans.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="testService" class="com.nuc.Aop.TestService">
        <property name="name" value="sjt"/>
    </bean>
    <!--配置前置通知-->
    <bean id="myMethodBeforeAdvice" class="com.nuc.Aop.MyMethodBeforeAdvice"></bean>
    <!--配置后置通知-->
    <bean id="myAfterReturningAdvice" class="com.nuc.Aop.MyAfterReturningAdvice"></bean>
    <!--配置环绕通知-->
    <bean id="myMethodInterceptor" class="com.nuc.Aop.MyMethodInterceptor"></bean>
    <!--配置异常通知-->
    <bean id="myThrowsAdvice" class="com.nuc.Aop.MyThrowsAdvice"></bean>
    <!--定义前置通知的切入点(引用通知)-->
    <bean id="myMethodBeforeAdviceFilter" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
        <property name="advice" ref="myMethodBeforeAdvice"></property>
        <property name="mappedNames">
            <list>
                <!--这里支持使用正则表达式匹配-->
                <!--配置了sayHello使用前置通知过滤-->
                <value>sayHello</value>
            </list>
        </property>
    </bean>
    <!--配置代理对象-->
    <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置代理接口-->
        <property name="proxyInterfaces">
            <list>
                <value>com.nuc.Aop.TestServiceInter</value>
                <value>com.nuc.Aop.TestServiceInter2</value>
            </list>
        </property>
        <!--把通知织入到代理对象-->
        <property name="interceptorNames">
            <list>
                <!--相当于把myMethodBeforeAdvice前置通知和代理对象关联起来-->
                <!--使用自定义切入点-->
                <value>myMethodBeforeAdviceFilter</value>
                <!--织入后置通知-->
                <value>myAfterReturningAdvice</value>
                <!--织入环绕通知-->
                <value>myMethodInterceptor</value>
                <!--织入异常通知-->
                <value>myThrowsAdvice</value>
            </list>
        </property>
        <!--配置被代理对象,可以指定-->
        <property name="target" ref="testService"></property>
    </bean>
</beans>

TestService.java
这里写图片描述
如图这一处变动
总之呢就是一个配置 -> 织入的过程
运行结果:
这里写图片描述
可以看到前置通知和后置通知,似乎能够识别方法,事实上也是这样的(spring框架内置)。而且sayBay()也得到了应用。这正是,我们前面所提到了,AOP是对一类或所有对象编程的体现,又由于异常通知的配置,有了异常,由于引用通知的配置,致使sayBay的前置通知及后续无法通知。
正常结果(配置引用通知):
这里写图片描述

总结

spring框架呢,其实就是学习了一大堆的配置,还有几个spring比较新颖的“思想”,IOC,AOP,这些技术。主要还是spring-config.xml文件的配置,之所以后来改成beans,是因为,这个文件就是对bean的配置!这套教程,是根据韩老师的视频总结的,全文基本都是要点,没有水货。。。(自我认为),因为韩老师将的很到位,很深刻,所以教程自然差不了

为时5天的总结就结束了。笔者后期还会推出springMVC,Hibernate,望支持~

猜你喜欢

转载自blog.csdn.net/song_jiangtao/article/details/79949633
今日推荐