<?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">
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
▶ 读取单个属性
▷ 步骤1 : 在项目中添对应的类
BookDao和BookDaoImpl类,并在BookDaoImpl类中添加name属性与setter方法
public interface BookDao { public void save();
}
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);
}
}
▷ 步骤2 : 完成配置文件的读取与注入
在applicationContext.xml添加配置,bean的配置管理、读取外部properties、依赖注入:
<?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">
<context:property-placeholder location="jdbc.properties"/>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="name" value="${jdbc.driver}"/>
</bean>
</beans>
▶ 注意事项
▷ 问题一 : 键值对的key为username引发的问题
1.在properties中配置键值对的时候,如果key设置为username
username=root666
2.在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"
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">
<context:property-placeholder location="jdbc.properties"/>
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<property name="name" value="${username}"/>
</bean>
</beans>
3.运行后,在控制台打印的却不是root666,而是自己电脑的用户名。出现问题的原因是<context:property-placeholder/> 标签会加载系统的环境变量,而且环境变量的值会被优先加载。运行下面的代码查看系统的环境变量:
public static void main(String[] args) throws Exception{ Map<String, String> env = System.getenv();
System.out.println(env);
}
解决方案
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>
system-properties-mode: 设置为NEVER,表示不加载系统属性,就可以解决上述问题。
还有一个解决方案就是避免使用username作为属性的key。
▷ 问题二 : 当有多个properties配置文件需要被加载,该如何配置?
1.调整下配置文件的内容,在resources下添加jdbc.properties,jdbc2.properties,内容如下:
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db
jdbc.username=root
jdbc.password=root
jdbc2.properties
username=root666
2.修改applicationContext.xml
<!--方式一 --><context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
● 方式一 : 可以实现,如果配置文件多的话,每个都需要配置
<!--方式二--><context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>
● 方式二 : \*.properties 代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准
<!--方式三 --><context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>
● 方式三 : 标准的写法,classpath:代表的是从根路径下开始查找,但是只能查询当前项目的根路径
<!--方式四--><context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
● 方式四 : 不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件
▶ 小结
● 开启
context
命名空间:
● 加载properties配置文件:
<context:property-placeholder location="" system-properties-mode="NEVER"/>
● 在applicationContext.xml引入properties配置文件中的值:
${key}
3、核心容器
▶ 容器的创建方式
▷ 类路径下的XML配置文件:直接加载类路径下的文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
▷ 文件系统下的XML配置文件:需要写完整的系统路径
ApplicationContext ctx = new FileSystemXmlApplicationContext("
D:\\workspace\\spring\\spring_10_container\\
src\\main\\resources\\applicationContext.xml");
▶ bean 获取的三种方式
▷ 方式一 :这种方式存在的问题是每次获取的时候都需要进行类型转换
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
▷ 方式二:这种方式可以解决类型强转问题,但是参数又多加了一个。
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
▷ 方式三 : 这种方式就类似依赖注入中的按类型注入。必须要确保IOC容器中该类型对应的bean对象只能有一个。
BookDao bookDao = ctx.getBean(BookDao.class);
▶ 容器类层次结构
(1)在IDEA中双击shift,输入BeanFactory
(2)点击进入BeanFactory类,ctrl+h,就能查看到如下结构的层次关系
▶ BeanFactory的使用
▷ 使用BeanFactory来创建IOC容器的具体实现方式为:
public class AppForBeanFactory { public static void main(String[] args) {
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean(BookDao.class);
bookDao.save();
}
}
▷ 对比
● BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建
● ApplicationContext是立即加载,容器加载的时候就会创建bean对象
● ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置:(加上 lazy-init)
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy-init="true"/>
▶ 总结
▷ 容器相关
● BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延迟加载
● ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载
● ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能
● ApplicationContext接口常用初始化类
○ ClassPathXmlApplicationContext(常用)
○ FileSystemXmlApplicationContext▷ bean相关
▷ 依赖注入相关
二、注解开发
1、注解开发定义bean
▶ 实现步骤
▷ 步骤1 : 删除原XML配置
将配置文件中的<bean>标签删除掉
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
▷ 步骤2 : Dao上添加注解
在BookDaoImpl类上添加@Component注解
@Component("bookDao")public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ..." );
}
}
● 注意 : @Component注解不可以添加在接口上,因为接口是无法创建对象的。
● XML与注解配置的对应关系:
▷ 步骤3 : 配置Spring的注解包扫描
为了让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">
<context:component-scan base-package="com.itheima"/>
</beans>
● 注意:
1、component-scan:
○ component : 组件,Spring将管理的bean视作自己的一个组件
○ scan:扫描2、base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
○ 包路径越多[如:com.itheima.dao.impl],扫描的范围越小速度越快
○ 包路径越少[如:com.itheima],扫描的范围越大速度越慢
○ 一般扫描到项目的组织名称即Maven的groupId下[如:com.itheima]即可。▷ 步骤4 : Service上添加注解
在BookServiceImpl类上也添加@Component交给Spring框架管理
@Componentpublic class BookServiceImpl implements BookService {
}
▷ 步骤5 : 运行程序
在App类中,从IOC容器中获取BookServiceImpl对应的bean对象,打印
public class App { public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//按名称
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
//按类型获取bean
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
说明 :
● BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象
● @Component注解如果不起名称,会有一个默认值就是当前类名首字母小写,所以也可以按照名称获取,如:
BookService bookService = (BookService)ctx.getBean("bookServiceImpl");System.out.println(bookService);
● 对于@Component注解,还衍生出了其他三个注解@Controller、@Service、@Repository
通过查看源码会发现:(这三个注解和@Component注解的作用是一样的,为什么要衍生出这三个呢?为了方便我们后期在编写类的时候能很好的区分出这个类是属于表现层、业务层还是数据层的类。)
● @Component
2、纯注解开发模式
▶ 实现方式
首先将配置文件applicationContext.xml删除掉,使用类来替换。
▷ 步骤1 : 创建配置类
创建一个配置类SpringConfig
public class SpringConfig {}
▷ 步骤2 : 标识该类为配置类
在配置类上添加@Configuration注解,将其标识为一个配置类,替applicationContext.xml
@Configurationpublic class SpringConfig {
}
▷ 步骤3 : 用注解替换包扫描配置
在配置类上添加包扫描注解@ComponentScan替换<context:component-scan base-package=""/>
@Configuration@ComponentScan("com.itheima")
public class SpringConfig {
}
▷ 步骤4 : 创建运行类并执行
创建一个新的运行类AppForAnnotation
public class AppForAnnotation { public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao);
BookService bookService = ctx.getBean(BookService.class);
System.out.println(bookService);
}
}
运行AppForAnnotation,可以看到两个对象依然被获取成功。
▶ 小总结
● Java类替换Spring核心配置文件
● @Configuration注解用于设定当前类为配置类
● @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({com.itheima.service","com.itheima.dao"})
● 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
//加载配置文件初始化容器ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
● @Configuration
● @ComponentScan
3、注解开发bean作用范围与生命周期管理
▶ @Scope 注解
默认情况下bean是单例,要想变成非单例,只需要在其类上添加
@scope
注解,列如:
@Repository//@Scope设置bean的作用范围
@Scope("prototype")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
▶ Bean的生命周期
● @PostConstruct
● @PreDestroy
● 注意 : @PostConstruct和@PreDestroy注解如果找不到,需要导入下面的jar包。找不到的原因是,从JDK9以后jdk中的javax.annotation包被移除了,这两个注解刚好就在这个包中。
<dependency> <groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
例如:
@Repositorypublic class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
@PostConstruct //在构造方法之后执行,替换 init-method
public void init() {
System.out.println("init ...");
}
@PreDestroy //在销毁方法之前执行,替换 destroy-method
public void destroy() {
System.out.println("destroy ...");
}
}
要想看到两个方法执行,需要注意的是destroy只有在容器关闭的时候,才会执行。
▶ 小结
4、注解开发依赖注入
● @Autowired
▶ 添加依赖
pom.xml 添加Spring的依赖
<dependencies> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
</dependencies>
▶ 注解实现按照类型注入
▷ (1) 在BookServiceImpl类的bookDao属性上添加@Autowired注解
@Servicepublic class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
// public void setBookDao(BookDao bookDao) {
// this.bookDao = bookDao;
// }
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
注意:
● @Autowired可以写在属性上,也可也写在setter方法上,最简单的处理方式是写在属性上并将setter方法删除掉 ● 为什么setter方法可以删除呢?
○ 自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
○ 普通反射只能获取public修饰的内容
○ 暴力反射除了获取public修饰的内容还可以获取private修改的内容
○ 所以此处无需提供setter方法▷ (2)@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加BookDaoImpl2
@Repositorypublic 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而容器中也有一个booDao,所以可以成功注入。
● 分析下面这种情况是否能完成注入呢?
不行,因为按照类型会找到多个bean对象,此时会按照
bookDao
名称去找,因为IOC容器只有名称叫bookDao1
和bookDao2
,会找不到,会报NoUniqueBeanDefinitionException
异常。▶ 注解实现按照名称注入
● @Qualifier
当根据类型在容器中找到多个bean,注入参数的属性名又和容器中bean的名称不一致,这个时候该如何解决,就需要使用到
@Qualifier
来指定注入哪个名称的bean对象。
@Servicepublic 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一起使用。
▶ 简单数据类型注入
● @Value
简单类型注入的是基本数据类型或者字符串类型,下面在
BookDaoImpl
类中添加一个name
属性,用其进行简单类型注入。数据类型换了,对应的注解也要跟着换,这次使用@Va
lue注解,将值写入注解的参数中就行了
@Repository("bookDao")public class BookDaoImpl implements BookDao {
@Value("itheima")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。
▶ 注解读取properties配置文件
● @PropertySource
● @Value
简单类型注入的是基本数据类型或者字符串类型,下面在
BookDaoImpl
类中添加一个name
属性,用其进行简单类型注入。数据类型换了,对应的注解也要跟着换,这次使用@Value
注解,将值写入注解的参数中就行了
@Repository("bookDao")public class BookDaoImpl implements BookDao { @Value("itheima")
private String name;
public void save() {
System.out.println("book dao save ..." + name);
}
}
注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。
▶ 注解读取properties配置文件
● @PropertySource