Spring配置bean的管理
- 1、Spring配置
- 2、依赖注入
- 2.1、构造器注入
- 2.2、Set注入【重点】
- 2.3、注入拓展
- 2.4、bean的作用域
- 3、自动装配Bean
- 3.1、xml方式自动装配
- 3.2、使用注解
- 1、@Autowired
- 1.1、@Qualifier
- 1.2、required
- 2、@Resource
- 3、小结:
- 3.3、使用注解开发
- 4、使用JavaConfig实现配置类
1、Spring配置
别名
alias设置别名,为bean设置别名,可以设置多个别名
<!--设置别名:在获取Bean的时候可以使用别名获取-->
<alias name="userT" alias="userNew"/>
Bean的配置
<!--bean就是java对象,由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的管理-->
<!--beanFactory、applicationContext 接口-->
<bean id="orderService" class="com.k2.bean的管理.实现接口.service.impl.OrderServiceImpl"/>
<!--bean的配置-->
<!--使用id属性定义bean1、构造方法实例化-->
<bean id="bean1" class="com.k2.bean的管理.bean的配置.beans.Bean1"/>
<!--使用name属性定义bean2-->
<bean id="bean2" class="com.k2.bean的管理.bean的配置.beans.Bean2"/>
<!--
id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名
-->
<bean id="hello" name="hello2 h2,h3;h4" class="com.k2.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
<!-- 静态工厂实例化 就是直接可以通过静态方法来实例化一个对象 -->
<bean id="beanStatic" class="com.k2.bean的管理.bean的配置.static_factory.MyBeanStaticFactory" factory-method="createBean"></bean>
<!-- 实例工厂实例化 就是先创建类对象,如何通过对象来调用创建实例对象的方法 -->
<!-- 配置工厂 -->
<bean id="myBeanFacFactory" class="com.k2.bean的管理.bean的配置.fac_factory.MyBeanFacFactory"></bean>
<!-- 使用factory-bean属性指向配置的实例工厂, 相当于new MyBeanFacFactory,然后调用方法
使用factory-method属性确定使用工厂中哪个方法
先获取工厂对象(myBeanFacFactory),再获取工厂方法(createBean)
-->
<bean id="beanFac" factory-bean="myBeanFacFactory" factory-method="createBean"></bean>
<!--看似没有创建MyBeanFactory对象,实则实现接口,已经创建对象-->
<bean id="bean" class="com.k2.bean的管理.bean的配置.fac_factory.MyBeanFactory"/>
<!-- Bean生命周期 -->
<!-- scope="singleton" 这个值默认,在默认情况下spring能够全程跟踪bean的生命周期 -->
<bean id="life" class="com.k2.bean的管理.bean的生命周期.entity.Life" init-method="initMethod" destroy-method="destroyMethod"></bean>
</beans>
import
用于团队和合作通过import来实现
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
<import resource="{path}/beans.xml"/>
2、依赖注入
概念
- 依赖注入(Dependency Injection,DI)。
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
构造方法注入:
确定参数的:
name属性: 通过参数的名字来确定参数
type属性: 通过参数的类型来确定参数
index属性:通过参数的下标来确定参数
给属性赋值的:
value属性:用来给基本数据类型赋值
ref属性: 用来给引用数据类型赋值
属性setter方法注入:
name的值准确的讲并不是属性名,而是set方法去掉set关键字后的名字
2.1、构造器注入
2.2、Set注入【重点】
要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is
【环境搭建】
User实体类
public class User2 {
private int id;
private String userName;
private String passWord;
}
public class User3 {
/**
* 依赖注入 值的注入 有属性、有set方法才可以动态赋值
*/
private String name;
private List<String> list;
private Set<String> set;
private String[] array;
private Map map;
// 配置类
private Properties prop;
private User2 user2;
private String wife; // Null注入
public void setWife(String wife){
this.wife = wife;
}
public String getWife(){
return wife;
}
public void setUser2(User2 user2) {
this.user2 = user2;
}
public void setName(String name) {
this.name = name;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setArray(String[] array) {
this.array = array;
}
public void setMap(Map map) {
this.map = map;
}
public void setProp(Properties prop) {
this.prop = prop;
}
@Override
public String toString() {
return "User3{" +
"name='" + name + '\'' +
", list=" + list +
", set=" + set +
", array=" + Arrays.toString(array) +
", map=" + map +
", prop=" + prop +
", user2=" + user2 +
'}';
}
}
常量注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="name" value="卑微小焦"></property>
</bean>
Bean注入
<bean id="user2" class="com.k2.DI依赖注入.User2">
<property name="id" value="2"></property>
<property name="userName" value="李四"></property>
<property name="passWord" value="1234"></property>
</bean>
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="user2" ref="user2"></property>
</bean>
数组注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="array">
<array>
<value>array1</value>
<value>array2</value>
<value>array3</value>
</array>
</property>
</bean>
List注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
</bean>
Set注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
</bean>
Map注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="map">
<map>
<entry key="key1" value="value1"></entry>
<entry key="key2" value="value2"></entry>
<entry key="key3" value="value3"></entry>
</map>
</property>
</bean>
Properties注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="prop">
<props>
<prop key="username">小焦同学</prop>
<prop key="password">123</prop>
<prop key="email">jiao@126.com</prop>
</props>
</property>
</bean>
Null注入
<bean id="user3" class="com.k2.DI依赖注入.User3">
<property name="wife"><null/></property>
</bean>
2.3、注入拓展
p命名和c命名注入
<!--使用p(properties)命名空间引入,属性依然要设置set方法-->
导入约束:xmlns:p="http://www.springframework.org/schema/p"
<bean id="kuangUser" class="com.k2.DI依赖注入.KuangUser" p:name="小焦" p:age="18"/>
<!--使用c(Constructor)命名空间引入,属性依然要设置set方法
有参构造器加上,这里也能知道,c 就是所谓的构造器注入!
-->
导入约束:xmlns:c="http://www.springframework.org/schema/c"
<bean id="kuangUser2" class="com.k2.DI依赖注入.KuangUser" c:name="青椒" c:age="16"/>
2.4、bean的作用域
1、单例模式(Spring默认机制)
singleton:单例模式(默认)。Spring容器中只会存在一个共享的Bean实例,所有对Bean的请求,只要请求的id(name)与Bean的定义相匹配,会返回Bean的同一个实例,Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
<bean id="scope1" class="com.k2.bean的管理.bean的配置.scope.Scope" scope="singleton"/>
2、原型模式:每次从容器中get的时候,都会产生一个新对象
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="scope2" class="com.k2.bean的管理.bean的配置.scope.Scope" scope="prototype"/>
// prototype:原型模式,每次从容器中请求Bean时,都会产生一个新的实例
3、其余的request、session、application这些只能在web开发中使用
3、自动装配Bean
- 自动装配是使用spring满足bean依赖的一种方法
- spring会在应用上下文中为某个bean寻找其依赖的bean
Spring中bean三种装配机制:
- 在xml中显式配置
- 在java中显式配置
- 隐式的bean发现机制和自动装配
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
- 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
- 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
推荐不使用自动装配xml配置 , 而使用注解 .
3.1、xml方式自动装配
实体类
public class User1 {
private int id;
private String userName;
private String passWord;
public User1(int id, String userName, String passWord) {
this.id = id;
this.userName = userName;
this.passWord = passWord;
}
}
public class User2 {
private int id;
private User1 user1;
public void setId(int id) {
this.id = id;
}
public void setUser1(User1 user1) {
this.user1 = user1;
}
}
applicationContext.xml
ByName方式
<?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="user1" class="com.k2.bean的管理.bean的装配.自动装配.User1">
<constructor-arg name="userName" value="焦焦"></constructor-arg>
</bean>
<!--属性setter方法注入-->
<bean id="user2" class="com.k2.bean的管理.bean的装配.自动装配.User2" autowire="byName">
<property name="id" value="2"></property>
<!--<property name="user1" ref="user1"></property>-->
</bean>
</beans>
结论:
当一个bean节点带有 autowire byName的属性时。
- 将查找其类中所有的set方法名,例如setUser1,获得将set去掉并且首字母小写的字符串,即user1。
- 去spring容器中寻找是否有此字符串名称id的对象。
- 如果有,就取出注入;如果没有,就报空指针异常。
byType方式
使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。id="user1"可以省略不写
<?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="user1" class="com.k2.bean的管理.bean的装配.自动装配.User1">
<constructor-arg name="userName" value="焦焦"></constructor-arg>
</bean>
<!--属性setter方法注入-->
<bean id="user2" class="com.k2.bean的管理.bean的装配.自动装配.User2" autowire="byType">
<property name="id" value="2"></property>
<!--<property name="user1" ref="user1"></property>-->
</bean>
</beans>
结论:如果把id="user1"删掉不会报错,因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。
3.2、使用注解
jdk1.5开始支持注解,spring2.5开始全面支持注解
注解可以写在属性上,也可以写在setter方法上
在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
// 开启属性注解支持
<context:annotation-config/>
1、@Autowired
- @Autowired是按类型自动装配的,不支持id匹配。可以没有set方法
- 需要导入 spring-aop的包!
byName:根据<bean>元素id属性的值自动装配
byType:根据<bean>元素的数据类型(Type)自动装配,如果一个Bean的数据类型,兼容另一个Bean中的数据类型,则自动装配
实体类
public class Cat{
}
public class Dog{
}
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String str;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
applicationContext.xml
<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
1.1、@Qualifier
- @Autowired默认是根据类型自动装配的,加上@Qualifer则可以根据byName的方式自动装配
- @Qualifier不能单独使用
applicationContext.xml
<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
在属性上添加Qualifier注解
@Autowired
private Cat cat;
// @Autowired默认根据类型自动装配,如果有多个实现类,需要配合@Qualifier注解使用,根据bean id(byName)自动装配
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
1.2、required
@Autowired(required = false)说明:对象可以为null;可以不用配置bean
@Autowired(required = true)默认为true说明:对象必须存在,不能为null;配置bean
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
2、@Resource
- @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
- 其次再进行默认的byName方式进行装配;
- 如果以上都不成功,则按byType的方式自动装配。
- 都不成功,则报异常。
实体类
public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
applicationContext.xml
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
3、小结:
@Autowired 与 @Resource 不同
- @Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
- @Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用。
- @Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
3.3、使用注解开发
步骤:
- 导入aop的包
- 在配置文件中,引入context约束
<?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">
</beans>
- bean的实现
- 配置扫描包
<!--使用context命名空间,通知spring扫描指定包下所有的Bean,进行注解解析-->
<context:component-scan base-package="com.k2.bean的管理.bean的装配.annotation方式"/>
- 在指定包下编写类,增加注解
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
@Component("user")
// 作用域
@Scope("singleton")
public class User {
// 为属性赋值,不需要setter方法(属性注入)
@Value("1")
private int id;
@Value("小焦同学")
private String userName;
@Value("123")
private String passWord;
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", passWord='" + passWord + '\'' +
'}';
}
}
- 衍生注解
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Repository:dao层
- @Service:service层
- @Controller:web层
写上这些注解,就相当于将这个类交给Spring管理装配了!
@Repository("userDao1")
public class UserDaoImpl implements UserDao {
}
@Service("userService")
public class UserServiceImpl implements UserService {
/*
// @Resource:引用数据类型
@Resource(name = "userDao1")
private UserDao userDao;
*/
// 如果有两个类型实现类(UserDao接口有两个实现类),则需要使用@Qualifier("userDao")
@Autowired
@Qualifier("userDao1")
private UserDao userDao;
}
@Controller("userController")
public class UserController {
/*
@Resource(name = "userService")
private UserService userService;
*/
// userService接口只有一个实现类,所以不需要@Qualifier()
@Autowired
private UserService userService;
}
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("annotation.xml");
UserController userController = (UserController) ac.getBean("userController");
userController.save();
}
- 作用域
- singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂,所有的对象都会销毁
- prototype:多例模式。关闭工厂,所有的对象不会销毁,通过内部大垃圾回收机制回收
// 作用域
@Scope("singleton")
public class User {
}
小结:
XML与注解比较
- XML可以适用于任何场景,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
**推荐方案:**xml与注解整合开发
- xml管理bean
- 注解完成属性注入
- 使用过程中不用扫描包,扫描是为了类上的注解
<!--使用context命名空间,通知spring扫描指定包下所有的Bean,进行注解解析-->
<context:component-scan base-package="com.k2.包"/>
<!--使用context命名空间,在配置文件中开启相应的注解处理器-->
<context:annotation-config/>
作用:
- 进行注解驱动注册,从而使注解生效
- 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显式的向Spring注册
- 如果不扫描包,就需要手动配置bean
- 如果不加注解驱动,则注入的值为null!
4、使用JavaConfig实现配置类
springboot中常有
// 实体类中也就不需要再配置@Component注解,
public class User {
@Value("张三3")
private String name;
@Value("18")
private int age;
@Autowired
private Pet pet;
}
/**
* 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、proxyBeanMethods:代理bean的方法
* Full(proxyBeanMethods = true):全配置【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
* Lite(proxyBeanMethods = false):轻量级配置【每个@Bean方法被调用多少次返回的组件都是新创建的】
* 组件依赖必须使用Full模式。其他默认是否Lite模式
* 4、@Import({User.class, Filter.class})
* 给容器中自动创建出这两个类型的组件,默认组件的名字就是全类名
* @Import():com.k2.springboothelloworld.pojo.User
* @Bean:user
*/
@Import({User.class})
@Configuration(proxyBeanMethods = true) // 告诉SpringBoot这是一个配置类 == 配置文件xml
public class MyConfig {
@Bean // 给容器中添加组件,以方法名作为组件的id(bean中的id),返回类型就是组件类型(bean中的class)。
public User user() {
User user = new User();
return user; // 就是返回要注入bean的对象 返回的值,就是组件在容器中的实例
}
}