继续引用spring中的java基础(注解)的例子代码person类和air接口。
Spring通过<bean>配置来实例化、设置Bean的属性以及设置bean的相互依赖,有id和class的属性,class相当于我们new来创建新实例时所需要导入的类,而id相当于创建new时的对象名。class是必须的,id是非必须的,但在配置文件中配置其他bean时引用该bean时,该bean也必须要有定义id。
<bean id="CleanAir" class="test.model.CleanAir">
使用xml配置的时候,定义bean和实现bean是分离的,而用注解定义bean时,只要在bean实现类上标注注解就能实现配置bean。
@Component
public class CleanAir implements IAir {
@Override
public String toString(){
return "CleanAir";
}
}
使用@Component注解在CleanAir类声明处对类进行标注,它可以被Spring容器识别,自动将POJO类转换为容器管理的bean。他和1、中的xml配置是等效的。定义Bean的注解还提供了其他3个功能与@Component等效的注解
@Repository:用于对DAO实现类进行标注。
@Service:用于对Service实现类进行标注。
@Controller:用于对Controller实现类进行标注。
3、基于Java类提供Bean定义
在普通的pojo类中标注@Configuration注解,相当于一个xml文件配置,每个@Bean注解,相当于一个<bean>。
@Configuration
public class AppConf {
@Bean
public CleanAir cleanAir(){
return new CleanAir();
}
@Bean
public DirtyAir dirtyAir(){
return new DirtyAir();
}
@Bean
public Person person(){
return new Person(dirtyAir());
}
}
public static void main(String [] args){
ApplicationContext ac =
new AnnotationConfigApplicationContext(AppConf.class);
Person person1 = ac.getBean(Person.class);
person1.Breath();
}
Bean的注入
Bean的注入方式有两种:一种是在XML中配置,另一种是使用注解的方法注入
1、XML方式注入
1)属性注入
对类中的字段进行注入,在传统的对象实例化可以使用new,再进行set方法来设置字段的值,在Spring中,可以使用property来进行属性注入,需要有字段对应的set方法。
新建XMLInstance类
public class XMLInstance {
private String name;
public void setName(String name ){
this.name = name;
}
private IAir iAir;
public void setAir(IAir iAir){
this.iAir = iAir;
}
public void Breath(){
System.out.println("Name:"+this.name+"\nAir:"+this.iAir.toString());
}
}
在XMLInstance中已设置了set方法,可以使用XMLproperty来配置属性,name表示属性名,value用来设置基本类型的属性值,ref标签配置非基本类型的属性值。XML配置如下。
<bean id="CleanAir" class="test.model.CleanAir">
<qualifier value="cleanair"/>
</bean>
<bean id="xmlinstance" class="test.model.XMLInstance">
<property name="name" value="abc"/>
<property name="air" ref="CleanAir"/>
</bean>
运行输出
Name:abc
Air:CleanAir
2)构造函数注入
在属性注入时先使用默认的无参构造函数,再使用set,在使用有参构造函数时进行的属性注入,就是构造函数注入,在1)中增加一个构造函数,在XMLInstance类中增加如下代码
public XMLInstance(String name,IAir iAir){
super();
this.name = name;
this.iAir = iAir;
}
在XML中使用construect-arg来设置构造函数的参数,index代表参数的顺序位置,value设置构造参数的基本类型,ref引用非基本类型的bean的id。XML配置如下。
<bean id="xmlcontructinstance" class="test.model.XMLInstance">
<constructor-arg index="0" value="def"/>
<constructor-arg index="1" ref="CleanAir"/>
</bean>
运行输出
Name:def
Air:CleanAir
3)工厂方法注入
工厂方法注入参考的工厂设计模式,通过工厂类来实现对象的实例化,工厂方法有静态与非静态方法。新建一个工厂类。
public class XMLFactory {
public XMLInstance CreateInstance(){
return new XMLInstance("instance",new CleanAir());
}
public static XMLInstance CreateStaticInstance(){
return new XMLInstance("static instance",new CleanAir());
}
}
可见工厂类中有静态方法和非静态方法,静态时不需要实例化工厂类直接调用静态工厂方法,非静态需要实例化工厂类再调用工厂方法。XML配置如下。
<!--静态工厂配置-->
<bean id="xmlstaticfactoryinstance" class="test.model.XMLFactory"
factory-method="CreateStaticInstance"/>
<!--非静态工厂配置-->
<bean id="xmlfactory" class="test.model.XMLFactory" />
<bean id="xmlfactoryinstance" factory-bean="xmlfactory"
factory-method="CreateInstance"/>
运行结果
Name:static instance
Air:CleanAir
Name:instance
Air:CleanAir
4)常见数据类型注入
已有类型注入
常见数据类型有List、Set、Map、Properties。 新建XmlCollectionsDemo类,为这四种类型进行数据注入。
public class XmlCollectionsDemo {
private List<Object> list;
private Properties pros;
private Set<Object> sets;
private Map<String,Object> maps;
public List<Object> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
public Properties getPros() {
return pros;
}
public void setPros(Properties pros) {
this.pros = pros;
}
public Set<Object> getSets() {
return sets;
}
public void setSets(Set<Object> sets) {
this.sets = sets;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public static void main(String[] args){
ApplicationContext ac = new ClassPathXmlApplicationContext("META-INF/model.xml");
BeanFactory factory = ac;
XmlCollectionsDemo xmlCollectionsDemo
= (XmlCollectionsDemo)factory.getBean("xmlCollectionsDemo");
System.out.println(xmlCollectionsDemo.getList());
System.out.println(xmlCollectionsDemo.getPros());
System.out.println(xmlCollectionsDemo.getSets());
System.out.println(xmlCollectionsDemo.getMaps());
System.out.println(xmlCollectionsDemo.getDate());
}
}
XML配置
<bean id="xmlCollectionsDemo" class="test.model.XmlCollectionsDemo">
<!-- list类型注入-->
<property name="list">
<list>
<value>1</value>
<ref bean="CleanAir"/>
<bean class="test.model.CleanAir"/>
</list>
</property>
<!-- set类型注入-->
<property name="sets">
<set>
<value>1</value>
<ref bean="CleanAir"/>
<bean class="test.model.CleanAir"/>
</set>
</property>
<!-- pros类型注入-->
<property name="pros">
<props>
<prop key="prokey1">prokeyA</prop>
<prop key="prokey2">prokeyB</prop>
</props>
</property>
<!-- maps类型注入-->
<property name="maps">
<map>
<entry key="key1" value="1"></entry>
<entry key="key2" value-ref="CleanAir"></entry>
<entry key="key3" >
<bean class="test.model.CleanAir"/>
</entry>
</map>
</property>
运行结果
[1, CleanAir, CleanAir]
{prokey2=prokeyB, prokey1=prokeyA}
[1, CleanAir, CleanAir]
{key1=1, key2=CleanAir, key3=CleanAir}
自定义属性编辑器
有一些属性是没法注入的,比如说日子。但可以通过PropertyEditorySupport,重写setAsText方法来实现注入。新建CustomerProperty类
public class CustomerProperty extends PropertyEditorSupport {
private String format = "yyyy-MM-dd";
public String getFormat(){
return format;
}
public void setFormat(String format){
this.format = format;
}
@Override
public void setAsText(String text) throws IllegalArgumentException{
SimpleDateFormat sdf = new SimpleDateFormat(format);
try{
this.setValue(sdf.parse(text));
}catch (Exception e){
e.printStackTrace();
}
}
}
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value="test.model.CustomerProperty"/>
</map>
</property>
</bean>
该类中将日期以String的形式传入,再重写了setAsText方法,setAsText方法会自动执行转换成日期类重新赋值给传入的value,在XML配置中,相当于import,将Date类引用到customEditors中。将4)中XmlCollectionsDemo添加字段Date,并在xml中增加
<property name="date" value="2019-8-12"/>
运行结果
[1, CleanAir, CleanAir]
{prokey2=prokeyB, prokey1=prokeyA}
[1, CleanAir, CleanAir]
{key1=1, key2=CleanAir, key3=CleanAir}
Mon Aug 12 00:00:00 CST 2019
5)初始化函数、销毁函数
在某些对象实例化完毕后还需要执行某些初始化代码,但由于某些原因并不能写在构造函数中,这个时候可以使将初始化代码写到另一个方法中,将init-method属性设置为该方法,有的对象使用完毕之后需要释放操作,可以用destroy-method设置销毁方法
在XMLInstance类中增加初始化方法和销毁方法
public void DestroyMethod(){
System.out.println("DestroyMethod");
}
public void InitMethod(){
System.out.println("InitMethod");
}
并在XML中注入初始化方法和销毁方法,修改xml
<bean id="xmlinstance" class="test.model.XMLInstance"
destroy-method="DestroyMethod" init-method="InitMethod">
<property name="name" value="abc"/>
<property name="air" ref="CleanAir"/>
</bean>
这样在创建实例后会输出InitMethod,在执行销毁之后会输出DestroyMethod。
2、注解方式注入
1)常用注解
- @Autowired:默认按类型匹配注入bean,可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。在使用该注解时,会在容器中查询对应类型的bean。如果查询结果刚好为一个,就将该bean装配给他指定的数据:如果查询结果不止一个,会按照名称来查找,如果查询结果为空,会抛出异常。
- @Required:适用于bean属性的setter方法,并表示受影响的bean属性必须在XML配置文件在配置时进行填充:否则会抛出一个异常。
- @Qualifier:@Autowird为单实例的,但通常在面向接口编程中,一个接口会有可能有多个实现,这个时候@Qualifier可选择用哪个实现进行注入。
- @Value:等效于property中的value设置。
- @Resource:默认按名称匹配注入bean。要求提供一个bean名称,如果没有提供bean名称,会自动采用标注处的变量名或方法名作为bean的名称,如果没有指定bean的名字,同时Spring容器中又没有该名字的bean.这时@Resource会退回为@Autowired按类型匹配注入。
新建一个AnnontationInstance类
@Component
public class AnnontationInstance {
@Value("789")
private String name;
public void setName(String name){
this.name = name;
}
@Autowired
@Qualifier(value = "cleanair")
private IAir air;
// @Resource(name = "CleanAir")
public void setAir(IAir air){
this.air = air;
}
public void Breath(){
System.out.println("Name:"+this.name+"\nAir:"+this.air.toString());
}
}
注:@Autowired和@Qualifier是可以与@Resource相互代替使用的,但是在@Resource中使用的是name是bean的id,而@Qualifier中的valu使用的是bean的qualifier。
回顾xml
<bean id="CleanAir" class="test.model.CleanAir">
<qualifier value="dirtyair"></qualifier>
</bean>
<bean id="DirtyAir" class="test.model.DirtyAir">
<qualifier value="cleanair"></qualifier>
</bean>
在配置完注解之后,还要告诉Spring开启注解,这样自动装配的注解才会生效,可以使用component-scan或者annotation-config来启用注解
<context:component-scan base-package="test.model"/>
<context:annotation-config/>