Spring的IoC和DI

IOC控制反转

使用对象时,由主动new产生对象转换为由外部提供对象 ,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。
Spring技术对Ioc思想进行了实现
Spring提供了一个容器,称为Ioc容器,用来充当Ioc思想中的外部
Ioc容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在Ioc容器中统称为Bean

DI依赖注入

在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入。

在这里插入图片描述
在这里插入图片描述
1、导入spring的坐标spring-context
2、配置bean,bean标签标识配置bean,id属性表示给bean起名字,class表示给bean定义类型

<bean id="bookDao" class="com.zgdaren.dao.impl.BookDaoImpl">
<bean id="bookService" class="com.zgdaren.service.impl.BookServiceImpl">

3、在启动类中获取Ioc容器
4、使用getBean获取bean

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)app.getBean("userDao");
        userDao.save();

5、删除业务层中使用new的方式创建的dao对象

private BookDao bookDao;

6、提供对应的set方法

public void setBookDao(BookDao bookDao){
this.bookDao = bookDao;
}

7、配置service 和dao的关系

<bean id="userService" class="com.zg.service.impl.UserServiceImpl">
        <!--property 表示排石当前Bean的属性,这里的name表示配置哪一个具体的属性-->
        <!--这里对象的引用使用ref(表示参照哪一个具体的属性)-->
        <!--把容器中的userDao 通过userService内部的setUserDao这个方法注入给userService-->
        <property name="userDao" ref="userDao"></property>
    </bean>

bean的配置

bean别名配置
类名描述
名称name
类型属性
所属bean标签
功能定义bean的别名,可定义多个,使用(,)分号(;)空格( )分割
范例<bean id="userService" name = "dao userDaoImpl" class="com.zg.service.impl.UserServiceImpl">

获取bean无论是通过id还是name获取,如果无法获取到,将抛出异常NoSuchBeanDefinitionException

Spring

Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(翻转控制)和AOP(面向切面编程)为内核
提供了展现层SpringMVC和持久层Spring JDBCTemplatey以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多的第三方框架和类库,逐渐成为使用最多的Java EE企业级应用开源框架

Spring优势

1、方便解耦,简化开发
通过Spring提供的IOC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的多度耦合,用户不必再为单例模式、属性文件解析等这些底层的需求编写代码,可以更专注于上层的应用
2、AOP编程的支持
通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传统OOP实现的功能可以通过AOP轻松实现
3、声明式事务的支持
可以将我们从单调烦闷的事务代理代码中解脱出来,通过声明式灵活的进行事务管理,提高开发效率和质量
4、方便程序的测试
可以用非容器依赖的编程方式进行几乎所欲的测试工作,测试不再是昂贵的操作,而是随手可做的事情
5、方便继承各种优秀的框架
Spring对各种优秀的框架(Struts、Hibemate、Hessian、Quartz等)的支持
6、降低Java EE API的使用难度
Spring 对Java EE API(JDBC、JavaMail、远程调用等)进行了薄薄的封装,使用这些API的使用难度大为降低
7、Java源码是经典的学习范例
Spring的源代码设计精妙,结构清晰,匠心独具,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣

Spring体系结构

在这里插入图片描述

Spring程序开发步骤

1、导入Spring开发的基本包坐标

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.7</version>
        </dependency></dependencies>

2、编写Dao接口和实现类
UserDao接口

package com.zg.dao;

public interface UserDao {
    public void save();
}

UserDao实现类

package com.zg.dao.impl;

import com.zg.dao.UserDao;

public class userDaoIMPL implements UserDao {
    @Override
    public void save() {
        System.out.println("save running.....");
    }
}

3、创建Spring核心配置文件

<?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="userDao" class="com.zg.dao.impl.userDaoIMPL"></bean>
</beans>

4、在Spring配置文件中配置UserDaoImpl

<bean id="userDao" class="com.zg.dao.impl.userDaoIMPL"></bean>

5、使用Spring的API获得Bean实例

package com.zg.demo;

import com.zg.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;

