依赖注入
上篇博客已经提到了DI注入方式的构造器注入,下面采用set方式进行注入
基于set方法注入
public class User {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
<bean id="address" class="com.yuqu.pojo.Address">
<property name="addr" value="地址"/>
</bean>
<bean id="user" class="com.yuqu.pojo.User">
<!-- 1. 普通值注入 String类型 value -->
<property name="name" value="远丘"/>
<!-- 2. Bean注入 ref -->
<property name="address" ref="address"/>
<!-- 3. 数组注入 ref -->
<property name="books">
<array>
<value>Java核心编程思想</value>
<value>Java入门到实战</value>
<value>Java进阶编程</value>
</array>
</property>
<!-- 4. list集合注入 -->
<property name="hobbys">
<list>
<value>篮球</value>
<value>音乐</value>
<value>吉他</value>
</list>
</property>
<!-- 5. map集合注入 -->
<property name="card">
<map>
<entry key="我" value="你"/>
<entry key="他" value="她"/>
</map>
</property>
<!-- 6. Set集合注入 -->
<property name="games">
<set>
<value>set1</value>
<value>set2</value>
</set>
</property>
<!-- 7. null注入 -->
<property name="wife">
<null/>
</property>
<!-- 8. Properties -->
<property name="info">
<props>
<prop key="driver">mysql-....</prop>
<prop key="url">localhost://3306</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
最终可以成功执行,以上也是在正常开发中最经常使用的一些set注入语法格式。对构造器进行输出,可以发现采用Set注入方式时,程序最开始在映射bean的时候依然是走无参构造,创建对象之后使用各个属性的set方法为其赋值
public User() {
System.out.println("无参构造");
}
// 输出
无参构造
Spring扩展配置注入
p-namespace
p-namespace允许使用bean元素属性直接为对象参数赋值,不需要额外的< properties >子元素标签
p-namespace就对应于set方法注入
Spring官网给出:Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring
也就是说Spring支持基于XML配置文件中定义的具有命名空间的额外可扩展配置。就是需要用带有namespace的配置到xml头文件中。同时P-namespace在不定义的情况下,仅存在于Spring的核心中(就是不配不能用)
配置头文件p-namespace:xmlns:p="http://www.springframework.org/schema/p"
映射Bean时使用:
<bean id="person" class="com.yuqu.pojo.Person" p:name="人类" p:tel="13523025288"/>
<!-- 不使用p-namespace -->
<bean id="person" class="com.yuqu.pojo.Person">
<property name="name" value="人类"/>
<property name="tel" value="15632808079"/>
</bean>
c-namespace
c:constructor所以c-namespace对应于构造器注入
c-namespace扩展配置头文件:xmlns:c="http://www.springframework.org/schema/c"
<bean id="person" class="com.yuqu.pojo.Person" c:name="Mountain_think" c:tel="13434544324124"/>
也可以使用c_0或者c_1,数字代表构造器参数列表中参数的索引
使用c-namespace一定要添加含参构造,spring会自动检测class所对应的类中是否有含参构造,然后根据构造器的参数给出可以配置的属性名
Bean Scopes
Bean的作用域,是指将编写好的实体类映射到applicationContext.xml文件中之后的作用范围
查询官方文档可以了解到,在映射bean的过程中不仅可以定义各种依赖项和配置值,还可以自行控制每个Bean的作用区域,不用在Java当中设置。并且Spring支持六大作用域,但其中四个只有在WebMVC中才可以配置。除此之外还可以自行定义作用域
singleton
单例:(默认)将单个bean定义范围限定为每个SpringIoC容器的单个对象实例。
ApplicationContext context = new ClassPathXmlApplicationContext("application-Context.xml");
Person person = context.getBean("person", Person.class);
Person person1 = context.getBean("person",Person.class);
System.out.println(person==person1);
// 最终输出 true
可以发现在不进行显式设置的情况下,所映射好的bean在springIOC容器中都是单一实例
也可以显式定义:scope="singleton"
prototype
原型: 将单个bean定义范围限定为任意数量的对象实例。
也就是你每次调用getBean方法从IOC容器中获取到的实例都是一个新对象
<bean id="person" class="com.yuqu.pojo.Person"
c:_0="Mountain_think" c:_1="13434544324124"
<!-- 除了单例之外的其他作用域都需要显式定义 -->
scope="prototype"
/>
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application-Context.xml");
Person person = context.getBean("person", Person.class);
Person person1 = context.getBean("person",Person.class);
System.out.println(person==person1);
}
// 最终打印: false
其他都是智能在Web开发中使用的Only valid in the context of a web-aware Spring ApplicationContext.
比如:
request请求作用域:只在一次请求中有效
Session会话作用域:只在一次会话中有效
application全局作用域:在整个servlet中始终有效
自动装配Bean
先前提到的依赖注入都是采用在xml文件中由我们显式的去装备,但是Spring还提供了自动装配的机制
Spring会自动的在上下文中寻找,并自动装配Bean
ByName方式
ByName使用定义
<bean id="mom" class="com.mount.pojo.Mother"/>
<bean id="dady" class="com.mount.pojo.Father"/>
<bean id="person" class="com.mount.pojo.Person" autowire="byName">
<property name="name" value="远丘"/>
</bean>
ByName的自动装配的原理就是:根据Bean中的set方法后缀来对应xml配置文件中的id名,比如setAge那么就会寻找id为age的bean
如上述代码所示,我们不需要在PersonBean中再去配置Father和Mother属性,只需要将这两个实体类映射到Bean中,Spring会帮助我们完成装配事情
使用ByName需要保证Bean的id全局唯一,并且id名要与set方法后缀字段一致(也就是与所属类中字段属性名一致)
ByType方式
ByType使用的定义格式就是将autowire="byName"
换为autowire="byType"
即可
<bean class="com.mount.pojo.Mother"/>
<bean class="com.mount.pojo.Father"/>
<bean id="person" class="com.mount.pojo.Person" autowire="byType" >
<property name="name" value="远丘"/>
</bean>
根据byType自动装配原理就是spring会在上下文中寻找对应的对象类型,也就是说此时即便不为其他的实体类配置id,Spring也可以找到。
但是需要保证每一个实体类的全局唯一性,否则编译报错
注解自动装配Bean
使用前的配置
头文件信息配置,使用注解需要配置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 https://www.springframework.org/schema/beans/spring-beans.xsd
<!-- 配置2 -->
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置3:开启注解的使用权限 -->
<context:annotation-config/>
</beans>
@Autowired
可以在实体类中的属性前添注解@Autowired,那么只要这个属性对应的实体类在applicationContext中注册完毕,spring在装配Bean时就会自动的将添加了@Autowired注解的属性映射到具体的Bean中
public class Person {
private String name;
@Autowired
private Father dady;
@Autowired
private Mother mom;
@Nullable 某个字段标记了该注解则表明该字段可以为null,当然也可以设置@Autowired实现该功能,打开Autowired的源码:
public @interface Autowired {
boolean required() default true;
}
可以发现Autowired有一个参数required,默认为true表示该属性不能为null,也可以修改为false表示该属性可以为null
@Autowired(required = false)
那么通常我们在实体类中所定义的属性名需要与xml配置文件中bean的id一致,但是即便不一致。仍然可以通过注解来解决这个问题
@Qualifier提供了value参数,如果出现了bean的id与实体类属性名不一致的问题,我们可以通过@Qualifier注解将bean中的id映射到指定的实体类字段上
@Qualifier和@Autowired经常配套使用
就像这样:
@Autowired
@Qualifier(value = "momom")
private Mother mom;
<bean id="momom" class="com.mount.pojo.Mother"/>
当然我们也可以忽略id,只保留class属性也可以运行
<bean class="com.mount.pojo.Mother"/>
<bean class="com.mount.pojo.Father"/>
@Resource
@Resource注解是Java的原生注解,就功能来讲比Spring所内置的注解更加强大。它集合了Spring的@Autowired和@Qualifier两个注解的特性。
不仅可以利用指定名称映射到bean的id,也可以通过类型映射。
public class Person {
private String name;
@Resource
private Father dady;
@Resource(name = "mommm")
private Mother mom;
也可以配置@Resource要映射的实体类type
@Resource(type = Mother.class)
xml:
<bean id="mommm" class="com.mount.pojo.Mother"/>
<bean class="com.mount.pojo.Father"/>
两者都是用来自动装配bean使用的
@Autowired默认采用ByType方式实现,如果ByType找不到或者存在多个同类型bean就会根据属性名与Bean的id进行查找,当bean的id与属性名不一致时就可以添加注解@Qualifier进行映射字段名
而@Resource默认采用ByName方式,不指定name属性时,就会根据字段名去扫描bean中的id,找不到就会通过ByType方式扫描,再找不到就报错
此外我们可以直接再@Resource注解中配置name字段名属性和type类型名称
利用注解开发
我们不在需要将每个需要使用的类都利用xml配置注册到spring容器当中,只需要给特定层级的类添加对应注解即可。并且要开启spring注解的支持以及对包的扫描,扫描到了注解才能将该类成功注册为spring容器中的bean
<!-- 扫描包 该包下的注解才会生效 -->
<context:component-scan base-package="com.mount.pojo"/>
<!-- 注解支持 -->
<context:annotation-config/>
针对实体类POJO层,使用@Component注解将该类注册为bean
DAO层使用@Repository注解
Service层使用@service注解
Controller层使用@Controller注解
上述三个注解的作用一致,均为将其所修饰的类注册到Spring容器中,由spring同一管理bean
@Scope作用域:@Scope(scopeName = "singleton")
@Value:表示为当前所修饰的属性赋值。较简单的字段可以采取该方式,复杂一些的仍然要到xml文件中去配置
示例:
// pojo层
@Component
public class User {
// dao层
@Repository
public class UserDaoImpl implements UserDao{
// service层
@Service
public class UserService {
// controller层
@Controller
@Scope(scopeName = "singleton")
public class UserController {
// value使用
@Value("远丘")
private String name;
@Value("远丘")
public void setName(String name){this.name = name;}
但是即便如此,我们的spring注入配置还没有完全脱离配置文件
使用Java配置Spring
核心:Java配置类、不再需要applicationContext.xml配置文件
不再使用ClassPathXmlApplicationContext获取Spring的上下文来获取容器
更换为使用AnnotationConfigApplicationContext获取Spring容器
先来看配置类:
@Configuration // 配置类 也会被spring容器托管
@ComponentScan("com.mountain.pojo")// 扫描包
public class MountConfig {
// 注册一个bean 等同于xml中的一个bean标签
// bean的id等同于方法名 bean的class属性等同于该方法的返回值
@Bean
public User getUser() {
return new User();
}
@Bean
public Bitch getBitch(){
return new Bitch();
}
}
@Configuration:表示该类为配置类,并且其本身也被@Component标注,也就是说该类同样会被Spring容器管理
@ComponentScan等同于<context:component-scan base-package="com.mountain"/>
表示开启扫描,在该包下面所有的注解才能生效
@Bean等同于先前在xml文件中映射的某一个bean,该注解所修饰的方法名等于bean的id,返回值为bean的class属性
实体类:
@Component
public class User {
@Value("be better")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试类:
public void testDemo(){
ApplicationContext context = new AnnotationConfigApplicationContext(MountConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
Bitch bitch = context.getBean("getBitch", Bitch.class);
System.out.println(bitch.getAge());
}
如果使用了配置类去做,那么只能通过ApplicationContext上下文来获取容器,其参数为被标记了@Configuration的配置类,生成对象context可以用于调用在配置类中被标记为@Bean的方法获取对象
Spring注解速查
@Autowired:用在属性上,表示自动装配该属性
@Qualifier:与Autowired配套使用,用于映射bean的id名称到类的属性上
@Nullable:表示该值可以未null
@Resource:Java原生的自动装配注解 带有name、type属性
@Component【pojo层】:组件、放在类上,表示这个类注入到Spring容器中,由spring管理
@value:可以放在属性上和set方法上,表示为这个属性赋值
等价于<property name="XXX" value="XXX"/>
。
@Component的衍生注解:
- @Respository 【Dao层】
- @Services【service层】
- @Controller 【controller层】
@Scope:作用域
@Import:引入其他配置类
@Configuration:表明该类为配置类
@ComponentSan:扫描包,被扫描包下的注解可生效
@Bean:等同于bean标签的配置