前言:重点是注解开发,记住常用的几个注解使用
1.IOC/DI配置管理第三方bean
实现步骤:
1.在resources下创建一个jdbc.properties(文件的名称可以任意)
2.将数据库连接四要素配置到配置文件中
3.在Spring的配置文件中加载properties文件
4.使用加载到的值实现属性注入
准备环境:
步骤1:准备properties配置文件
创建一个jdbc.properties文件,添加对应的属性键值对
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=123
username = zhangsan
步骤2:开启context
命名空间
在applicationContext.xml中开context
命名空间,通过${key}
获取对应的值实现注入
<!--读取单个属性 system-properties-mode="NEVER"意思是不读取环境变量-->
<!--加载核心配置文件-->
<!--读取单个属性方式一,优先使用最后一个配置文件的,其实没啥意义-->
<context:property-placeholder location="jdbc2.properties,jdbc.properties" system-properties-mode="NEVER"/>
<!--方式二-->
<context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
<!--方式三 -->
<context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
<!--方式四-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
<bean id="bookDao" class="com.hnu.dao.impl.BookDaoImpl">
<!--<property name="name" value="${jdbc.username}"/>-->
<!--直接读取配置文件中定义的变量名读取出来的是环境变量,需要加system-properties-mode="NEVER"表示从不读取环境变量-->
<property name="name" value="${username}"/>
</bean>
说明:
- 方式一:可以实现,如果配置文件多的话,每个都需要配置
- 方式二:
*.properties
代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准 - 方式三:标准的写法,
classpath:
代表的是从根路径下开始查找,但是只能查询当前项目的根路径 - 方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件
步骤3:创建接口和实现类BookDao、BookDaoImpl
package com.hnu.dao;
public interface BookDao {
public void save();
}
package com.hnu.dao.impl;
import com.hnu.dao.BookDao;
public class BookDaoImpl implements BookDao {
private String name;
public void setName(String name) {
this.name = name;
}
public void save() {
System.out.println("book dao save ..." + name);
}
}
步骤4:运行程序App
package com.hnu;
import com.hnu.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.sql.DataSource;
import java.util.Map;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//打印环境变量
//Map<String, String> getenv = System.getenv();
//System.out.println(getenv);
}
}
运行结果:
2.核心容器
这里所说的核心容器,大家可以把它简单的理解为ApplicationContext
环境准备:
步骤1:pom.xml添加Spring的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
步骤2:resources下添加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">
<bean id="bookDao" class="com.hnu.dao.impl.BookDaoImpl"/>
</beans>
步骤3:创建接口和实现类BookDao、BookDaoImpl
package com.hnu.dao;
public interface BookDao {
public void save();
}
package com.hnu.dao.impl;
import com.hnu.dao.BookDao;
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("constructor");
}
public void save() {
System.out.println("book dao save ...");
}
}
步骤4:运行程序App
package com.hnu;
import com.hnu.dao.BookDao;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
public class App {
public static void main(String[] args) {
//如果在xml配置文件中加上lazy-init="true就会延迟加载
//创建容器方式一:
//ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//创建容器方式二:类路径下的XML配置文件
// ApplicationContext ctx = new FileSystemXmlApplicationContext("E:\\Spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");
//获取bean方式一:类型转换
//BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//bookDao.save();
//方式二:类的字节码文件
// BookDao bookDao1 = ctx.getBean("bookDao", BookDao.class);
// bookDao1.save();
//方式三:按类型注入 必须要确保IOC容器中该类型对应的bean对象只能有一个 该类型只能有一个实现类
// BookDao bookDao2 = ctx.getBean(BookDao.class);
// bookDao2.save();
//使用BeanFactory来创建IOC容器 目前已经不再使用了
Resource resource = new ClassPathResource("applicationContext.xml");
BeanFactory beanFactory = new XmlBeanFactory(resource);
//在加载ioc容器时不会加载对象,延迟加载
// BookDao bookDao = beanFactory.getBean(BookDao.class);
// bookDao.save();
}
}
使用BeanFactory来创建ioc容器运行结果,发现没有运行无参构造也就说明没有加载对象:
使用ClassPathXmlApplicationContext来创建容器,发现运行了无参构造,说明不存在延迟加载
总结:
容器相关:
- BeanFactory是IOC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
- ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
- ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
- ApplicationContext接口常用初始化类
- ClassPathXmlApplicationContext(常用)
- FileSystemXmlApplicationContext
bean相关:
依赖相关注入:
3.注解开发:
注解开发可以极大简化开发,不用我们配之前的那些复杂配置,像xml容器配置文件,注入的配置都可以就此不用,只需要注解就可以实现!怎么实现,一块来看看√
1.非纯注解开发
环境准备:
步骤1:导入pom 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"
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 https://www.springframework.org/schema/context/spring-context.xsd">
<!-- <bean id="bookDao" class="com.hnu.dao.impl.BookDaoImpl"/>-->
<!--扫描包,如果包中有注解就拿来用-->
<context:component-scan base-package="com.hnu"/>
</beans>
component-scan
- component:组件,Spring将管理的bean视作自己的一个组件
- scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
- 包路径越多[如:com.hnu.dao.impl],扫描的范围越小速度越快
- 包路径越少[如:com.hnu],扫描的范围越大速度越慢
- 一般扫描到项目的组织名称即Maven的groupId下[如:com.hnu]即可。
步骤2:创建接口和实现类BookDao、BookDaoImpl
package com.hnu.dao;
public interface BookDao {
public void save();
}
package com.hnu.dao.impl;
import com.hnu.dao.BookDao;
import org.springframework.stereotype.Component;
//注解写在哪个类上,注解就是那个类的全类名,相当于一个bean
@Component("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
XML与注解配置的对应关系:
注意:@Component注解不可以添加在接口上,因为接口是无法创建对象的。
步骤3:运行程序App
package com.hnu;
import com.hnu.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//按类型自动注入
BookDao bookDao = ctx.getBean(BookDao.class);
bookDao.save();
}
}
结果:
几个常用注解:
对于@Component注解,还衍生出了其他三个注解@Controller
、@Service
、@Repository
通过查看源码会发现:
这三个注解和@Component注解的作用是一样的,为什么要衍生出这三个呢?
方便我们后期在编写类的时候能很好的区分出这个类是属于表现层
、业务层
还是数据层
的类。
2.纯注解开发
上面已经可以使用注解来配置bean,但是依然有用到配置文件,在配置文件中对包进行了扫描,Spring在3.0版已经支持纯注解开发
- Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
配置环境
步骤1:导入pom spring依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
步骤2:创建接口和实现类BookService、BookServiceImpl
package com.hnu.service;
public interface BookService {
public void save();
}
package com.hnu.service;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
public void save() {
System.out.println("book service save ...");
}
}
步骤3:创建配置config包并创建配置类SpringConfig
1.在配置类上添加@Configuration
注解,将其标识为一个配置类,替换applicationContext.xml
2.在配置类上添加包扫描注解@ComponentScan
替换<context:component-scan base-package=""/>
package com.hnu.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//用注解代替包扫描 @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
//@ComponentScan("com.hnu")
@ComponentScan({"com.hnu.service","com.hnu.dao"})
public class SpringConfig {
}
- Java类替换Spring核心配置文件
- @Configuration注解用于设定当前类为配置类
- @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
- 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
步骤4:运行程序AppForAnnotation
public class AppForAnnotation {
public static void main(String[] args) {
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
结果:
小结:
- 记住@Component、@Controller、@Service、@Repository这四个注解
- applicationContext.xml中
<context:component-san/>
的作用是指定扫描包路径,注解为@ComponentScan - @Configuration标识该类为配置类,使用类替换applicationContext.xml文件
- ClassPathXmlApplicationContext是加载XML配置文件
- AnnotationConfigApplicationContext是加载配置类
3.注解开发bean作用与生命周期管理
环境准备:
步骤1:导入pom spring依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
步骤2:创建接口和实现类BookDao、BookDaoImpl
package com.hnu.dao;
public interface BookDao {
public void save();
}
package com.hnu.dao.impl;
//注解写在哪个类上,注解就是那个类的全类名,相当于一个bean
@Repository
//@Scope("prototype")是否是单例化,默认单例
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//在构造方法之后执行,替换 init-method
@PostConstruct
public void init() {
System.out.println("init...");
}
@PreDestroy
//在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy...");
}
}
步骤3.创建配置config包并创建配置类SpringConfig
package com.hnu.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
//用注解代替包扫描 @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
//@ComponentScan("com.hnu")
@ComponentScan({"com.hnu.service","com.hnu.dao"})
public class SpringConfig {
}
步骤4:运行程序AppForAnnotation
package com.hnu;
public class AppForAnnotation {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = ctx.getBean(BookDao.class);
System.out.println(bookDao);
ctx.registerShutdownHook();
}
}
运行结果:
注意:@PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
找不到的原因是,从JDK9以后jdk中的javax.annotation包被移除了,这两个注解刚好就在这个包中。
小结:
4.注解开发依赖注入
Spring为了使用注解简化开发,并没有提供构造函数注入
、setter注入
对应的注解,只提供了自动装配的注解实现。
准备环境
步骤1:导入pom spring依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
步骤2:创建配置config包并创建配置类SpringConfig
package com.hnu.config;
@Configuration
//用注解代替包扫描 @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
//@ComponentScan("com.hnu")
@ComponentScan({"com.hnu.service","com.hnu.dao"})
//加载jdbc配置文件
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
}
步骤3.添加BookDao、BookDaoImpl、BookService、BookServiceImpl类
package com.hnu.dao;
public interface BookDao {
public void save();
}
package com.hnu.dao.impl;
//注解写在哪个类上,注解就是那个类的全类名,相当于一个bean
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
//注解实现简单数据类型的注入!!
@Value("${name}")
private String name;
public void save() {
System.out.println("book dao save ..."+name);
}
}
package com.hnu.service;
public interface BookService {
public void save();
}
package com.hnu.service;
@Service
public class BookServiceImpl implements BookService {
//注解实现按照类型注入!!
@Autowired
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉
为什么setter方法可以删除呢?
- 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
- 普通反射只能获取public修饰的内容
- 暴力反射除了获取public修饰的内容还可以获取private修改的内容
- 所以此处无需提供setter方法
@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2
@Repository
public class BookDaoImpl2 implements BookDao {
public void save() {
System.out.println("book dao save ...2");
}
}
这个时候再次运行App,就会报错
此时,按照类型注入就无法区分到底注入哪个对象,解决方案:按照名称注入
先给两个Dao类分别起个名称
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
@Repository("bookDao2")
public class BookDaoImpl2 implements BookDao {
public void save() {
System.out.println("book dao save ...2" );
}
}
此时就可以注入成功,但是得思考个问题
-
@Autowired是按照类型注入的,给BookDao的两个实现起了名称,它还是有两个bean对象,为什么不报错?
-
@Autowired默认按照类型自动装配,如果IOC容器中同类的Bean找到多个,就按照变量名和Bean的名称匹配。因为变量名叫
bookDao
而容器中也有一个bookDao
,所以可以成功注入。
分析下面这种情况是否能完成注入呢?
不行,因为按照类型会找到多个bean对象,此时会按照bookDao
名称去找,因为IOC容器只有名称叫bookDao1
和bookDao2
,所以找不到,会报NoUniqueBeanDefinitionException
注解实现按照名称注入
当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候该如何解决,就需要使用到@Qualifier
来指定注入哪个名称的bean对象。
@Service
public class BookServiceImpl implements BookService {
@Autowired
@Qualifier("bookDao1")
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
@Qualifier注解后的值就是需要注入的bean的名称。
注意:@Qualifier不能独立使用,必须和@Autowired一起使用
步骤4.创建propertity配置文件
1.@Value
一般会被用在从properties配置文件中读取内容进行使用
2.如果读取的properties配置文件有多个,可以使用@PropertySource
的属性来指定多个
@PropertySource({"jdbc.properties","xxx.properties"})
3.@PropertySource
注解属性中不支持使用通配符*
,运行会报错
@PropertySource({"*.properties"})
4.@PropertySource
注解属性中可以把classpath:
加上,代表从当前项目的根路径找文件
@PropertySource({"classpath:jdbc.properties"})
步骤5.运行程序AppForAnnotation
package com.hnu;
public class AppForAnnotation {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookService bookService = ctx.getBean(BookService.class);
bookService.save();
}
}
运行结果: