前言
🎄:CSDN的小伙伴们大家好,今天跟大家分享一个框架——Spring,本篇文章仅仅介绍Spring框架的基本使。如果这篇文章对你有用,麻烦给我点个小赞以示鼓励吧🎄
🏡:博客主页:空山新雨后的java知识图书馆
☀️:重庆的大热天到了,太热了。
📝:任何事业都可能受挫折,虽然为事业而奋斗的人是伟大的。——本涅特📝
📖上一篇文章:Mybatis框架学习📖
👏欢迎大家一起学习,进步。加油👊
文章目录
一、什么是Spring
1.1.、spring的概念
Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等功能。Spring可以单独应用于构筑应用程序,也可以和Struts、Webwork、Tapestry等众多Web框架组合使用,并且可以与 Swing等桌面应用程序AP组合。因此, Spring不仅仅能应用于J2EE应用程序之中,也可以应用于桌面应用程序以及小应用程序之中。Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。【概念摘自百度百科】
1.2、spring的特点
1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3.声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EEAPI的使用难度大为降低。
7.Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
二、Spring程序开发的步骤(简单介绍)
2.1、导入 Spring 开发的基本包坐标(maven项目,其他的下载jar包即可)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
2.2、编写 Dao 接口和实现类
public interface UserDao {
public void save();
}
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("UserDao save 方法被执行");
}
}
2.3、创建 Spring 核心配置文件
如果你使用的是idea,可以使用这个方法快捷创建spring的xml配置文件。文件名习惯写为applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
2.4、在 Spring 配置文件中配置 UserDaoImpl
<?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.study.dao.Impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
2.5、使用 Spring 的 API 获得 Bean 实例
@Test
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
这时我们的测试类就已经写完了,测试执行就会看到save方法的内容输出,也就是save方法执行成功,也就是说明我们的spring基本框架的使用正确。
接下来我会对里面的各种标签,API等进行解释。
三、spring相关配置的详解
3.1、bean标签
3.1.1、bean标签的基本概念
spring配置文件中有一个bean标签,他的作用是用于配置对象交由spring进行创建。
那么默认情况下,该标签创建对象调用的是类中的无参构造函数,如果没有无参构造函数则不能成功创建。
3.1.2、bean标签的基本属性
1、id:Bean实例在Spring容器中的唯一标识,这个id取名之后,我们在测试中可以通过这个id来获取这个对象。
2、class:Bean的全限定名称,即你为哪一个对象创建到spring容器当中,需要指定对象的全限定名。
例如↓
<!--配置Spring,id为标识符,class为类 scope属性,singleTon为单例,prototype为多例 下面这个方法使用无参构造获取bean对象-->
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
这段代码就是为userDaoImpl这个对象交由spring进行创建,id取得userDao
public static void main(String [] args){
//参数为配置好的xml文件
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
}
那么在测试类中,我就可以通过他的id让spring将创建到的userDaoImpl的对象交给我,我就可以调用里面的方法进行使用。
3.1.3、bean标签的作用范围:scope属性的使用。
bean标签有一个scope属性,这个属性的作用是告诉spring,你要给我创建的对象是单例的还是多例的,因此可以指定,当然常用的功能是这两个,还有其他的功能。
取值范围 | 说明 |
---|---|
singleton | 默认值,单例的 |
prototype | 多例的 |
request | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中 |
session | WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中 |
global session | WEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session |
1)当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2)当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
Bean的生命周期:
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被Java 的垃圾回收器回收了
案例:
测试类:
@Test
//测试xml配置文件中的scope属性
public void testScope() {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao1 = (UserDao) app.getBean("userDao");
UserDao userDao2 = (UserDao) app.getBean("userDao");
//如果两个地址值为相同的,说明获取的是同一个,为单例,地址值不同则是获取的不是同一个,scope属性就是多例的
System.out.println(userDao1);
System.out.println(userDao2);
}
配置文件
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl" scope="singleton">
单例输出:
com.study.dao.Impl.UserDaoImpl@27fe3806
com.study.dao.Impl.UserDaoImpl@27fe3806
多例输出:
com.study.dao.Impl.UserDaoImpl@27fe3806
com.study.dao.Impl.UserDaoImpl@5f71c76a
测试成功。
3.1.4、bean的生命周期配置
bean的生命周期配置有两个属性
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
具体的做法就是在被实例化对象的内部写一个初始化方法和一个销毁方法
这里我写在daoImpl中
/**
* 初始化方法和销毁方法,要想使用,得在xml配置文件中进行配置,方法名随意
*/
public void init() {
System.out.println("初始化方法被调用");
}
public void destroy() {
System.out.println("销毁方法被调用");
}
配置文件
<!--配置Spring,id为标识符,class为类 scope属性,singleTon为单例,prototype为多例 下面这个方法使用无参构造获取bean对象-->
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
那么测试的时候,他不仅会输出save方法,还会在他的生命周期内执行初始化和销毁方法。
测试代码及输出结果。
//测试初始化和销毁的方法
@Test
public void testInitAndDestroy() {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
//UserDaoImpl对象被创建
//初始化方法被调用 此时只能看到初始化方法调用了,销毁方法却没有
//原因是我们单元测试代码结束之后,控制台还没来得及打印,就已经销毁对象了,在服务器中则不会,那么
//要想看到就可以使用app接口的实现类对象的close方法,手动关闭,这样就可以答应
// ((ClassPathXmlApplicationContext) app).close();
/*UserDaoImpl对象被创建
初始化方法被调用
销毁方法被调用*/
}
3.2、bean实例化的三种方式
3.2.1、 使用无参构造方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败
配置
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl"/>
3.2.2、 工厂静态方法实例化
工厂的静态方法返回Bean实例
package com.study.factory;
import com.study.dao.Impl.UserDaoImpl;
import com.study.dao.UserDao;
/**
* @author wang
* @version 1.0
* @packageName com.study.factory
* @className UserDaoFactory
* @date 2022/4/25 10:35
* @Description
*/
public class UserDaoFactory {
//静态工厂获取对象
public static UserDao getUserDao() {
return new UserDaoImpl();
}
}
<!--静态工厂获取对象的配置-->
<bean id="userDao" class="com.study.factory.UserDaoFactory" factory-method="getUserDao"></bean>
3.2.3、 工厂实例方法实例化
工厂的非静态方法返回Bean实例
package com.study.factory;
import com.study.dao.Impl.UserDaoImpl;
import com.study.dao.UserDao;
/**
* @author wang
* @version 1.0
* @packageName com.study.factory
* @className UserDaoFacotryDynamic
* @date 2022/4/25 10:38
* @Description
*/
public class UserDaoFactoryDynamic {
//工厂实例对象来返回需要的对象,因为这次我们的方法不是静态方法,所以必须获取工厂对象,才能调用我们自己的方法
public UserDao getUserDaoImpl(){
return new UserDaoImpl();
}
}
<!--工厂实例对象获取对象配置-->
<!--先获取工厂对象-->
<bean id="factory" class="com.study.factory.UserDaoFactoryDynamic"></bean>
<!-- 再获取对象中的方法-->
<bean id="userDao" factory-bean="factory" factory-method="getUserDaoImpl"></bean>
3.3、bean的依赖注入入门
依赖注入,就是给某个属性,注入对象,或者普通类型等等。详细请往下看
①创建 UserService,UserService 内部在调用 UserDao的save() 方法
package com.study.service.Impl;
import com.study.dao.UserDao;
import com.study.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author wang
* @version 1.0
* @packageName com.study.service.Impl
* @className UserServiceImpl
* @date 2022/4/25 11:13
* @Description
*/
public class UserServiceImpl implements UserService {
private UserDao userDao;
//依赖注入,方法一:set方法注入,将userdao在Spring容器中注入到userService
// public void setUserDao(UserDao userDao) {
// this.userDao = userDao;
// }
//方法二:有参构造注入
public UserServiceImpl(UserDao userDao){
this.userDao = userDao;
}
public UserServiceImpl() {
}
public void save() {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) app.getBean("userDao");
userDao.save();
}
}
配置文件注入
<bean id="userService" class="com.study.service.Impl.UserServiceImpl">
<!--name是构造方法中的属性,ref是引用的对象id-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
测试能获取到对象,并且能使用方法就注入成功了。
3.4、bean依赖注入的概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取
3.5、依赖注入的两种方式
3.5.1、构造方法进行注入
创建有参构造,注意如果你创建了有参构造,别忘了把无参构造加上
public class UserServiceImpl implements UserService {
private UserDao userDao;
//方法二:有参构造注入
public UserServiceImpl(UserDao userDao){
this.userDao = userDao;
}
public UserServiceImpl() {
}
}
配置文件注入对象,前提是你的被注入对象userDao也得实例化出来在spring容器中
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl" scope="prototype"/>
<!--测试有参构造的注入方式-->
<bean id="userService" class="com.study.service.Impl.UserServiceImpl">
<!--name是构造方法中的属性,ref是引用的对象id-->
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
3.5.2、set方法进行注入
需要注入对象的类
public class UserServiceImpl implements UserService {
//依赖注入,方法一:set方法注入,将userdao在Spring容器中注入到userService
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
配置文件
<bean id="userService" class="com.study.service.Impl.UserServiceImpl">
<!--依赖注入,将userDao对象,在Spring容器中注入到UserService中,
name:是注入的那个set方法,setUserDao,userDao
ref:是引的注入的对象,为上面的被注入的对象的id-->
<property name="userDao" ref="userDao"></property>
</bean>
3.6、bean的依赖注入的数据类型
前面演示的注入时注入的一个对象,那么接下来我们介绍三种数据类型可以被注入的类型
3.6.1、普通数据类型
看实例代码:可以使用构造注入和set注入两种方式,任你选择,注意构造方法注入的时候,如果此时你有两个属性,但是你只写了一个构造函数也就是他俩一起的构造函数,当然无参构造是默认存在的,那么当你在配置注入的时候,如果只注入了一个属性,他是会报错的,找不到对应的构造函数。
因此,建议在使用构造注入的时候,给各种情况都添加一个构造函数,或者注入的时候强制必须注入所有等等。也可以更简单直接使用set注入即可。
package com.study.dao.Impl;
import com.study.dao.UserDao;
/**
* @author wang
* @version 1.0
* @packageName com.study.dao.Impl
* @className UserDaoImpl
* @date 2022/4/25 9:26
* @Description
*/
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl对象被创建");
}
/*测试普通属性的注入*/
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public UserDaoImpl(String username, int age) {
this.username = username;
this.age = age;
}
public void save() {
System.out.println("username:" + username + "\nage:" + age );
System.out.println("save 方法被调用");
}
}
配置文件中注意标签的使用。
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl">
<!--set,get方法的注入普通数据类型,非对象,那么就不是ref,而是value-->
<!-- <property name="age" value="15"></property>
<property name="username" value="zhangsan"></property>-->
<!--构造方法的注入普通数据-->
<constructor-arg name="username" value="lisi"></constructor-arg>
<constructor-arg name="age" value="55"></constructor-arg>
</bean>
3.6.2、引用数据类型
引用数据类型就是我们之前演示的注入对象之类。
3.6.3、集合数据类型
集合数据类型(List)的注入
user
package com.study.domain;
/**
* @author wang
* @version 1.0
* @packageName com.study.domain
* @className User
* @date 2022/4/25 15:46
* @Description
*/
public class User {
private String name;
private String password;
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
userDaoImpl
package com.study.dao.Impl;
import com.study.dao.UserDao;
import com.study.domain.User;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* @author wang
* @version 1.0
* @packageName com.study.dao.Impl
* @className UserDaoImpl
* @date 2022/4/25 9:26
* @Description
*/
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl对象被创建");
}
/*测试对集合的数据注入*/
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;
}
public UserDaoImpl(List<String> strList, Map<String, User> userMap, Properties properties) {
this.strList = strList;
this.userMap = userMap;
this.properties = properties;
}
/*测试普通属性的注入*/
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public UserDaoImpl(String username, int age) {
this.username = username;
this.age = age;
}
public void save() {
System.out.println("strList:" +strList);
System.out.println("userMap:" + userMap);
System.out.println("properties:" + properties);
// System.out.println("username:" + username + "\nage:" + age );
System.out.println("save 方法被调用");
}
}
配置文件:注意集合注入标签的使用,set注入使用property子标签,构造注入使用constructor-arg标签
另外list集合选择list标签,值采用value标签即可。
<bean id="userDao" class="com.study.dao.Impl.UserDaoImpl">
<!--第一种set,get方法注入-->
<!--注入list集合 name就是dao中的属性-->
<property name="strList">
<!--选list集合标签-->
<list>
<!--有个value标签就是list集合的值所填写的位置-->
<value>zhangsan</value>
<value>lisi</value>
<value>王五</value>
</list>
</property>
集合数据类型(List)的注入
daoimpl
public class UserDaoImpl implements UserDao {
private List<User> userList;
public void setUserList(List<User> userList) {
this.userList = userList;
}
public void save() {
System.out.println(userList);
System.out.println("UserDao save method running....");
}
}
配置文件:注意这种集合中注入引用数据的,要相应的去修改他的对应的标签
<bean id="u1" class="com.study.domain.User"/>
<bean id="u2" class="com.study.domain.User"/>
<bean id="userDao" class="com.study.dao.impl.UserDaoImpl">
<property name="userList">
<list>
<bean class="com.study.domain.User"/>
<bean class="com.study.domain.User"/>
<ref bean="u1"/>
<ref bean="u2"/>
</list>
</property>
</bean>
集合数据类型( Map<String,User> )的注入
DaoImpl
public class UserDaoImpl implements UserDao {
private Map<String,User> userMap;
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void save() {
System.out.println(userMap);
System.out.println("UserDao save method running....");
}
}
配置文件
<!--注入map集合,选map标签-->
<property name="userMap">
<map>
<!--选择entry标签,如果,key,value的值为普通数据类型:直接输入即可
如果为引用类型,就要使用对应的引用属性,-->
<entry key="u1" value-ref="user1"></entry>
<entry key="u2" value-ref="user2"></entry>
<entry key="u3" value-ref="user3"></entry>
</map>
</property>
集合数据类型(Properties)的注入
DaoImpl
public class UserDaoImpl implements UserDao {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void save() {
System.out.println(properties);
System.out.println("UserDao save method running....");
}
}
配置文件
<!--注入properties集合-->
<property name="properties">
<!--选择标签,键值对-->
<props>
<prop key="first">张飞</prop>
<prop key="second">邢道荣</prop>
<prop key="third">吕布</prop>
</props>
</property>
3.7、引入其他配置文件
实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载
<import resource="applicationContext-xxx.xml"/>
四、Spring相关的API
4.1、ApplicationContext的继承体系
applicationContext:接口类型,代表应用上下文,可以通过其实例获得 Spring 容器中的 Bean 对象
4.2、ApplicationContext的实现类
1)ClassPathXmlApplicationContext
它是从类的根路径下加载配置文件 推荐使用这种
2)FileSystemXmlApplicationContext
它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3)AnnotationConfigApplicationContext
当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
4.3、getBean()方法的使用
源码
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public <T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
其中,当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转。
当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错
getBean()方法使用实例
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = (UserService) applicationContext.getBean("userService");
UserService userService2 = applicationContext.getBean(UserService.class);
五、Spring数据源的配置
5.1、数据源(连接池)的作用
作用
数据源(连接池)是提高程序性能如出现的
事先实例化数据源,初始化部分连接资源
使用连接资源时从数据源中获取
使用完毕后将连接资源归还给数据源
常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid等
5.2、数据源连接池的开发步骤(传统方法)
①导入数据源的坐标和数据库驱动坐标
②创建数据源对象
③设置数据源的基本连接数据
④使用数据源获取连接资源和归还连接资源
实例:测试获取c3p0和Druid连接池,通过以前的方法
在pom.xml文件中导入c3p0或者Druid的坐标
<!-- C3P0连接池 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!-- Druid连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
导入MySQL数据库驱动
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
测试获取c3p0
/**
* @Date 2022/4/25 17:26
* @Param
* @Return void
* @MetodName testC3p0
* @Author wang
* @Description 测试手动创建c3p0数据源
*/
@Test
public void testC3p0() throws Exception {
//获取数据源信息
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//设置连接的基本信息
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("2395");
Connection connection = dataSource.getConnection();
/*测试是否获取到连接对象*/
System.out.println(connection);
connection.close();
}
测试获取Druid
@Test
/**
* 测试手动创建德鲁伊连接池
*/
public void testDruid() throws SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("2395");
DruidPooledConnection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
通过测试会输出获取到的对象,则数据源获取成功
另外,这里的设置数据源基本信息,我们也学习过将其抽取到properties文件中进行
jdbc.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
测试抽取properties文件获取
/**
* 通过读取配置文件,手动创建c3p0数据源
*/
@Test
public void testC3withJdbc() throws PropertyVetoException, SQLException {
//读取配置文件,getBundle()方法的参数, 就是配置文件的基本名字,例如jdbc.properties.这里就写jdbc就写
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
String driver = rb.getString("jdbc.dirver");
String url = rb.getString("jdbc.url");
String username = rb.getString("jdbc.username");
String password = rb.getString("jdbc.password");
//获取c3p0数据源对象
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();
}
5.3、使用spring框架配置数据源
使用spring框架配置数据源对象,可以将DataSource的创建权交由Spring容器去完成
DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
在spring配置文件中配置数据源的相关信息,
<!--给c3p0配置spring容器,spring进行加载,并创建对象-->
<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置数据源-->
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
测试通过这种配置获取数据源对象
/**
* 通过spring容器获得c3p0数据源
*/
@Test
public void testSpringC3p0() throws SQLException {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
/*传入DataSource.class参数主要是因为配置文件之中只有一个bean,使用这种较为方便*/
// DataSource dataSource = app.getBean(DataSource.class);
DataSource dataSource = (DataSource) app.getBean("c3p0DataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
同样,我们也可以将配置数据源的那一部分使用properties配置文件进行抽取
这个抽取外部的配置文件就需要进行两步操作
①首先,需要引入context命名空间和约束路径:
命名空间:xmlns:context=“http://www.springframework.org/schema/context”
约束路径:http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd3
②其次,获取值的时候,通过springel表达式来获取,即通过properties文件中的key获取值
注意需要通过 <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
改标签去指定你的配置文件即可。classpath指定你的文件名 。
${jdbc.driver}就是通过这个key去文件中找到他对应的值放到value中。
<!--引入外部properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--配置数据源,通过外部的properties文件-->
<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>
六、spring注解开发
spring开发除开xml配置文件之外,与其他框架一样,也提供了注解开发的方法
Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。
spring的注解主要分为两部分,一部分是原始的注解开发,一部分就是一些新注解
6.1、原始注解
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需要进行扫描以便识别使用注解配置的类、字段和方法。
<!--配置组件扫描,配置一个扫描的包,spring会自动去扫描包下所有的注解,并执行对应的操作-->
<context:component-scan base-package="com.study"></context:component-scan>
6.1.1、实例化bean的注解
这个注解主要有四个,@Component 、@Controller、@Service 、@Repository。
@Component适用于所有的类,也就是你不知道他是什么层,用它配置就可以了。
其他三个,表格中分别写明,按照需求使用即可,作用等同于@Component
这个注解替代的是applicationContext.xml文件中的这个标签:<bean id="userService" class="com.study.service.impl.UserServiceImpl">
例如我在service层和dao层分别做的实例
/* 如果想要语义更加清晰,可以使用Service注解,专用于service层的注解。*/
//@Component("userService")
@Service("userService")
@Scope("singleton")//,作用范围,这个是创建一个实例对象
//@Scope("prototype")//这个是创建多个实例对象
public class UserServiceImpl implements UserService {
dao层
//@Component("userDao")
@Repository("userDao")
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("save方法被调用");
}
}
6.1.2、进行依赖注入的注解
一、@AutoWired 和 @Qualifier 注解一起配合使用进行注入,他的使用分为两种情况,一种就是spring容器中只有一个需要注入的对象时,那么只使用@AutoWired注解就可以,他会自动去spring容器中去匹配需要的类型;另一种就是如果有多个需要被注入的对象的时候,就需要使用两个注解配合使用的情况,由@Qualifier注解去指明被注入对象的id。
二、还有一个注解@Resource他的作用就相当于一 里面的两个注解的作用,也是直接指定需要注入的对象的id接口,这里记得在注入的时候用name去指定。
三、那么还有一个关于普通属性的注入,@Value注解,他可以对普通的数据类型去注入到spring容器当中,它可以实现一些骚操作,例如将配置文件中的数据注入到spring容器当中,例如我们在进行加载数据源的操作的时候,不使用spring框架的情况下是用的ResourceBundle这个类的getBundle() 方法去获取的参数,使用spring配置的时候用的是spring的表达式去取值,那么在注解操作时也可以使用@Value注解进行配置,例如
@Value("${jdbc.driver}")
private String driver;
这段代码就是对配置文件的属性的注入,我们在给数据源配置的时候,输入driver这个属性就可以了。当然使用这个的前提还需要用到一个注解,引入我们的properties配置文件,这个在后面会跟大家分享。
package com.study.service.impl;
import com.study.dao.UserDao;
import com.study.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.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
/**
* @author wang
* @version 1.0
* @packageName com.study.service.impl
* @className UserServiceImpl
* @date 2022/4/26 10:04
* @Description
*/
/*<bean id="userService" class="com.study.service.impl.UserServiceImpl">
* 如果想要语义更加清晰,可以使用Service注解,专用于service层的注解。*/
//@Component("userService")
@Service("userService")
@Scope("singleton")//,作用范围,这个是创建一个实例对象
//@Scope("prototype")//这个是创建多个实例对象
public class UserServiceImpl implements UserService {
//普通属性的注入,下面是使用spel表达式,在spring容器中已经存储的properties文件中
//的jdbc.driver的key去获取他的值。如果需要注入其他的值,直接写就可以
@Value("hhahah")
//@Value("${jdbc.driver}")
private String driver;
/*<property name="userDao" ref="userDao"></property>
* 注入对象的两个注解,配合使用,qualifier标签写被注入的对象的id*/
// @Autowired//如果spring容器中只有一个dao,那么可以只是用autoWired一个注解即可,他从spring容器中自动去匹配要被注入的类型
// @Qualifier("userDao")//qualifier这个注解要配合autoWired进行使用,他是去spring容器中找id值相匹配的bean进行注入
/*如果不想使用上面两个注解 ,可以直接使用resources注解,他的功能相当于autoWired加qualifier两个注解*/
@Resource(name = "userDao")
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
System.out.println(driver);
userDao.save();
}
//初始化方法
@PostConstruct//初始化方法的注解为构造方法之后,POSTConstruct
public void init() {
System.out.println("serviceImpl的初始化方法");
}
//销毁方法
@PreDestroy//销毁方法在销毁之前执行,因此采用preDestroy注解
public void destroy() {
System.out.println("serviceImpl的销毁方法");
}
}
6.1.3、其他注解
这些注解的作用相同与xml配置文件,用法也很简单,例如@POSTConstruct是在构造方法之后运行初始化方法,那么你把这个注解放到初始化方法之上即可,销毁方法同理,放到你自定义的销毁方法之上即可;
另外一个就是@Scope注解主要就是告诉spring你给我创建几个实例对象。放到类上即可,要被创建的类,那么参数依旧是singleton和prototype。
参考代码参上一小节。
6.2、新注解
那么通过以上这些注解,仍旧有一些spring的xml配置无法通过注解进行替代,
这四个↓
非自定义的Bean的配置:
<bean>
加载properties文件的配置:<context:property-placeholder>
组件扫描的配置:<context:component-scan>
引入其他文件:<import>
很显然,spring也是给我们提供了框架的。
注解 | 说明 |
---|---|
@Configuration | 用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解 |
@ComponentScan | 用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package=“com.itheima”/>一样 |
@Bean | 使用在方法上,标注将该方法的返回值存储到 Spring 容器中 |
@PropertySource | 用于加载.properties 文件中的配置 |
@Import | 用于导入其他配置类 |
那么这个就可以说是我们自定义一个配置类来理解这五个新配置
自定义的配置类
package com.study.config;
import org.springframework.context.annotation.*;
/**
* @author wang
* @version 1.0
* @packageName com.study.config
* @className SpringConfiguration
* @date 2022/4/26 14:55
* @Description spring注解开发核心配置类
*/
//标志该类是一个配置类,当创建容器时,会从该类进行加载注解
@Configuration
//<context:component-scan base-package="com.study"></context:component-scan>
//用于指定 Spring 在初始化容器时要扫描的包。,作用同上方的配置文件标签
@ComponentScan("com.study")
/*引入分配置,数据源的配置,这里引入的配置是一个数据,可以引入多个,用逗号隔开
*{DataSourceConfiguration.class,xxxx.class} */
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {
}
@Configuration标签用于告诉spring这是一个配置类,
@ComponentScan标签用于扫描初始化容器的包,如果不做这一步,我们这个自定义配置类的作用是不会作用到我们的项目当中去的。
@Import标签将我们的其他的配置类引入,因为实际开发中不可能只存在一个配置,因为这里演示的是数据源的配置,开发中可能会存在很多的配置类,因此我们通过这种引入的方法,简化了臃肿的代码。
我的项目的结构
数据源的配置类
这里用到的就是我之前提过的通过@Value注入从@PropertySource获取到的文件,将里面的值注入到配置类的属性当中。
另外@Bean注解就是将方法的返回值存储到spring容器当中,使用在方法之上。这个返回值我们测试的时候就可以拿他进行数据源的访问,当然这不需要我们做。
package com.study.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;
/**
* @author wang
* @version 1.0
* @packageName com.study.config
* @className DataSourceConfiguration
* @date 2022/4/26 15:06
* @Description 数据源的Spring配置类
*/
/* <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
引入外部文件,存储到spring容器中*/
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
/*这里通过在jdbc.properties中引入的键获取其值,注入到属性中,将属性赋值到getDataSource中*/
@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容器中
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.study.web;
import com.study.config.SpringConfiguration;
import com.study.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author wang
* @version 1.0
* @packageName com.study.web
* @className UserController
* @date 2022/4/26 10:05
* @Description
*/
public class UserController {
public static void main(String[] args) {
/*这个是引入配置文件进行加载spring配置*/
// ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
/*这个是通过spring配置类进行加载spring配置*/
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = (UserService) app.getBean("userService");
userService.save();
}
}
通过加载我们配置好的配置类也可以实现对数据的访问。
七、Spring集成Junit
7.1、原始Junit测试Spring的问题
在测试类中,每个测试方法都有以下两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = ac.getBean("accountService",IAccountService.class);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。
上述问题的解决思路
让SpringJunit负责创建Spring容器,但是需要将配置文件的名称告诉它
将需要进行测试Bean直接在测试类中进行注入
7.2、Spring集成Junit的步骤
①导入spring集成Junit的坐标
<!--此处需要注意的是,spring5 及以上版本要求 junit 的版本必须是 4.12 及以上-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
②使用@Runwith注解替换原来的运行期
这个替换的类是固定的,spring规定的
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunitTest {
}
③使用@ContextConfiguration指定配置文件或配置类
这个配置类是我们刚刚写好的
@RunWith(SpringJUnit4ClassRunner.class)
//加载spring核心配置文件
//@ContextConfiguration(value = {"classpath:applicationContext.xml"})
//加载spring核心配置类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
}
④使用@Autowired注入需要测试的对象
这里我测的是我自己的userService类,各位自行测试即可。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;
}
⑤创建测试方法进行测试
测试直接调用里面有的方法测试就行,成功即可。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})public class SpringJunitTest {
@Autowired
private UserService userService;
@Test
public void testUserService(){
userService.save();
}
}
总代码
package com.study.test;
import com.study.config.SpringConfiguration;
import com.study.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;
/**
* @author wang
* @version 1.0
* @packageName com.study.test
* @className SpringJunitTest
* @date 2022/4/26 15:34
* @Description
*/
//②使用@Runwith注解替换原来的运行期
@RunWith(SpringJUnit4ClassRunner.class)
//③使用@ContextConfiguration指定配置文件或配置类
/*加载配置文件的*/
//@ContextConfiguration("classpath:applicationContext.xml")
/*加载配置类的*/
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
//④使用@Autowired注入需要测试的对象
@Autowired
private UserService userService;
@Autowired
private DataSource dataSource;
//创建测试方法进行测试
@Test
public void test() throws SQLException {
userService.save();
System.out.println(dataSource.getConnection());
}
}