Spring

IOC/DI

DI 的优势:解耦

紧耦合:软件组件之间是紧密依赖,很难拆分

松耦合:软件组件之间是松散依赖,随时可以更换

解耦:将软件组件从紧耦合转换为松耦合关系。

IOC、DI

利用Spring 管理光头强和工具之间的依赖关系

  1. 创建工具接口

    /**
     * 工具接口 
     */
    public interface Tool {
    
    }
    
  2. 创建具体的工具 斧子

    public class Axe implements Tool, Serializable {
    
        @Override
        public String toString() {
            return "斧子";
        }
    }
    
  3. 创建锯

    public class Saw implements Tool, Serializable{
    
        @Override
        public String toString() {
            return "电锯";
        }
    }
    
  4. 创建工人类型

    public class Worker implements Serializable {
    
        private String name; //基本值
    
        private Tool tool;   //Bean 对象
    
        private int age;     //基本值,
    
    
        public Worker() {
            name = "光头强";
        }
    
        public Worker(String name) {
            super();
            this.name = name;
        }
    
        public void setTool(Tool tool) {
            this.tool = tool;
            System.out.println("setTool:"+tool);
        }
        public Tool getTool() {
            return tool;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            System.out.println("setName:"+name);
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
            System.out.println("setAge:"+age);
        }
    
        public void work() { //测试方法
            System.out.println(name+"使用"+tool+"砍树"); 
        }
        @Override
        public String toString() {
            return "Worker [name=" + name + ", tool=" + tool + "]";
        }
    }
    
  5. 在Spring配置文件中利用 IOC/DI 解决依赖关系

    <!-- 控制反转 IOC:由Spring创建对象称为控制反转 -->
    <bean id="axe" class="day02.Axe"/>  
    <bean id="saw" class="day02.Saw"/>
    <bean id="qiang" class="day02.Worker">
        <!-- 依赖注入DI:由Spring帮助建立对象的依赖关系 -->
        <!-- spring会自动调用 setTool 方法注入 axe 引用 -->
        <property name="tool" ref="saw"/> 
    </bean>
    
  6. 测试

    public class TestCase {
    
        ClassPathXmlApplicationContext ctx;
    
        @Before
        public void init() {
            ctx=new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
        }
    
        @After
        public void destroy() {
            ctx.close();
        }
    
        @Test
        public void testQiang() {
            Worker qiang = ctx.getBean("qiang",Worker.class);
            qiang.work();
        }   
    
    }
    

构造器参数注入

Spring支持 利用 构造器创建对象:constructor-arg 子元素,其中index="0"用于指定参数在构造器参数的位置。

  1. 配置

    <bean id="xiong" class="day02.Worker" lazy-init="true">
        <!-- 构造器参数注入,也就是调用有参数
        构造器创建对象 -->
        <constructor-arg index="0" value="熊大"/>
        <property name="tool" ref="saw"/> 
    </bean>
    
  2. 测试

    @Test
    public void testXiong() {
        Worker xiong = ctx.getBean("xiong",Worker.class);
        xiong.work();
    }
    

按照名字自动注入

Spring支持自动按照名字注入功能,Spring会自动检查Bean属性和Bean ID的匹配,如果匹配上就自动完成注入

优点:自动注入可以减少XML文件的编写量。

  1. 编写配置文件

    <bean id="tool" class="day02.Axe" lazy-init="true"/>
    <!-- autowire="byName" 自动按照 Bean属性名字和id的
    名字 对应关系进行自动注入: bean属性叫tool匹配ID tool
    称为按照名字自动装配  autowire="byName"-->
    <bean id="tom" class="day02.Worker" 
        autowire="byName"/>
    
  2. 测试

    @Test
    public void testTom() {
        Worker tom= ctx.getBean("tom",Worker.class);
        tom.work();
    }
    

    如果测试结果数 tool 属性不为空,则说明自动注入成功了

按照类型自动注入

