目录
1. 依赖注入的三种方式
1.2 构造器注入
1.3 使用setter注入
1.4 接口注入
2. bean的装配
2.1 简易值装配
2.2 集合装配
2.3 命名空间装配
2.4 通过注解装配
1. 依赖注入的三种方式
- 构造器注入
- setter注入
- 接口注入
1.2 构造器注入
在Spring中创建bean的时候必须提供一个无参构造器,否则Spring将抛出异常,程序报错,为什么呢?因为Spring容器在创建bean的时候会先创建一个空壳,即Bean里面的元素全为null的对象,然后会在后面再将配置的属性进行填充,这样可以防止循环注入,Spring的构造器注入方式如下配置,即可使用
<bean id="person" class="com.xxx.Person" ></bean>
但是如果使用有参数构造器,可以不用提供无参构造器,就要在配置文件里进行配置以构造器模式创建对象,但是不推荐这种方式。因为可读性差,如果属性多了,还得注意构造器的index值,容易出错
<bean id="person" class="com.xxx.blog.Person">
<constructor-arg index="0" value="516" />
<constructor-arg index="1" value="20" />
</bean>
1.3 使用setter注入
使用setter注入当然就得提供setter方法了,然后在配置文件里使用<property>标签配置,Spring容器会通过反射调用setter方法填充属性值,setter方式必须提供一个无参构造器,否则Spring将无法创建对象
<bean id="person" class="com.xxx.blog.Person">
<property name="name" value="516"/>
<property name="age" value="18"/>
</bean>
1.4 接口注入
暂时留着 2019.01.08 21:16
2. bean的装配
2.1 简易值装配
像八种数据类型和他的包装类以及String可以直接在value里面写入值
<bean id="person" class="com.xxx.blog.Person">
<property name="name" value="516"/>
<property name="age" value="18"/>
<!-- 引用类型装配 -->
<property name="book" ref="math"/>
</bean>
<bean id="math" class="com.xxx.blog.Math"></bean>
2.2 集合装配
2.2.1 Map类型
其中name就是属性名称了,然后往里面存入一些值
<property name="studentScore">
<map>
<entry key="stu2" value="60"></entry>
<entry key="stu3" value="50"></entry>
<entry key="stu1" value="65"></entry>
<entry key="stu1" value="65"></entry>
<!--引用类型装配 -->
<entry key-ref="stu4" value-ref="class4"></entry>
</map>
</property>
<bean id="stu4" class="com.xxx.blog.Stu4"></bean>
<bean id="class4" class="com.xxx.blog.Class4"></bean>
2.2.2 Set类型
<property name="NamesSet">
<set>
<value>Tom</value>
<value>G.E.M</value>
<value>Lisa</value>
<!--引用类型装配 -->
<ref bean="role1" />
</set>
</property>
<bean id="role1" class="com.xxx.blog.Role1"></bean>
2.2.3 List类型
<property name="girlList">
<list>
<value>吴宣仪</value>
<value>邓紫棋</value>
<value>宋茜</value>
<!--引用类型装配 -->
<ref bean="gir4" />
</list>
</property>
<bean id="gir4" class="com.xxx.blog.Gir4"></bean>
2.2.4数组类型
<property name="array">
<array>
<value>5</value>
<value>1</value>
<value>6</value>
<!--引用类型装配 -->
<ref bean="num" />
</array>
</property>
<bean id="num" class="com.xxx.blog.Num"></bean>
2.3 命名空间装配
命名空间装配需要引入Spring的命名空间
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
c 代表是Constructor(构造器)的意思,所以示例使用构造器方式
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
<bean id="person" class="com.xxx.blog.Person" c:name="517" c:age="18"></bean>
p就是property 对应着上面<property>标签的意思,使用setter方式注入值,所以必须提供setter方法和无参构造器
<bean id="person" class="com.xxx.blog.Person" p:name="520" p:age="18"></bean>
private String name;
private Integer age;
/**setter/无参构造器 **/
2.4 通过注解装配
使用注解可以减少xml的繁琐配置,但并不意味着可以代替xml方式,所以要根据业务逻辑选择合适的方式
2.4.1 @Component
在类上加入@Component注解可以使当前类被Spring容器管理,成为一个bean,@Component注解里面的value属性代表当前bean的id名称, 如果不写默认就为当前类名小写(person)
package com.xxx.blog;
@Component(value="per")
public class Person{
@Value("516")
private String name;
@Value("11")
private Integer age;
/**setter/getter **/
}
当前只是定义了一个bean,但要是想要被Spring管理还得让Spring知道去哪里找这些bean,@ComponentScan对应着Xml配置文件里面的 <context:component-scan base-package="com.xxx.blog" /> 意思都是通知Spring去哪里扫描bean,而@ComponentScan注解如果不写范围,默认值就是当前类所在的包和子包
package com.xxx.blog;
@ComponentScan
public class Config {
}
----------------------------------------------
package com.xxx.study;
@ComponentScan(basePackages= {"com.xxx.blog","com.xxx.test"})
public class Config {
}
这时候测试就需要用AnnotationConfigApplicationContext类去初始化SpringIoc容器
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Object bean = context.getBean("person");
System.out.println(bean);
2.4.2 @Autowired
@Autowired可以根据类型自动装配,@Value只能注入基本数据类型和他的包装类,但@Autowired就可以注入另一个Bean
@Component
public class HomeWork {
@Value("English")
private String name;
@Value("90")
private String score;
}
@Component
public class Student {
@Autowired
private HomeWork homeWork;
}
首先就是注入一个普通HomeWorkBean,然后在管理Student时发现@Autowired HomeWork ,这时候就会去IOC容器寻找是否有HomeWork.class的bean,如果找到就注入,否则抛出异常,如果不想抛出异常就这样配置@Autowired(required=false),意思就是这个bean找不到也没有关系,不是必须的,
2.4.3 @Primary
在使用@Autowired的时候也可以使用接口类型注入,如下
此时有一个JavaTeacher实现了Teacher接口,最终IOC容器会将实现类JavaTeacher注入到teacher字段
@Component
public class JavaTeacher implements Teacher{
@Override
public String toString() {
return "JavaTeacher";
}
}
Spring推荐面向接口开发,所以难免会有一个接口有多个实现类,那么此时再这样子写就会报错,因为Spring不知道该注入哪一个,所以有两个注解@Primary,@Qualifier帮助我们解决这种情况,当存在多个实现类的时候我们可以使用@Primary来指定一个bean为优先注入的
@Component
public class JavaTeacher implements Teacher{
@Override
public String toString() {
return "JavaTeacher";
}
}
-----------------------------------------------
@Primary
@Component
public class KotlinTeacher implements Teacher{
@Override
public String toString() {
return "KotlinTeacher";
}
}
即以上两个bean都实现了teacher接口,则如果出现冲突,则会使用@Primary修饰的bean注入
2.4.4 @Qualifier
@Qualifier也是解决多个实现类冲突的,其value值就是bean的id,所以就是根据id来注入,@Component修饰的bean默认id是类名小写,然后结果最终是注入了JavaTeacher
@Autowired
@Qualifier("javaTeacher")
private Teacher teacher;
2.4.5 @Bean
@Component注解只能在类上使用,而如果我们想用注解把第三方的类成为一个Bean,就需要@Bean注解了
在使用@Bean注解的时候,一定要加一个@Conifguration 否则Spring无法扫描到当前类,@Configuration代表当前类为一个配置类,@Bean修饰的方法最终的返回值就是一个Bean,然后被Spring容器管理,value属性存储的是当前bean的id
@Configuration
public class TestBean {
@Bean(value="dataSource")
public DataSource getDataSource() {
DataSource dataSource = new DataSource();
dataSource.setDriverName("com.jdbc.mysql.Driver");
dataSource.setUrl("jdbc:mysql:///spring");
dataSource.setUsername("root");
dataSource.setPassword("sx516");
return dataSource;
}
}
@Bean还有其他属性 比如自定义初始化方法和销毁方法 ,在原Bean里添加与配置的方法名一样的方法后就会被Spring容器调用
@Bean(value="dataSource",initMethod="init",destroyMethod="destroy")
public void init() {
System.out.println("啊 我活了");
}
public void destroy() {
System.out.println("啊 我死了");
}
2.4.5 @Conditional
根据条件判断是否生成bean
首先新建一个类并实现Condition接口,重写的方法的返回值决定着是否创建bean,context可以获取到Spring运行时环境Environment,然后判断是否包含属性“name”,包含就创建这个bean
import org.springframework.context.annotation.Condition;
public class ConditionalTest implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
return environment.containsProperty("name");
}
}
在getBean方法上有一个@Conditional注解里面的参数就是对应的条件类,如果返回true就创建这个bean
@Configuration
@ComponentScan
@PropertySource(value= {"classpath:student.properties"})
public class Config {
@Value("${name}")
private String name;
@Value("${age}")
private String age;
@Value("${level}")
private String level;
@Bean(name="my")
@Conditional({ConditionalTest.class})
public My getBean() {
return new My(name,age,level);
}
}