目录
1.FactoryBean
Spring 有两种类型 Bean,一种是普通 Bean,另外一种是工厂 Bean(FactoryBean)
普通 Bean:在配置文件中定义的 Bean 类型就是返回类型
工厂 Bean:在配置文件中定义的 Bean 类型可以和返回类型不一致
上述的例子都是普通 Bean 的类型,那么工厂 Bean 该怎么实现呢?
1)创建类,实现 FactoryBean 接口,使其作为一个工厂 Bean
2)实现接口中的方法,在实现方法中定义返回的 Bean 类型
public class MyFactoryBean implements FactoryBean<Course> { @Override public Course getObject() throws Exception { Course course = new Course(); course.setCname("CourseName"); return course; } @Override public Class<?> getObjectType() { return null; } }
3)在 Spring 配置文件中进行配置
<bean id="myFactoryBean" class="com.zking.spring.factorybean.MyBean"></bean>
由于是 FactoryBean,所以再通过上下文获取时,需要使用实现 FactoryBean 时传入的泛型类型进行接收
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");
Course course = applicationContext.getBean("myFactoryBean", Course.class);
如果仍然使用配置文件中定义的 Bean 类型,则会报错
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'myBean' is expected to be of type 'com.zking.spring.factorybean.MyBean' but was actually of type 'com.zking.spring.collectiontype.Course'
2.Bean 作用域和生命周期
2.1 Bean 作用域
在 Spring 里面,可以设置创建 Bean 的实例是单实例还是多实例,默认情况下是单实例
<bean id="book" class="com.zking.spring.collectiontype.Book"></bean>
测试
ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml"); Book book1 = context.getBean("book", Book.class); Book book2 = context.getBean("book", Book.class); System.out.println(book1 == book2); // true 表示是同一个对象,证明默认情况下是单实例
如何设置单实例多实例?
在 Spring 配置文件中 bean 标签里
scope
属性用于设置单实例还是多实例
1)
singleton
,单实例,默认情况下不写也是它2)
prototype
,多实例<bean id="book2" class="com.zking.spring.collectiontype.Book" scope="prototype"></bean>
测试
Book book3 = context.getBean("book2", Book.class); Book book4 = context.getBean("book2", Book.class); System.out.println(book3 == book4); // false 表示不是同一个对象,证明scope为prototype时是多实例
singleton
和prototype
的区别
singleton
和prototype
除了单实例和多实例的差别之外,还有以下区别
设置
scope
值是singleton
时,加载 Spring 配置文件时就会创建单实例对象设置
scope
值是prototype
时,加载 Spring 配置文件时不会创建对象,而是在调用getBean
方法时创建多实例对象
scope
的其他值
scope
的属性值除了singleton
和prototype
之外,其实还有一些属性值,如
request
,每个request
创建一个新的 bean
session
,同一session
中的 bean 是一样的
不过这两个属性值使用非常少,了解即可
2.2 Bean 生命周期
生命周期:从对象创建到对象销毁的过程
Bean 生命周期
通过构造器创建 Bean 实例(无参构造)
为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)
调用 Bean 的初始化方法(需要进行配置初始化方法)
Bean 就可以使用了(对象获取到了)
当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)
代码演示
public class Orders { //无参数构造 public Orders() { System.out.println("第一步 执行无参数构造创建bean实例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 调用set方法设置属性值"); } //创建执行的初始化的方法 public void initMethod() { System.out.println("第三步 执行初始化的方法"); } //创建执行的销毁的方法 public void destroyMethod() { System.out.println("第五步 执行销毁的方法"); } }
Spring 配置文件中的配置、
<bean id="orders" class="com.zking.spring.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手机"></property> </bean>
测试
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("Step4.获取创建Bean实例对象."); System.out.println(orders); // 手动销毁Bean实例 context.close();
执行结果
第一步 执行无参数构造创建bean实例 第二步 调用set方法设置属性值 第三步 执行初始化的方法 第四步 获取创建bean实例对象 com.zking.spring.bean.Orders@c81cdd1 第五步 执行销毁的方法
Spring 中 Bean 更加完整的生命周期其实不止上述 5 步,另外还有 2 步操作叫做 Bean 的后置处理器
2.3 Bean 后置处理器
加上 Bean 后置处理器,Bean 生命周期如下
通过构造器创建 Bean 实例(无参构造)
为 Bean 属性设置值和对其他 Bean 引用(调用 setter 方法)
把 Bean 的实例传递给 Bean 后置处理器的
postProcessBeforeInitialization
方法调用 Bean 的初始化方法(需要进行配置初始化方法)
把 Bean 的实例传递给 Bean 后置处理器的
postProcessAfterInitialization
方法Bean 就可以使用了(对象获取到了)
当容器关闭时,调用 Bean 的销毁方法(需要进行配置销毁方法)
代码演示
1)创建类,实现接口
BeanPostProcessor
,创建后置处理器public class MyBeanPost implements BeanPostProcessor { @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; } }
2)Spring 配置文件中配置后置处理器
<!--配置后置处理器,会为当前配置文件中所有bean添加后置处理器--> <bean id="myBeanPost" class="com.zking.spring.bean.MyBeanPost"></bean>
执行结果
第一步 执行无参数构造创建bean实例 第二步 调用set方法设置属性值 在初始化之前执行的方法 第三步 执行初始化的方法 在初始化之后执行的方法 第四步 获取创建bean实例对象 com.zking.spring.bean.Orders@289d1c02 第五步 执行销毁的方法
3.注解方式
3.1 什么是注解
-
注解是一种代码特殊标记,格式:
@注解名称(属性名称=属性值,属性名称=属性值...)
-
注解作用:在类上面,方法上面,属性上面
-
注解目的:简化 XML 配置
3.2 创建对象
-
@Component
-
@Service
-
@Controller
-
@Repository
上面四个注解功能是一样的,都可以用来创建 Bean 实例
1)引入依赖
spring-aop.xxx.jar
2)开启组件扫描
<?xml version="1.0" encoding="UTF-8"?> <!--引入context名称空间--> <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.zking.spring.dao,com.zking.spring.service"></context:component-scan> </beans>
3)创建类,在类上添加创建对象注解
/** * value可省略,默认值为类名首字母小写 */ @Component(value = "userService") public class UserService { public void add(){ System.out.println("UserService add..."); } }
3.3 组件扫描配置
设置扫描
use-default-filters
表示现在不使用默认filter
,自己配置filter
include-filter
设置扫描哪些内容<context:component-scan base-package="com.zking.spring" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
设置不扫描
配置扫描包下所有内容
exclude-filter
设置不扫描哪些内容<context:component-scan base-package="com.zking.spring"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
4.属性注入
-
@Autowired
根据属性类型进行自动装配 -
@Qualifier
根据属性名称进行注入,需要和@Autowired
一起使用 -
@Resource
可以根据类型和名称注入 -
@Value
根据普通类型注入
4.1 @Autowired
1)创建 Service 和 Dao 对象,在 Service 和 Dao 类上添加创建对象注解
public interface UserDao { void add(); } @Repository public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("UserDaoImpl add..."); } } @Service public class UserService { public void add() { System.out.println("UserService add..."); } }
2)在 Service 类中添加 Dao 类型属性,在属性上面使用注解注入 Dao 对象
@Service public class UserService { @Autowired private UserDao userDao; public void add() { System.out.println("UserService add..."); userDao.add(); } }
因为
@Autowired
是根据属性类型进行注入的,如果 UserDao 的实现类不止一个,比如新增一个 UserDaoImpl2 类@Repository public class UserDaoImpl2 implements UserDao { @Override public void add() { System.out.println("UserDaoImpl2 add..."); } }
那么此时测试程序就会报错
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2 ... Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected single matching bean but found 2: userDaoImpl,userDaoImpl2 ...
大概意思就是说,主程序抛出了一个UnsatisfiedDependencyException
即不满足依赖异常,嵌套异常是NoUniqueBeanDefinitionException
即Bean定义不唯一异常,预期匹配单个 Bean 但是找到了两个 Bean
此时想要指定装配某一个实现类,就需要用到@Qualifier
注解
4.2 @Qualifier
书接上回,如果我们想要从多个实现类中装配具体某一个实现类,可以这么写
@Autowired @Qualifier(value = "userDaoImpl") private UserDao userDao;
其中
value
值为具体的实现类上配置的注解中value
值@Repository public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("UserDaoImpl add..."); } } @Repository public class UserDaoImpl2 implements UserDao { @Override public void add() { System.out.println("UserDaoImpl2 add..."); } }
由于上述例子中,我们没有对@Repository
配置相应的value
,所以默认为首字母小写的类名
如果想使用 UserDaoImpl2 类,则
@Autowired @Qualifier(value = "userDaoImpl2") private UserDao userDao;
如果指定名称有误,即不存在名称为
value
对应的类,则会报NoSuchBeanDefinitionException
异常,即找不到对应类Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDaoImpl1)} ... Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.zking.spring.s11_annotation.dao.UserDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=userDaoImpl1)}
4.3 @Resource
根据类型注入
@Resource private UserDao userDao;
根据名称注入
@Resource(name = "userDaoImpl") private UserDao userDao;
需要注意的是@Resource
注解所在包为javax.annotation
即 Java 扩展包,所以 Spring 官方不建议使用该注解而推崇@Autowired
和@Qualifier
注解
4.4 @Value
上述注解都是对对象类型的属性进行注入,如果想要装配普通类型属性,如基本数据类型及其包装类等,则可以需要使用
@Value
注解;@Value(value = "${user.name}") private String name; @Value(value = "100") private Integer age; @Value(value = "200.0d") private Double length; @Value(value = "true") private boolean isOk; @Value(value = "0,a,3,6,test") private String[] arrs;
@Value(value = "${user.name}"):需要加载外部属性资源文件
4.5 完全注解开发
创建配置类,替代 XML 配置文件
//标注当前类为配置类 @Configuration //开启组件扫描 @ComponentScan({"com.zking.spring"}) //引入外部属性文件 @PropertySource({"classpath:config.properties"}) public class SpringConfig { }