为了实现按照类型注入,需要利于新的配置文件完成测试:

  1. 编写 applicationContext1.xml

    <!-- 测试  autowire="byType" 按照类型自动
    装配,解决对象的依赖关系 -->
    <bean class="day02.Axe"/>   
    <bean id="tom" class="day02.Worker" 
        autowire="byType"/>
    
  2. 测试:

    public class TestCase2 {
    
        ClassPathXmlApplicationContext ctx;
        @Before
        public void init() {
            ctx=new ClassPathXmlApplicationContext(
                    "applicationContext2.xml");
        }
        @After
        public void destroy() {
            ctx.close();
        }
        @Test
        public void testTom() {
            Worker tom= ctx.getBean("tom",Worker.class);
            tom.work();
        }
    
    }
    

Spring基本值注入

Spring支持为Bean注入基本类型参数, 这里的基本类型包括:8个基本类,8个包装类型,String类型。

Spring注入基本值时候也是调用的“Bean属性”方法实现的。

  1. 配置

    <!-- 测试基本值的注入
      基本值包含:8个基本类,8个包装类型,String类型 -->
    <bean id="andy" class="day02.Worker" lazy-init="true">
        <!-- 注入 JavaBean 对象,使用ref属性 -->
        <property name="tool" ref="axe"></property>
        <!-- 注解基本值,使用value属性 -->
        <property name="name" value="熊二"></property>
        <property name="age" value="10"></property>
    </bean>
    
  2. Worker类

    public class Worker implements Serializable {
    
        private String name; //基本值
    
        private Tool tool;   //Bean 对象
    
        private int age;     //基本值,
    
        private List<Tool> tools; //注入集合
    
        public Worker() {
            name = "光头强";
        }
    
        public Worker(String name) {
            super();
            this.name = name;
        }
    
        public void setTool(Tool tool) {
            this.tool = tool;
            System.out.println("setTool:"+tool);
        }
        public Tool getTool() {
            return tool;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            System.out.println("setName:"+name);
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
            System.out.println("setAge:"+age);
        }
    
        public List<Tool> getTools() {
            return tools;
        }
    
        public void setTools(List<Tool> tools) {
            this.tools = tools;
        }
    
        public void work() { //测试方法
            System.out.println(name+"使用"+tool+"砍树"); 
        }
        @Override
        public String toString() {
            return "Worker [name=" + name + ", tool=" + tool + "]";
        }
    }
    
  3. 测试:

    @Test
    public void testAndy() {
        Worker andy= ctx.getBean("andy",Worker.class);
        andy.work();
    }
    

案例:利用Spring配置数据源

  1. 导入DBCP和MySQL驱动

    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>               
    
  2. 配置:

    <!-- 利用Spring 创建 DataSource 连接池实例 -->
    <bean id="dataSource" 
        class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" 
            value="com.mysql.jdbc.Driver"/>
        <property name="url" 
            value="jdbc:mysql://localhost:3306/jsd1810db"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxActive" value="10"/>
        <property name="initialSize" value="2"/>
    </bean> 
    
  3. 测试:

    @Test
    public void testDataSource() 
        throws SQLException {
        //提示: BasicDataSource 实现了 DataSource接口
        //所以 使用 DataSource创建变量可以引用
        // BasicDataSource 对象实例
        DataSource ds = ctx.getBean("dataSource",
                DataSource.class);
        System.out.println(ds); 
        Connection conn = ds.getConnection();
        //执行SQL版本的Hello World
        String sql="select 'Hello World!' as str";
        Statement st=conn.createStatement();
        ResultSet rs=st.executeQuery(sql);
        while(rs.next()) {
            System.out.println(rs.getString("str"));
        }
        rs.close();
        st.close();
        conn.close();
    }
    

    如果能够显示 Hello World 说明能够访问到数据库

Spring 集合注入