public class UserDaoDemo {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)app.getBean("userDao");
        userDao.save();
    }
}

Spring配置文件详解

Bean标签基本配置

用于配置对象交由Spring来创建
默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功
id:Bean实例在Spring容器中的唯一标识
class:Bean的全限定名称

Bean标签范围配置

scope:指令对象的作用范围
在这里插入图片描述
1、当scope的取值为singleton时<bean id="userDao" class="com.zg.dao.impl.userDaoIMPL" scope="singleton"></bean>
Bean的实例化个数为1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的声明周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载时,销毁容器时,对象就被销毁了

2、当scope的取值为prototype时<bean id="userDao" class="com.zg.dao.impl.userDaoIMPL" scope="prototype"></bean>
Bean的实例化个数为多个
Bean的实例化时机:当调用getBean()方法时,实例化配置的Bean实例
Bean的声明周期:
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,对象一直活着
对象销毁:对象长时间不用,被Java的垃圾回收器回收

Bean生命周期配置
1、实例化一个Bean,也就是我们通常说new
2、按照Spring上下文对实例的Bean进行配置,也就是IOC的注入
3、如果这个Bean实现了dao的BeanNameAware接口,会调用它实现setBeanName(String beanld)方法,此处传递的是SpringBeanID
4、如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他的Bean5、如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,因为ApplicationContextBeanFactory的子接口,有更多的实现方法。
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postprocessBeforelninitialization(Objecct obj,String s)方法,BeanPostProcessor经常被用做作用是Bean内容的修改,并且由于这个是在Bean初始化结束时调用After方法,也可以用于内存或缓存技术
7、如果这个BeanSpring配置了init-method属性会自动调用其配置的初始化方法
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postprocessAfterlninitialization(Objecct obj,String s)方法.
这样完成以上工作就可以使用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个IDBean会是在内容地址相同的实例。
9、当Bean不在需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法。
10、最后,如果这个BeanSpring配置了destroy-method属性,会自动调用其配置的销毁方法。

生命周期:从创建到销亡的完整过程
bean生命周期:bean从创建到销毁的整体过程
bean生命周期控制:在bean创建后到销毁前做的一些事情
1、使用配置的方式

 <bean id="userDao" class="com.zg.dao.impl.userDaoIMPL" init-method="init" destroy-method="destroy"></bean>

init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
2、使用接口的方式
在这里插入图片描述

bean销毁时机

容器关闭前出发bean的销毁
关闭容器方式:

手工关闭容器ConfigurableApplicationContext接口close()操作
注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机ConfigurableApplicationContext接口registerShutdownHook()操作
Bean实例化三种方式

1、无参构造方法实例化
以上方式都为无参构造方法实例化

public class userDaoIMPL implements UserDao {

    public userDaoIMPL() {
    }
}
<!--使用无参构创建方法-->
<bean id="userDao" class="com.zg.dao.impl.userDaoIMPL" ></bean>

2、工厂静态方法实例化

package com.zg.factory;

import com.zg.dao.UserDao;
import com.zg.dao.impl.userDaoIMPL;
//工厂静态方法实例化
public class StaticFactory {

    public static UserDao getUserDao(){
        return new userDaoIMPL();

    }
}
<bean id="userDao" class="com.zg.factory.StaticFactory" factory-method="getUserDao"></bean>

在获取全类名时可以右键其名称选择Copy Reference
3、工厂实例方法实例化,很多

<!--先让Spring产生工厂对象容器,然后调用工厂内部的某个方法-->
    <bean id="factory" class="com.zg.factory.DynamicFactory"></bean>
    <!--通过产生的id="factory"工厂对象容器来找到getUserDao这个方法-->
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"></bean>
package cn.zg.factory;


import cn.zg.dao.UserDao;
import cn.zg.dao.impl.UserDaoImpl;

public class DynamicFactory {

    public UserDao getUserDao(){
        return new UserDaoImpl();
    }

}

4、使用FactoryBean实例化bean

 <bean id="factory" class="com.zg.factory.DynamicFactory"></bean>
