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)方法,此处传递的是Spring中Bean的ID
4、如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他的Bean)
5、如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法。
6、如果这个Bean关联了BeanPostProcessor接口,将会调用postprocessBeforelninitialization(Objecct obj,String s)方法,BeanPostProcessor经常被用做作用是Bean内容的修改,并且由于这个是在Bean初始化结束时调用After方法,也可以用于内存或缓存技术
7、如果这个Bean在Spring配置了init-method属性会自动调用其配置的初始化方法
8、如果这个Bean关联了BeanPostProcessor接口,将会调用postprocessAfterlninitialization(Objecct obj,String s)方法.
这样完成以上工作就可以使用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例。
9、当Bean不在需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法。
10、最后,如果这个Bean的Spring配置了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的配置。