当一个对象Bean属性是一个集合时候,可以利用Spring注入一个集合:

  1. Worker类包含集合属性:

    public class Worker implements Serializable {
    
        private String name; //基本值
    
        private Tool tool;   //Bean 对象
    
        private int age;     //基本值,
    
        private List<Tool> tools; //注入集合
    
        public Worker() {
            name = "光头强";
        }
    
        public Worker(String name) {
            super();
            this.name = name;
        }
    
        public void setTool(Tool tool) {
            this.tool = tool;
            System.out.println("setTool:"+tool);
        }
        public Tool getTool() {
            return tool;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
            System.out.println("setName:"+name);
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
            System.out.println("setAge:"+age);
        }
    
        public List<Tool> getTools() {
            return tools;
        }
    
        public void setTools(List<Tool> tools) {
            this.tools = tools;
        }
    
        public void work() { //测试方法
            System.out.println(name+"使用"+tool+"砍树"); 
        }
        @Override
        public String toString() {
            return "Worker [name=" + name + ", tool=" + tool + "]";
        }
    }
    
  2. 配置:

    <!-- 为Bean对象注入集合 -->
    <bean id="john" class="day02.Worker">
        <!-- Bean对象注入 -->
        <property name="tool" ref="axe"></property>
        <!-- 基本值注入 -->
        <property name="name" value="大表哥"/>
        <!-- List集合注入 -->
        <property name="tools">
            <list>
                <bean class="day02.Saw"/>
                <bean class="day02.Axe"/>
                <bean class="day02.Saw"/>
            </list>
        </property>
    </bean>
    
  3. 测试:

    @Test
    public void testList() {
        Worker john = ctx.getBean("john", Worker.class);
        System.out.println(john.getTools()); 
    }
    

    如果输出了集合的内容,不为空,则注入成功

Spring读取Properties文件

Spring提供了读取Properties功能,Properties经常用于存储软件的参数:

  1. 在resource文件夹中编写配置文件 jdbc.properties

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jsd1810db?useUnicode=true&characterEncoding=UTF-8
    username=root
    password=root
    initSize=3
    maxSize=5
    
  2. 配置

    <!-- 利用Spring读取Properties文件 
    location: 位置,定位,这里是文件位置 -->
    <util:properties id="jdbc" 
        location="classpath:jdbc.properties"/>
    
  3. 测试:

    @Test
    public void testProperties() {
        Properties cfg=ctx.getBean("jdbc",
                Properties.class);
        System.out.println(cfg); 
    }
    

Spring 表达式

Spring表达式的语法与EL表达式类似:

  1. EL表达式: ${} Spring表达式: #{}
  2. Spring表达式也可以读取
  3. 在Spring文件中,可以利用Spring表达式,将一个Bean对象的属性,赋值给另外一个Bean。

使用Spring 表达式配置数据源:

  1. 配置:

    <!-- 利用Spring读取Properties文件 
    location: 位置,定位,这里是文件位置 -->
    <util:properties id="jdbc" 
        location="classpath:jdbc.properties"/>
    
    <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
        <!-- 读取 jdbc的driver属性,将值赋值给
         driverClassName 属性-->
        <property name="driverClassName"
            value="#{jdbc.driver}"/>
        <property name="url" 
            value="#{jdbc.url}"/>
        <property name="username"
            value="#{jdbc.username}"/>
        <property name="password"
            value="#{jdbc.password}"/>
        <property name="initialSize"
            value="#{jdbc.initSize}"/>
        <property name="maxActive"
            value="#{jdbc.maxSize}"/>
    </bean> 
    
  2. 测试:

    @Test
    public void testDS() 
        throws SQLException {
        //提示: BasicDataSource 实现了 DataSource接口
        //所以 使用 DataSource创建变量可以引用
        // BasicDataSource 对象实例
        DataSource ds = ctx.getBean("ds",
                DataSource.class);
        System.out.println(ds); 
        Connection conn = ds.getConnection();
        //执行SQL版本的Hello World
        String sql="select 'Hello World2!' as str";
        Statement st=conn.createStatement();
        ResultSet rs=st.executeQuery(sql);
        while(rs.next()) {
            System.out.println(rs.getString("str"));
        }
        rs.close();
        st.close();
        conn.close();
    }
    