package cn.zg.factory;


import cn.zg.dao.UserDao;
import cn.zg.dao.impl.UserDaoImpl;

public class DynamicFactory implements FactoryBean<UserDao>{

	//代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception{
    return new UserDaoImpl();
    }
    public Class<?> getObjectType() {
    return UserDao.class;
    }
    //trun为单例,false为非单例的。
    public boolean isSingleton(){
    return false;
    }
}

Spring配置文件
创建service层,不使用Spring

当我们不使用Spring,在service层中我们需要通过UserServiceImpl实现类来重写UserService中的save()方法,然后调用通过Spring获取Dao层中UserDao的实现类UserDaoImpl对象的save方法(业务层save调用Dao层save),然后web层中通过UserService userService = new UserServiceImpl(); 获取service,来实现dao层的具体业务,这里因为service层没有配置到Spring容器中。
1、创建UserService,UserServie内部调用UserDao的save()方法

package com.zg.service.impl;

import com.zg.dao.UserDao;
import com.zg.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        //UserService要调UserDao,但现在UserDao已经在Spring产生
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)app.getBean("userDao");
        userDao.save();

    }
}

2、web层不使用Spring来封装service

package com.zg.demo;

import com.zg.service.UserService;
import com.zg.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserController {
    //它的内部需要获得Service
    public static void main(String[] args) {
        //不使用Spring封装service
        UserService userService = new UserServiceImpl();
        userService.save();
    }

}
将service层也配置到Spring容器中

1、在配置文件applicationContext.xml中将UserServiceImpl的创建权交给Spring

<bean id="userService" class="com.zg.service.impl.UserServiceImpl"></bean>

2、从Spring容器中获得UserService进行操作

package com.zg.demo;

import com.zg.service.UserService;
import com.zg.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserController {
    //它的内部需要获得Service
    public static void main(String[] args) {
        //使用Spring封装service
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.save();
    }

}

问题分析

目前UserSrvice实例和UserDao实例都存在于Spring中,当前的做法是在容器外部获取UserService实例和UserDao实例,然后再程序中进行结合
在这里插入图片描述
因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部
在这里插入图片描述

Bean的依赖注入概念

依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现

在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。
IOC解耦只是降低他们之间的关系,但不会消除(业务层仍然会调用持久层的方法)

那这种业务层和持久层的依赖关系,在使用Spring后,使用Spring来维护,也就是等框架把持久层对象传入入业务层,不用我们获取

Bean的依赖注入方式

将UserDao注入到UserService内部可通过有参的构造方法和set方法来实现

set方法注入(注入对象)

1、在UserServiceImpl中添加setUserDao方法

package com.zg.service.impl;

import com.zg.dao.UserDao;
import com.zg.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {

    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
       /* //UserService要调UserDao,但现在UserDao已经在Spring产生
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao)app.getBean("userDao");*/
        //我们不用从容器获取Dao,因为在容器内部已经将Dao通过set方法注入给了service
        userDao.save();

    }
}

2、配置Spring容器中调用set方法进行注入

 <bean id="userDao" class="com.zg.dao.impl.userDaoImpl" ></bean>
 <bean id="userService" class="com.zg.service.impl.UserServiceImpl">
        <!--这里的name是属性的名字,就是(方法操作了成员变量值)去掉set,剩下首字母变小写的属性,是          		service中对应的属性名-->
        <!--这里对象的引用使用ref-->
        <!--把容器中的userDao 通过userService内部的setUserDao这个方法注入给userService-->
        <property name="userDao" ref="userDao"></property>
    </bean>

但是但是,在web层就不能new一个UserService啦,因为它没有获取UserDao的注入,所以在web层只能使用通过Spring封装的service来调用dao的方法

set方法注入的简单方式–>P命名空间注入

P命名空间注入的本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中

xmlns:p="http://www.springframework.org/schema/p"

