该系列为imooc Spring从入门到进阶笔记,跟随课程加入自己见解,同时也为项目中碰到一些问题做了解答
大纲
1、Spring的工厂类
1.1、Spring工厂类结构
现在用的是:ApplicationContext
生成Bean的时机:ApplicationContext是在加载配置文件,就会将配置文件中所有单例模式生成的类全部
实例化
以前用的是老的工厂类:BeanFactory
生成Bean的时机:BeanFactory是在完成工厂实例化后,调用getBean后,才会去创建Java Bean的实例
具体的实现为下面两种,仅是创建Spring工厂类的方式不一样
ClassPathXmlApplicationContext:加载类路径下的配置文件(项目中的)
FileSystemXmlApplicationContext:加载磁盘系统中的配置文件(不在项目中,而是在某个磁盘中的配置文件)
1.2、两种创建Spring工厂类对比
@Test
//Spring的方式实现-读取项目中的配置文件
public void demo2(){
//创建Spring的工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类
UserService userService=(UserService) applicationContext.getBean("userService");
userService.sayHello();
}
@Test
//Spring的方式实现-读取磁盘系统中的配置文件
public void demo3(){
//创建Spring的工厂
ApplicationContext applicationContext=new FileSystemXmlApplicationContext("C:\\Users\\35208\\Desktop\\applicationContext.xml");
//通过工厂获得类
UserService userService=(UserService) applicationContext.getBean("userService");
userService.sayHello();
}
2、Spring的Bean管理(XML方式)
我们把类交给Spring,然后Spring来帮我们生成这个类的实例,那它是如何来生成的呢?那我们首先来看Spring三种实例化Bean的方式
2.1、三种实例化Bean的方式
- 使用类构造器实例化(默认无参数)
- 使用静态工厂方法实例化(简单工厂模式)
- 使用实例工厂方法实例化(工厂方法模式)
2.1.1、使用类构造器实例化(默认无参数)
- 新建com.imooc.ioc.demo2>新建Bean1
package com.imooc.ioc.demo2;
/**
* Bean的实例化的三种方式:第一种采用无参数的构造方法的方式
*/
public class Bean1 {
public Bean1() {
System.out.println("Bean1被实例化了...");
}
}
- 在applicationContext.xml中进行bean标签配置
<!--Bean的实例化的三种方式-->
<!--第一种:无参构造器的方式-->
<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
- 在com.imooc.ioc.demo2>新建测试类SpringDemo2
package com.imooc.ioc.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的实例化的三种方式
*/
public class SpringDemo2 {
//第一种:无参构造器的方式
@Test
public void demo1(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean1 bean1=(Bean1) applicationContext.getBean("bean1");
}
}
- 执行测试代码
可见Bean1已经被成功实例化
2.1.2、使用静态工厂方法实例化(简单工厂模式)
- 在com.imooc.ioc.demo2>新建Bean2
package com.imooc.ioc.demo2;
/**
* Bean的实例化的三种方式:第二种静态工厂实例化方式
*/
public class Bean2 {
}
- 在com.imooc.ioc.demo2>新建工厂类Bean2Factory
package com.imooc.ioc.demo2;
/**
* Bean2的静态工厂
*/
public class Bean2Factory {
public static Bean2 createBean2(){
System.out.println("Bean2Factory的方法已经执行了...");
return new Bean2();
}
}
- 在applicationContext.xml中进行bean标签配置
<!--第一种:无参构造器的方式-->
<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
<!--第二种:静态工厂的方式-->
<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
- 在com.imooc.ioc.demo2>在测试类SpringDemo2新增使用静态工厂方法实例化的测试代码
package com.imooc.ioc.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的实例化的三种方式
*/
public class SpringDemo2 {
//第一种:无参构造器的方式
@Test
public void demo1(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean1 bean1=(Bean1) applicationContext.getBean("bean1");
}
//第二种:静态工厂的方式
@Test
public void demo2(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean2 bean1=(Bean2) applicationContext.getBean("bean2");
}
}
- 执行测试代码
可见Bean2已经被成功实例化
2.1.3、使用实例工厂方法实例化(工厂方法模式)
- 在com.imooc.ioc.demo2>新建Bean3
package com.imooc.ioc.demo2;
/**
* Bean的实例化的三种方式:第二种实例工厂实例化方式
*/
public class Bean3 {
}
- 在com.imooc.ioc.demo2>新建工厂类Bean3Factory
package com.imooc.ioc.demo2;
/**
*Bean2的实例工厂
*/
public class Bean3Factory {
public Bean3 createBean3(){
System.out.println("Bean3Factory的方法已经执行了...");
return new Bean3();
}
}
- 在applicationContext.xml中进行bean标签配置
<!--第一种:无参构造器的方式-->
<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>
<!--第二种:静态工厂的方式-->
<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>
<!--第三种:实例工厂的方式-->
<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
- 在com.imooc.ioc.demo2>在测试类SpringDemo2新增使用实例工厂方法实例化的测试代码
package com.imooc.ioc.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的实例化的三种方式
*/
public class SpringDemo2 {
//第一种:无参构造器的方式
@Test
public void demo1(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean1 bean1=(Bean1) applicationContext.getBean("bean1");
}
//第二种:静态工厂的方式
@Test
public void demo2(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean2 bean1=(Bean2) applicationContext.getBean("bean2");
}
//第三种:实例工厂的方式
@Test
public void demo3(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Bean3 bean1=(Bean3) applicationContext.getBean("bean3");
}
}
- 执行测试代码
可见Bean3已经被成功实例化
2.2、Bean的常规配置
2.2.1、id和name
- 一般情况下,装配一个Bean时,通过指定一个id属性作为Bean的名称
- id 属性在IOC容器中必须是唯一的
- id和name : 这两个一样,都是作为Bean的名称。区别:Bean的名称中含有特殊字符,需要使用name属性
2.2.2、class
- class用于设置一个类的完全路径名称,主要作用是IOC容器生成类的实例
2.2.3、scope
scope的取值即为Bean 的作用域,有以下四种
- singleton 为默认值(常用)
- prototype通常是在Spring去整合Struts2的时候,Struts2的action是多例的,要用Spring来管理,所以要设置为prototype (常用)
- request与session主要应用于Web项目
2.2.4、Bean的常规配置的测试
- 在com.imooc.ioc包下>新建demo3>新建Person类
package com.imooc.ioc.demo3;
public class Person {
}
- 在applicationContext.xml中进行bean标签配置,scope为默认
<!--Bean的作用范围-->
<bean id="person" class="com.imooc.ioc.demo3.Person"/>
3.在com.imooc.ioc包下>demo3>新建测试代码SpringDemo3类
package com.imooc.ioc.demo3;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Bean的作用范围测试
*/
public class SpringDemo3 {
//第一种:单例模式
@Test
public void demo1(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Person person1=(Person) applicationContext.getBean("person");
Person person2=(Person) applicationContext.getBean("person");
//当scope为默认或者singleton时比较两个示例地址是否一致,答案是一致的
//当scope为prototype时比较两个示例地址是否一致,答案是不一致的
System.out.println(person1);
System.out.println(person2);
}
}
- 执行代码,当scope为默认或者singleton时比较两个示例地址是否一致,答案是一致的,为单例模式
- 修改applicationContext.xml中bean标签配置,scope为prototype
<!--Bean的作用范围-->
<bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>
- 执行代码,当scope为prototype时比较两个示例地址是否一致,答案是不一致的,为多里模式
2.3、Spring容器中Bean的生命周期
2.3.1、Spring容器中Bean的生命周期的两个属性的配置
Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<bean id=“xxx” class=“...Yoo”
init-method=“init”
destory-method=“destroy”/>
-
init-method: “init” 方法名称,即bean被载入到容器时调用init()方法(单利、多例都有效),该方法名称可自定义
-
destroy-method: "destroy"方法名称,即 bean从容器中删除时调用destroy()方法(只有单利有效,即scope= singleton有效),该方法名称可自定义
下面我们在代码中测试一下
- 在com.imooc.ioc.demo3包下>新建Man类
package com.imooc.ioc.demo3;
public class Man {
public Man() {
System.out.println("Man被实例化了...");
}
public void setup(){
System.out.println("Man被初始化...");
}
public void teardown(){
System.out.println("Man被销毁了...");
}
}
- 在applicationContext.xml中进行bean标签配置
<bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown"/>
- 在com.imooc.ioc.demo3包下>完善测试代码SpringDemo3类
//测试spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法
@Test
public void demo2(){
//创建工厂
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Man man=(Man) applicationContext.getBean("man");
//关闭工厂
applicationContext.close();
}
- 运行测试代码demo2
2.3.2、Bean的生命周期的完整过程
Spring创建类的时候,实际经历了11个过程
Spring bean生命周期
1. instantiate bean对象实例化
2. populate properties 封装属性
3. 如果Bean实现BeanNameAware 执行setBeanName(该方法用于获取applicationContext.xml配置文件中bean标签的id或name的值)
4. 如果Bean实现BeanFactoryAware或者applicationAontextAware设置工厂setBeanFactory或者上下文对象setApplicationContext
5. 如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforInitialization()方法
6. 如果Bean实现InitializingBean执行afterPropertieSet()方法
7. 调用<bean init-method="init">指定的初始化方法init()
8 .如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterinitializtion()方法 从而实现类的增强
8. 执行业务方法
9. 如果Bean实现DisposableBean执行destroy()方法
10. 调用<bean destroy-method="customerDestroy">指定销毁方法
在代码中演示Spring bean的完整生命周期
- 修改com.imooc.ioc.demo3包下的Man类
package com.imooc.ioc.demo3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class Man implements BeanNameAware, ApplicationContextAware, InitializingBean, DisposableBean {
private String name;
public Man() {
System.out.println("第一步:Man被实例化了");
}
public void setName(String name) {
System.out.println("第二步:设置属性");
this.name = name;
}
@Override
public void setBeanName(String s) {
System.out.println("第三步:设置Bean的名称");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("第四步:了解工厂的信息");
}
//第五步由实现了BeanPostProcessor接口的MyBeanPostProcessor类执行postProcessBeforeInitialization方法
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("第六步:属性设置后执行的方法");
}
public void setup(){
System.out.println("第七步:Man被初始化");
}
//第八步由实现了BeanPostProcessor接口的MyBeanPostProcessor类执行postProcessAfterInitialization方法
public void run(){
System.out.println("第九步:执行自身的业务方法");
}
@Override
public void destroy() throws Exception {
System.out.println("第十步:执行Spring销毁方法");
}
public void teardown(){
System.out.println("第十一:Man被销毁了");
}
}
- 在com.imooc.ioc.demo3包下新建MyBeanPostProcessor(处理第5、8步)
在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
package com.imooc.ioc.demo3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
//在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:初始化前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第八步:初始化后执行的方法");
return bean;
}
}
- 在applicationContext.xml中进行bean标签配置(配置BeanPostProcessor,同时注释掉其他Bean即注释其他的bean标签,避免影响试验)
<?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">
<!--UserService的创建权交给Spring -->
<!-- <bean id="userService" class="com.imooc.ioc.demo1.UserServiceImpl">-->
<!-- <!– 设置属性 –>-->
<!-- <property name="name" value="李四"/>-->
<!-- </bean>-->
<!--Bean的实例化的三种方式-->
<!--第一种:无参构造器的方式-->
<!--<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>-->
<!--第二种:静态工厂的方式-->
<!--<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>-->
<!--第三种:实例工厂的方式-->
<!--<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>-->
<!--<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>-->
<!--Bean的作用范围-->
<!-- <bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>-->
<bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown">
<property name="name" value="张三" />
</bean>
<bean class="com.imooc.ioc.demo3.MyBeanPostProcessor"/>
</beans>
- 完善com.imooc.ioc.demo3包下SpringDemo3测试代码
@Test
public void demo2(){
//创建工厂
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
Man man=(Man) applicationContext.getBean("man");
man.run();
//关闭工厂
applicationContext.close();
}
启动测试代码,试验成功
2.3.3、BeanPostProcessor的作用
使用BeanPostProcessor可以增强一个类中的方法,为AOP做铺垫,在类的实例化过程中对原有的类进行增强
演示怎么去增强一个类里面的某个方法?
以下例子中的代码不是很标准,我们现在没有学习第三方的技术,只能是面向接口编程,这样的话我们可以使用JDK的动态代理
- com.imooc.ioc.demo3包下>新建UserDao接口
该接口提供了增删改查的4个方法
package com.imooc.ioc.demo3;
public interface UserDao {
public void findAll();
public void save();
public void update();
public void delete();
}
- com.imooc.ioc.demo3包下>新建UserDaoImpl类(UserDao接口的实现类)
package com.imooc.ioc.demo3;
public class UserDaoImpl implements UserDao{
@Override
public void findAll() {
System.out.println("查询用户");
}
@Override
public void save() {
System.out.println("保存用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
- 在applicationContext.xml中进行bean标签配置(保留配置BeanPostProcessor,同时注释掉其他Bean即注释其他的bean标签,避免影响试验)
<?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">
<!--UserService的创建权交给Spring -->
<!-- <bean id="userService" class="com.imooc.ioc.demo1.UserServiceImpl">-->
<!-- <!– 设置属性 –>-->
<!-- <property name="name" value="李四"/>-->
<!-- </bean>-->
<!--Bean的实例化的三种方式-->
<!--第一种:无参构造器的方式-->
<!--<bean id="bean1" class="com.imooc.ioc.demo2.Bean1"/>-->
<!--第二种:静态工厂的方式-->
<!--<bean id="bean2" class="com.imooc.ioc.demo2.Bean2Factory" factory-method="createBean2"/>-->
<!--第三种:实例工厂的方式-->
<!--<bean id="bean3Factory" class="com.imooc.ioc.demo2.Bean3Factory"/>-->
<!--<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>-->
<!--Bean的作用范围-->
<!-- <bean id="person" class="com.imooc.ioc.demo3.Person" scope="prototype"/>-->
<!-- <bean id="man" class="com.imooc.ioc.demo3.Man" init-method="setup" destroy-method="teardown">-->
<!-- <property name="name" value="张三" />-->
<!-- </bean>-->
<bean class="com.imooc.ioc.demo3.MyBeanPostProcessor"/>
<bean id="userDao" class="com.imooc.ioc.demo3.UserDaoImpl"/>
</beans>
- 完善com.imooc.ioc.demo3包下SpringDemo3测试代码
//测试使用BeanPostProcessor可以增强一个类中的方法
@Test
public void demo3(){
//创建工厂
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
//通过工厂获得类的实例
UserDao userDao=(UserDao) applicationContext.getBean("userDao");
userDao.findAll();
userDao.save();
userDao.update();
userDao.delete();
}
启动测试
那么此时我们想增强一下save()方法,想实现保存之前进行校验,看是不是管理员的操作,该怎么编码呢?
- 在com.imooc.ioc.demo3包下>修改MyBeanPostProcessor
package com.imooc.ioc.demo3;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyBeanPostProcessor implements BeanPostProcessor {
//在Spring容器中,每一个Bean被实例化时,Spring都会调用该类执行第5、8步
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//System.out.println("第五步:初始化前执行的方法");
return bean;
}
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// System.out.println("第八步:初始化后执行的方法");
// return bean;
// }
@Override
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
//System.out.println("第八步:初始化后执行的方法");
if ("userDao".equals(beanName)){
Object proxy=Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("save".equals(method.getName())){
System.out.println("权限校验===========================");
return method.invoke(bean,args);
}
return method.invoke(bean,args);
}
});
return proxy;
}else {
return bean;
}
}
}
启动测试代码
可见在保存用户前进行了权限校验
3、Spring的属性注入(XML方式)
3.1、Spring的属性注入的三种方式
- 对于类成员变量,注入方式有三种
- 构造方法注入
- 属性setter方法注入
- 接口注入
- Spring支持前两种
3.1.1、Spring的属性注入-构造方法注入
- 通过构造方法注入Bean 的属性值或依赖的对象,它保证了 Bean 实例在实
例化后就可以使用。 - 构造器注入在
<constructor-arg>
元素里声明的属性- name表示属性名称
- value表示普通属性,填写 基本类型的值
- ref表示对象属性,填写 对象所属类配置的bean的id(该例子在3.1.2代码中有提现,原理相同)
代码示例
- 在com.imooc.ioc下>新建包demo4>新建User类
package com.imooc.ioc.demo4;
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 在applicationContext.xml中进行bean标签配置
<!--Bean的构造方法的属性注入-->
<bean id="user" class="com.imooc.ioc.demo4.User">
<constructor-arg name="name" value="张三"/>
<constructor-arg name="age" value="23"/>
</bean>
- 在com.imooc.ioc.demo4>新建SpringDemo4测试类
package com.imooc.ioc.demo4;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo4 {
//测试构造方法注入属性
@Test
public void demo1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
User user=(User) applicationContext.getBean("user");
System.out.println(user);
}
}
启动demo1测试代码
可见已成功进行属性注入
3.1.2、Spring的属性注入-set方法注入
- 使用set方法注入,在Spring配置文件中,通过
<property>
设置注入的属性- name表示属性名称
- value表示普通属性,填写 基本类型的值
- ref表示对象属性,填写 对象所属类配置的bean的id
代码示例
- 在com.imooc.ioc.demo4>新建Person类
package com.imooc.ioc.demo4;
public class Person {
private String name;
private Integer age;
private Cat cat;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", cat=" + cat +
'}';
}
}
- 在com.imooc.ioc.demo4>新建Cat类
package com.imooc.ioc.demo4;
public class Cat {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
- 在applicationContext.xml中进行bean标签配置
<!--Bean的set方法的属性注入-->
<bean id="person" class="com.imooc.ioc.demo4.Person">
<property name="name" value="李妹几"/>
<property name="age" value="18"/>
<property name="cat" ref="cat1"/>
<!--
name表示属性名称
value表示普通属性,填写 基本类型的值
ref表示对象属性,填写 对象所属类配置的bean的id
-->
</bean>
<bean id="cat1" class="com.imooc.ioc.demo4.Cat">
<property name="name" value="饼干"/>
</bean>
- 在com.imooc.ioc.demo4>完善SpringDemo4测试类
//测试set方法注入属性
@Test
public void demo2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
Person person=(Person) applicationContext.getBean("person");
System.out.println(person);
}
启动测试
可见set方法注入属性成功
3.1.3、Spring的属性注入-p名称空间
- 使用p命名空间
- 为了简化XML文件配置,Spring从2.5开始引入一个新的p名称空间
- 引入
- 在
<beans>
标签内添加属性 xmlns:p=“http://www.springframework.org/schema/p”
- 在
- 使用
p:<属性名>="xxx"
引入常量值p:<属性名>-ref="xxx"
引用其它Bean对象
代码示例
- 在applicationContext.xml中进行bean标签配置(只保留该节内容的bean标签,避免影响试验及观看学习体验)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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的p名称空间的属性注入-->
<bean id="person" class="com.imooc.ioc.demo4.Person" p:name="李妹几" p:age="18" p:cat-ref="cat1"/>
<bean id="cat1" class="com.imooc.ioc.demo4.Cat" p:name="饼干"/>
</beans>
启动测试
可见p名称空间方式注入属性成功
3.1.4、Spring的属性注入-SpEL注入
- SpEL:spring expression language ,spring表达式语言,对依赖注入进行简化
- 语法:#{表达式}
<bean id="" value="#{表达式}">
SpEL表达式语言
语法:#{}
#{'hello'} :使用字符串
#{beanId}: 使用另一个bean
#{beanId.content.toUpperCase()}: 使用指定名属性,并使用方法
#{T(java.lang.Math).PI}: 使用静态字段或方法
代码示例
- 在com.imooc.ioc.demo4>新建Product类
package com.imooc.ioc.demo4;
public class Product {
private String name;
private Double price;
private Category category;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", price=" + price +
", category=" + category +
'}';
}
}
- 在com.imooc.ioc.demo4>新建Category类
package com.imooc.ioc.demo4;
public class Category {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Category{" +
"name='" + name + '\'' +
'}';
}
}
- 在com.imooc.ioc.demo4>新建ProductInfo类
package com.imooc.ioc.demo4;
public class ProductInfo {
public Double calculatePrice(){
return Math.random()*199;
}
}
- 在applicationContext.xml中进行bean标签配置(只保留该节内容的bean标签,避免影响试验及观看学习体验)
<?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的SpEL的属性注入-->
<bean id="category" class="com.imooc.ioc.demo4.Category">
<property name="name" value="#{'服装'}"/>
</bean>
<bean id="productInfo" class="com.imooc.ioc.demo4.ProductInfo"/>
<bean id="product" class="com.imooc.ioc.demo4.Product">
<property name="name" value="#{'男装'}"/>
<!--使用ProductInfo类的方法,需要先配置ProductInfo类的bean标签,使得该类实例化-->
<property name="price" value="#{productInfo.calculatePrice()}"/>
<property name="category" value="#{category}"/>
</bean>
</beans>
- 在com.imooc.ioc.demo4>完善SpringDemo4测试类
//测试SpEL的注入属性
@Test
public void demo3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
Product product=(Product) applicationContext.getBean("product");
System.out.println(product);
}
启动测试代码
可见SpEL方式注入属性成功
3.1.5、复杂类型的属性注入
- 数组类型的属性注入
- List集合类型的属性注入
- Set集合类型的属性注入
- Map集合类型的属性注入
- Properties类型的属性注入
SpringBean为List/Map/Set/数组注入自定义类型
代码示例
- 在com.imooc.ioc下>新建包demo5>新建CollectionBean类
package com.imooc.ioc.demo5;
import java.util.*;
public class CollectionBean {
private String[] arrs;//数组类型
private List<String> list;//List集和类型
private Set<String> set;//Set集和类型
private Map<String,Integer> map;//Map集和类型
private Properties properties;//属性类型
public String[] getArrs() {
return arrs;
}
public void setArrs(String[] arrs) {
this.arrs = arrs;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, Integer> getMap() {
return map;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean{" +
"arrs=" + Arrays.toString(arrs) +
", list=" + list +
", set=" + set +
", map=" + map +
", properties=" + properties +
'}';
}
}
- 在applicationContext.xml中进行bean标签配置(只保留该节内容的bean标签,避免影响试验及观看学习体验)
<?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的复杂类型的属性注入-->
<bean id="collectionBean" class="com.imooc.ioc.demo5.CollectionBean">
<!--数组类型属性注入-->
<property name="arrs">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<!--List集合类型属性注入-->
<property name="list">
<list>
<value>111</value>
<value>222</value>
<value>333</value>
<!--如果是普通数据类型的值使用value标签-->
<!--如果是对象类型的值使用ref标签-->
</list>
</property>
<!--Set集合类型属性注入-->
<property name="set">
<set>
<value>ddd</value>
<value>eee</value>
<value>fff</value>
<!--如果是普通数据类型的值使用value标签-->
<!--如果是对象类型的值使用ref标签-->
</set>
</property>
<!--Map集合类型属性注入-->
<property name="map">
<map>
<entry key="aaa" value="111"></entry>
<entry key="bbb" value="222"></entry>
<entry key="ccc" value="333"></entry>
<!--如果是普通数据类型的值,在entry标签中使用key,value属性-->
<!--如果是对象类型的值使用ref标签,在entry标签中使用key-ref,value-ref属性-->
</map>
</property>
<!--properties类型属性注入-->
<property name="properties">
<props>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
- 在com.imooc.ioc.demo5包下>新建测试类SpringDemo5
package com.imooc.ioc.demo5;
import com.imooc.ioc.demo4.Product;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo5 {
//测试复杂类型的属性注入
@Test
public void demo1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean collectionBean=(CollectionBean) applicationContext.getBean("collectionBean");
System.out.println(collectionBean);
}
}
启动测试代码
可见复杂类型的属性成功注入
4、Spring的Bean管理(注解方式)
4.1、使用注解定义Bean
注解方式管理Spring Bean
-
需额外引入spring-aop包
-
需在配置文件额外添加context的依赖
- xmlns:context=“http://www.springframework.org/schema/context”
- xsi:schemaLocation=""中添加
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
-
配置文件中开启包注解扫描
<context:component-scan base-package="使用注解的包名" />
包含属性注入,类注入功能 -
Java中使用注解
- @Component(“beanId”)注解Bean类
以下3个注解功能基本与@Component等价 - @Repository 用于对DAO实现类进行标注
- @Service 用于对Service实现类进行标注
- @Controller 用于对Controller实现类进行标注
- @Component(“beanId”)注解Bean类
代码示例
- 使用idea Maven创建Web项目
- 导入Spring核心开发包到创建好的project中,在项目pom文件中引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
-
编写代码
在main下新建java文件夹,右键java文件夹
这样就可以在java文件夹下创建包或编写java代码了 -
在java文件夹>新建com.imooc.demo1包>新建UserService类
使用Component(“beanId”)注解Bean类
package com.imooc.demo1;
import org.springframework.stereotype.Component;
/**
* Spring的Bean管理的注解方式:
* 传统XML方式要去applicationContext中配置<bean id="" class=""></bean>
*/
@Component("userService")
public class UserService {
public String sayHello(String name){
return "Hello"+name;
}
}
- 在com.imooc.demo1包>新建SpringDemo1测试类
package com.imooc.demo1;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo1 {
@Test
public void demo1(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService) applicationContext.getBean("userService");
String s=userService.sayHello("李妹几");
System.out.println(s);
}
}
- 创建Spring配置文件
在main下>新建resources文件夹>新建Spring Config XML文件(Spring配置文件默认命名为applicationContext.xml该名称我们可以自定义)
- 需在配置文件额外添加context的依赖
- xmlns:context=“http://www.springframework.org/schema/context”
- xsi:schemaLocation=""中添加
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
- 配置文件中开启包注解扫描
<context:component-scan base-package="使用注解的包名" />
<?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:component-scan base-package="com.imooc.demo1"/>
</beans>
启动测试代码
可见使用注解定义Bean成功
5、Spring的属性注入(注解方式)
5.1、Spring的属性注入-注解方式
属性注入注解
- 基本类型的注解
- @Value(“属性值”) 有setter()方法注解方法,没有就注解属性
- 引用类型的注解
-
@Autowired
- 自动注入,默认按照类型进行注入,如果存在两个相同Bean类型,则按照名称注入
- 注入可以针对成员变量或者setter方法
- 通过@Autowired的required属性,设置一定要找到匹配的Bean
- @Autowired(required=true):当使用@Autowired注解的时候,其实默认就是@Autowired(required=true),表示注入的时候,该bean必须存在,否则就会注入失败。
- @Autowired(required=false):表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错。
-
@Qualifier
- 与@Autowired联用,在@Qualifier指定注入Bean的名称,注解的Bean必须指定相同名称的Bean
- @Autowired
@Qualifier(“userDao”)
两个注解联合使用,表示一定要找到指定的Bean"userDao",否则注入失败
-
@Resource(naem=“id”)和@Autowired+@Qualifier(“id”)的功能类似
-
代码示例
- 在com.imooc.demo1包>新建UserDao类
package com.imooc.demo1;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDao {
public void save(){
System.out.println("DAO中保存用户");
}
}
- 在com.imooc.demo1包>修改UserService类,新增基本类型属性,新增引用类型属性
package com.imooc.demo1;
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 javax.annotation.Resource;
/**
* Spring的Bean管理的注解方式:
* 传统XML方式要去applicationContext中配置<bean id="" class=""></bean>
*/
@Component("userService")
public class UserService {
@Value("米饭")
private String something;
/* @Autowired 方式1:自动注入*/
/* @Autowired 方式2:两个注解联合使用,表示一定要找到指定的Bean,否则注入失败
@Qualifier("userDao")*/
@Resource(name = "userDao")/*方式3:表示一定要找到指定的Bean,否则注入失败,相当于方式2*/
private UserDao userDao;
public String sayHello(String name){
return "Hello"+name;
}
public void eat(){
System.out.println("eat"+something);
}
public void save(){
System.out.println("Service中保存用户");
userDao.save();
}
}
- 在com.imooc.demo1包>SpringDemo1测试类中新增方法
//测试使用注解进行Bean基本类型属性注入
@Test
public void demo2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService) applicationContext.getBean("userService");
userService.eat();
}
//测试使用注解进行Bean引用类型属性注入
@Test
public void demo3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService=(UserService) applicationContext.getBean("userService");
userService.save();
}
}
启动测试代码中的demo2方法
可见使用注解进行Bean基本类型属性注入成功
启动测试代码中的demo3方法
可见使用注解进行Bean引用类型属性注入成功
5.2、Spring的其他注解
- Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建
和拆卸bean的时候调用bean的两个生命周期方法
XML方式与注解方式的联系
- 使用注解配置的Bean和配置的一样,默认作用范围都是
singleton - @Scope注解用于指定Bean的作用范围
代码示例
- 新建com.imooc.demo2包>新建bean1类
package com.imooc.demo2;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component("bean1")
public class Bean1 {
@PostConstruct
public void init(){
System.out.println("initBean...");
}
public void say(){
System.out.println("say...");
}
@PreDestroy
public void destory(){
System.out.println("destoryBean...");
}
}
- 在com.imooc.demo2包>新建bean2类
package com.imooc.demo2;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component("bean2")
@Scope("prototype")
public class Bean2 {
}
- 在com.imooc.demo2包>新建SpringDemo2测试类
package com.imooc.demo2;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo2 {
//测试使用注解配置bean的两个生命周期方法
@Test
public void demo1(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean1 bean1 = (Bean1)applicationContext.getBean("bean1");
bean1.say();
applicationContext.close();
}
//测试使用注解配置Scope
@Test
public void demo2(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Bean2 bean1 = (Bean2)applicationContext.getBean("bean2");
Bean2 bean2 = (Bean2)applicationContext.getBean("bean2");
System.out.println(bean1 == bean2);
}
}
- 修改applicationContext.xml配置文件,扩大包扫码范围
<context:component-scan base-package="com.imooc"/>
5.启动测试
- 启动测试demo1
可见测试使用注解配置bean的两个生命周期方法成功 - 启动测试demo2
可见测试使用注解配置Scope成功
6、传统XML配置和注解配置混合使用
-
XML方式的优势
- 结构清晰,易于阅读
-
注解方式的优势
- 开发便捷,属性注入方便
-
XML与注解的整合开发
- 引入context命名空间,既参照4.1额外添加context的依赖(用于使用注解)
- 在配置文件中添加
<context:annotation-config>
标签(这个是单独使用属性注入的标签,添加后可使用注解来注入属性)(注意包扫描标签<context:component-scan base-package="使用注解的包名" />
包含属性注入,类注入功能)
-
传统XML配置和注解配置混合使用:
- xml用于配置类,注解主要用于属性注入