Spring 注解

Spring 提供了一系列注解,用于管理JavaBean。

  1. 注解是Java语法,被Java编译器检查,可以减少配置错误
  2. 注解提供了默认支持功能,默认情况下会自动完成注入功能
  3. 注解和XML配置可以混合使用。
    1. 自己写的类使用注解
    2. 别人写的API使用XML配置

利用Spring注解创建Bean

  1. 创建Java类,标注注解

    package day02.bean;
    
    @Component //默认的BeanID为 "demoBean"
    public class DemoBean implements Serializable{
    
        @Override
        public String toString() {
            return "DemoBean";
        }
    }
    

    Spring 会自动查找注解@Componment 找到后自动实例 DemoBean 并且自动分配Bean的ID: demoBean

  2. 需要在XML配置文件中开启注解扫描功能。

    <context:component-scan base-package="day02.bean"/>
    

    base-package="day02.bean" 指定Spring扫描组件的包范围

  3. 测试:

    @Test
    public void testDemoBean() {
        DemoBean bean = ctx.getBean("demoBean",
                DemoBean.class);
        System.out.println(bean); 
    }
    

Spring 支持多个组件注解

这些注解功能是一样的,建议按照组件层次使用组件组件。

@Component 通用组件 @Named 通用组件 @Repository 持久层组件 @Service 业务层组件 @Controller 控制器

其中@Named来自 javax.inject 包,需要利用Maven导入

案例:

  1. 声明类

    @Repository // BeanID 是 demoBean2
    public class DemoBean2 {
    
        @Override
        public String toString() {
            return "DemoBean2";
        }
    }
    
  2. 测试

    @Test
    public void testDemoBean2() {
        DemoBean2 bean = ctx.getBean("demoBean2",
                DemoBean2.class);
        System.out.println(bean); 
    }
    

自定义BeanID

如果需要修改默认的BeanID,可以指定BeanID

  1. 声明类

    @Component("myBean") //自定义BeanID为 myBean
    public class TestBean implements Serializable{
    
        @Override
        public String toString() {
            return "testBean";
        }
    }
    
  2. 测试

    @Test
    public void testMyBean() {
        TestBean bean = ctx.getBean("myBean",
                TestBean.class);
        System.out.println(bean); 
    }
    

@Scope 创建多个实例

声明类:

@Component
@Scope("prototype") //创建多个对象实例
public class DemoBean3 {

}

测试:

@Test
public void testDemoBean3() {
    DemoBean3 bean1 = ctx.getBean("demoBean3",
            DemoBean3.class);
    DemoBean3 bean2 = ctx.getBean("demoBean3",
            DemoBean3.class);
    System.out.println(bean1==bean2);

}

管理对象的声明周期

@PostConstruct 构造器之后执行方法 @PreDestroy 在销毁之前执行的方法

  1. 导入注解包

    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>
    
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
    </dependency>
    
  2. 编写类:

    @Named
    public class Writer implements Serializable{
    
        private PrintWriter out;
    
        //Post 在..以后,Construct:构造器
        @PostConstruct //在构造器以后执行 init方法
        public void init() throws Exception {
            out = new PrintWriter("demo.txt");
            System.out.println("init()");
        }
        public void write(String str) {
            out.println(str);
        }
    
        //Pre 前, Destroy 销毁
        @PreDestroy //在销毁之前执行close()
        public void close() {
            out.flush();
            out.close();
            System.out.println("close()");
        }
    }
    
  3. 测试

    @Test
    public void testInitDestroy() {
        Writer writer = ctx.getBean("writer",
                Writer.class);
        writer.write("Hello World!"); 
    }