在这里插入图片描述
然后将下面的代码1进行更换为代码2就行
代码1:

 <bean id="userDao" class="com.zg.dao.impl.userDaoImpl" ></bean>
 <bean id="userService" class="com.zg.service.impl.UserServiceImpl">
        <!--这里的name是属性的名字,就是(方法操作了成员变量值)去掉set,剩下首字母变小写的属性-->
        <!--这里对象的引用使用ref-->
        <!--把容器中的userDao 通过userService内部的setUserDao这个方法注入给userService-->
        <property name="userDao" ref="userDao"></property>
    </bean>

代码2:

<bean id="userService" class="com.zg.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
构造注入(有参构造)

1、创建有参构造

package com.zg.service.impl;

import com.zg.dao.UserDao;
import com.zg.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceImpl implements UserService {

    private UserDao userDao;//有参构造
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    public UserServiceImpl() {//无参构造
    }
    

    @Override
    public void save() {
      
        //我们不用从容器获取Dao,因为在容器内部已经将Dao通过set方法注入给了service
        userDao.save();

    }
}

2、构造方法注入

<bean id="userDao" class="com.zg.dao.impl.userDaoImpl" ></bean>
 <!--如果不使用constructor-arg会找到无参构造方法,这样service中就么有dao-->
    <bean id="userService" class="com.zg.service.impl.UserServiceImpl">
        <!--这里的那么是构造内部的参数名, ref代表引用bean容器的id-->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>
依赖注入方式选择

1、强制依赖使用构造器进行,使用setter注入有概率不进行注入null对象出现。
2、可选依赖使用setter注入进行,灵活性强。
3、Spring框架倡导使用构造器,第三方框架内部大多采用构造器注入的形式进行数据初始化,相对严谨。
4、如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入。
5、实际开发过程中还需要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入。
6、自己开发的模块推荐使用setter注入。

Bean的依赖注入的数据类型

set和有参构造方法都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入
注入数据的三种数据类型
普通数据类型
引用数据类型
集合数据类型

以set方法注入为例,演示普通数据类型和集合数据类型的注入
普通数据类型的注入
package com.zg.dao.impl;

import com.zg.dao.UserDao;

public class userDaoImpl implements UserDao {

    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

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

    @Override
    public void save() {
        System.out.println(username+"===="+age);
        System.out.println("save runningg.....");
    }

}

<bean id="userDao" class="com.zg.dao.impl.userDaoImpl" >
        <!--ref为对象引用,而普通数据类型注入使用value-->
        <property name="username" value="Bob"></property>
        <property name="age" value="18"></property>
    </bean>
集合数据类型的注入
package com.zg.dao.impl;

import com.zg.dao.UserDao;
import com.zg.domain.User;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class userDaoImpl implements UserDao {

	private int[] array;
    private List<String> strList;
    private Map<String, User> userMap;
    private Properties properties;

    public void setStrList(List<String> strList) {
        this.strList = strList;
    }

    public void setUserMap(Map<String, User> userMap) {
        this.userMap = userMap;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

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

    @Override
    public void save() {
        //System.out.println(username+"===="+age);
        System.out.println("save runningg.....");
        System.out.println(strList);
        System.out.println(userMap);
        System.out.println(properties);
    }

}

<bean id="userDao" class="com.zg.dao.impl.userDaoImpl" >
		<property name="array">
			<array>
				<value>100</value>
				<value>200</value>
				<value>300</value>
			</array>
			</property>
        <!--这里既不用ref也不用value-->
        <property name="strList" >
            <!--在list集合中我们定义的是String为普通数类型所以里头用value-->
            <!--但如果list集合中定义的Bean则使用ref-->
            <list><value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <property name="userMap">
            <!--map集合有键有值,所以就写entry-->
            <map>
                <!--在userDaoImpl中key是普通数据类型,而value是user对象(引用数据类型)-->
                <!--这里value-ref是引用,所以要进行引用数据类型的注入-->
                <entry key="u1" value-ref="user1"></entry>
                <entry key="u2" value-ref="user2"></entry>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="p1" >ppp1</prop>
                <prop key="p2" >ppp3</prop>
                <prop key="p3" >ppp4</prop>

            </props>
        </property>

    </bean>

            <bean id="user1" class="com.zg.domain.User">
                <property name="name" value="Alice"></property>
                <property name="addr" value="北京"></property>
            </bean>

            <bean id="user2" class="com.zg.domain.User">
                <property name="name" value="Mallry"></property>
                <property name="addr" value="西安"></property>
            </bean>

Map集合中准备的user

package com.zg.domain;

public class User {
    private String name;
    private String addr;

    public String getName() {
        return name;
    }

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

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }
}

引入其他配置文件(分模块开发)

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
在这里插入图片描述

<import resource="applicationConext-user.xml"></import>
<import resource="applicationConext-product.xml"></import>

如果不使用set方法注入而使用构造方法引用,则标签和标签内部的配置写法一样。

Spring依赖自动装配

IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配。

    <bean id="userService" class="com.zg.service.impl.UserServiceImpl" autowire="byType"/>

自动装配用于引用类型依赖注入,不能对简单类型进行操作。
使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用。
使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效。

Spring相关API

ApplicationContext的继承体系

applicationContext:接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象
在这里插入图片描述

ApplicationContext的实现类

1、ClassPathXmlApplicationContext:从类的根路径下加载配置文件推荐使用

 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");

2、FileSystemXmlApplicationContext:从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
在项目名称上右键选择Copy path即可复制文件在磁盘上的路径

FileSystemXmlApplicationContext fileSystemXmlApplicationContext = new FileSystemXmlApplicationContext("D:\\java\\Spring\\src\\main\\resources\\applicationContext.xml");

3、AnnotationConfigApplicationContext:使用注解配置容器对象时,需要使用此类来创建spring容器,用来读取注解

getBean()方法的使用

使用ID:UserService userService = (UserService) app.getBean("userService");允许容器中存在相同类型的Bean
使用字节码类型:UserService userService1 = app.getBean(UserService.class);,这种只能是容器中有一个Bean,如果有多个,可以写成UserService userService1 = app.getBean("UserService" ,UserService.class);,当然如果需要加载多个配置文件则使用.隔开即可。
在这里插入图片描述
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。当参数的数据类型是Class时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。

Spring配置数据源

数据源(连接池)的作用

数据源(连接池)是提高程序性能出现的
实现实例化数据源,初始化部分连接资源
使用连接资源时从数据源中获取
使用完毕后将资源归还给数据源
常见的数据源(连接池):DBPC、C3P0、BoneCP、Druid等

数据源的开发步骤

1、导入数据源的坐标和数据库驱动坐标
2、创建数据源对象
3、设置数据源的基本连接数据
4、使用数据源获取连接和归还连接资源

数据源的手动创建
手动创建C3P0连接池
 @Test
    //测试手动创建c3p0数据源
    public void test1() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db1?serverTimezone=UTC");
        dataSource.setUser("root");
        dataSource.setPassword("root");
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
手动创建Druid连接池
 @Test
    //测试手动创建druid数据源
    public void test2() throws Exception {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/db1?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        DruidPooledConnection connection = dataSource.getConnection();
        System.out.println(connection);


    }
抽取jdbc.properties配置文件方便解耦

jdbc.properties

jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/db1?serverTimezone=UTC
jdbc.username = root
jdbc.password = root

读取jdbc.properties配置文件创建连接池

@Test
    //测试手动创建c3p0数据源(加载properties配置文件形式)
    public void test3() throws Exception {
        ResourceBundle rb = ResourceBundle.getBundle("jdbc");
        String driver = rb.getString("jdbc.driver");
        String url = rb.getString("jdbc.url");
        String username = rb.getString("jdbc.username");
        String password = rb.getString("jdbc.password");
        //创建数据源对象,设置连接参数
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);

        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();

    }
Spring配置数据源

可以将DataSourse的创建权交由Spring容器去完成
DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
DateSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入

 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db1?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>

测试从容器中获取数据源

@Test
    //测试Soring容器创建c3p0数据源
    public void test4() throws Exception {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = (DataSource) app.getBean("dataSource");
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
抽取JDBC配置文件

引入context命名空间和约束路径:
命名空间:xmlns:context="http://www.springframework.org/schema/context"
约束路径: http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

<?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"
       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.xsd">


    <!--加载外部的properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
</beans>

Spring容器加载properties文件

<context:property-placeholder location="classpath:jdbc.properties" system.properties.mode = "NEVER"></context:property-placeholder>
<property name="driverClass" value="${jdbc.driver}"></property>

system.properties.mode = "NEVER":这里为加载不加载系统属性,用来解决配置不生效的问题(具体username为系统属性字段)。
如果加载多个配置文件则在location属性的值中使用逗号,隔开
加载所有propertise文件<context:property-placeholder location="*.properties" />
加载propertise文件的标准格式<context:property-placeholder location="classpath:*.properties" />
从类路径或jar包中搜索并加载propertise文件<context:property-placeholder location="classpath*:*.properties" />

Spring注解开发

Spring原始注解

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发时一种趋势,注解代替xml配置文件可以简化配置,提高开发效率
Spring原始注解主要是替代<Bean>的配置

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

注意:使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别注解配置的类、字段和方法
UserDao加注解

package com.zg.dao.Impl;

import com.zg.dao.UserDao;
import org.springframework.stereotype.Component;

//<bean id="userDao" class="com.zg.dao.Impl.UserDaoImpl"></bean>
@Component("userDao")
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("save run....");
    }
}

UserService加注解

package com.zg.service.Impl;

import com.zg.dao.UserDao;
import com.zg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

//<bean id="userService" class="com.zg.service.Impl.UserServiceImpl">
@Component("userService")
public class UserServiceImpl implements UserService {

    //<property name="userDao" ref="userDao"></property>
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        userDao.save();
    }
}

Spring文件中配置

 <!--配置组件扫描,要在context命名空间下-->
    <!--表示com.zg下的所有子包都会被扫描-->
    <context:component-scan base-package="com.zg"></context:component-scan>
注意:

1、上面方法全使用了@Component注解,其实用@Controller、@Service、@Repository进行注解效果也是一样的,在Dao层如果使用@Autowired和@Qualifier进行注解也是可以不用写set方法的。
2、在将UserDao注入到UserService时使用了@Autowired和@Qualifier,但使用@Autowired也可达到相应的效果,@Autowired是按照数据类型从Spring容器中进行配置的,但如果容器中UserDao有多个Bean,就需要添加@Qualifier并指定相应的ID。@Qualifier是按照ID的值从容器中进行匹配,此处@Qualifier要结合@Autowired注入
3、@Resource(name="userDao")相当于@Autowired+@Qualifier,按照名称进行注入
4、自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法。

使用@value进行字符串的注入
package com.zg.service.Impl;

import com.zg.dao.UserDao;
import com.zg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

//<bean id="userService" class="com.zg.service.Impl.UserServiceImpl">
//@Component("userService")
@Service("userService")
public class UserServiceImpl implements UserService {
    @Value("zgDaren")
    private String driver;
    @Value("${jdbc.url}")//在对应的Spring容器中找到key对应的值赋值给url
    private String url;

    //<property name="userDao" ref="userDao"></property>
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void save() {
        System.out.println(driver);//zgDaren
        userDao.save();
        System.out.println(url);//jdbc:mysql://localhost:3306/db1?serverTimezone=UTC
    }
}

使用@Scope标注Bean的范围
//@Scope("singleton")一个Bean
@Scope("prototype")多个Bean
public class UserServiceImpl implements UserService {
}
使用标注初始化方法,使用标注销毁方法
@PostConstruct//初始化方法
    public void init(){
        System.out.println("初始化方法....");
    }
    @PreDestroy//销毁方法
    public void destory(){
        System.out.println("销毁方法...");
    }  

Spring新注解

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:
非自定义的Bean的配置:<bean>
加载properties文件的配置:<context:property-placeholder>
组件扫描的配置:<context:component-scan>
引入其他文件:<import>

注解说明
@Configuration用于指定当前类是一个Spring配置类,当前创建容器时会从该类上加载注解
@ComponentScan用于指定Spring在初始化容器时要扫描的包,作用和在Spring的xml配置文件中的<context:component-scn base-pack=“com.zg”/> 一样
@Bean使用在方法上,标注将该方法的返回值存储到Spring容器中
@PropertySource用于加载.properties文件中的配置 ,如果有多个则使用数组,并且用逗号隔开,但不支持使用通配符*
@Import用于导入其他配置类
新建一个类用于主配置文件@Configuration
package com.zg.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

//标志该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="com.zg"></context:component-scan>
@ComponentScan("com.zg")
@Import(DataSourceConfiguration.class)//这里是数组,多了可以使用使用逗号隔开
public class SpringConfiguration {


}

存放附属配置文件@PropertySource
package com.zg.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

//负责数据源配置
//<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>\
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")//Spring会将当前方法的返回值以指定名称存储到Spring容器当中
    public DataSource getDataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

测试类
package com.zg.web;

import com.zg.config.SpringConfiguration;
import com.zg.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserController {
    public static void main(String[] args) {
        //ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
                //在使用了注解后,获取不到userService,因为没有告诉Spring有了注解(即需要配置组件扫描)
        UserService userService = (UserService) app.getBean("userService");
        userService.save();
    }
}

第三方bean 管理@Bean
package com.zg.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

//标志该类是Spring的核心配置类
@Configuration
public class SpringConfiguration {

	//定义一个方法获得要管理的对象
	//添加@Bean,表示当前方法的返回值是一个bean
	@Bean
	public DataSource dataSource(){
	DruidDataSource ds = new DruidDataSource();
	ds.setDriverClassName("com.mysql.jdbc.Driver");
	ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
	ds.setUsername("root");
	ds.setPassword("root");
	return ds;
	}
	
}

导入其他配置类 @Import

将独立的配置类加入核心配置

package com.zg.config;

//标志该类是Spring的核心配置类
@Configuration
@Import({jdbcConfig.class})
public class SpringConfiguration {

	//定义一个方法获得要管理的对象
	//添加@Bean,表示当前方法的返回值是一个bean
	@Bean
	public DataSource dataSource(){
	DruidDataSource ds = new DruidDataSource();
	ds.setDriverClassName("com.mysql.jdbc.Driver");
	ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
	ds.setUsername("root");
	ds.setPassword("root");
	return ds;
	}
	
}

第三方bean依赖注入

简单类型依赖注入

在这里插入图片描述
引用类型依赖注入
在这里插入图片描述
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。

Spring集成Junit

原始Junit测试Spring的问题

在测试类中,每个测试方法都有一下两行代码:

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) app.getBean("userService");

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常,所以又不能轻易删掉

解决方案

让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
将需要进行测试Bean直接在测试类中进行注入

Spring集成Junit步骤

1、导入Spring集成Junit的坐标
2、使用@Runwith注解替换原来的运行期
3、使用@ContextConfiguration指定配置文件或配置类
4、使用@Autowired注入需要测试的队象
5、创建测试方式进行测试

package com.zg.test;

import com.zg.config.SpringConfiguration;
import com.zg.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.sql.SQLException;

//以前执行测试是直接使用junit
//现在可以是找Spring提供的内核,然后在找junit进行测试,但在找之前可以完成Spring容器的创建、配置文件的加载
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")//指定配置文件的位置
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {

    @Autowired
    private UserService userService;
    @Autowired
    private DataSource dataSource;


    @Test
    public void test1() throws SQLException {
        userService.save();
        System.out.println(dataSource.getConnection());
    }

}

这里需要注意的是,当使用@ContextConfiguration的时候需要将我们之前在applicationContext.xml中剪切的Bean复原,不然会报错。这里也验证了Spring的全注解和从Spring Config中创建的.xml文件可以共存于共一个项目,不用删除Bean的配置。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zgDaren

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值