spring——(6)基于注解的配置

我们前面在使用Spring的时候每个类都要在xml文件中配置,在java5中新增了注解,于是Spring在2.5的版本中加入了注解,使用注解我们可以直接在java代码中配置Spring,而不需要在xml中配置。
首先看一下Spring中常见的注解有哪些:

  • @Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
  • @Scope注解 作用域
  • @Lazy(true) 表示延迟初始化
  • @Service用于标注业务层组件、
  • @Controller用于标注控制层组件(如struts中的action)
  • @Repository用于标注数据访问组件,即DAO组件。
  • @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
  • @Scope用于指定scope作用域的(用在类上)
  • @PostConstruct用于指定初始化方法(用在方法上)
  • @PreDestory用于指定销毁方法(用在方法上)
  • @DependsOn:定义Bean初始化及销毁时的顺序
  • @Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
  • @Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合 @Qualifier注解一起使用。如下:
    @Autowired @Qualifier(“personDaoBean”) 存在多个实例配合使用
  • @Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
  • @PostConstruct 初始化注解
  • @PreDestroy 摧毁注解 默认 单例 启动就加载
  • @Async异步方法调用

在使用注解之前要开启自动扫描功能,其中base-package为需要扫描的包(含子包):

<?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" >

        <context:component-scan base-package="cn.test"/> 

 </beans>

默认情况下,类会被自动发现并注册bean的条件是使用@Service、@Controller、@Repository、@Component注解。
< context:component-scan /> 还允许定义过滤器将基包下的某些类纳入或排除。

        <context:component-scan base-package="cn.test">
          <context:exclude-filter type="" expression="" />    
          <context:include-filter type="" expression=""/>
        </context:component-scan> 

其中type是过滤的方式,有根据包名过滤,正则表达式过滤等5种过滤方式,具体的暂时就不讲了;expression是具体过滤规则;< context:include-filter /> 是过滤符合条件对象,< context:exclude-filter />是过滤符合条件之外的对象。

定义Bean

我们定义bean的时候会使用@Component(不推荐使用)、@Repository、@Service、@Controller 这四个注解。(@Component是元注解,其他的三个都是继承这个注解,并且在他的基础之上添加了其他的东西)@Component是所有受Spring管理组件的通用形式,Spring还提供了更加细化的注解形式:@Repository、@Service、@Controller,它们分别对应存储层Bean,业务层Bean,和展示层Bean。

@Service
public class TestBean {

}

上面的代码定义了一个bean,他的id是testBean,默认的命名规则就是首字母小写,我们也可以直接指定他的Id:

@Service("test")
public class TestBean {

}

当然我们也可以定义命名规则,比如所有的字母大写等等,具体做法就是写一个类实现BeanNameGenerator接口,在这个类中定义好规则之后配置到< context:component-scan /> 中,

<context:component-scan base-package="cn.test" name-generator="" />

作用域(Scope)

这个的作用域和之前的一样,只是配置方式不同,在bean上加一个注解就可以实现:

@Scope("singleton")
@Service
public class TestBean{

}

我们也可以自定义注解,定义方式和定义命名规则一样,只不过实现的是ScopeMetadataResolver接口,最后配置到< context:component-scan /> 中,

<context:component-scan base-package="cn.test" scope-resolver="" />

Required

@Required 注释应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。

public class Student {
   private Integer age;

   @Required
   public void setAge(Integer age) {
      this.age = age;
   }
   public Integer getAge() {
      return age;
   }
}

在上面的bean中,age这个属性必须在配置时就赋值。

Autowired

  • Setter 方法中的 @Autowired:当 Spring遇到一个在 setter 方法中使用的 @Autowired 注释,它会在方法中视图执行 byType 自动连接。
  • 属性中的 @Autowired:你可以在属性中使用 @Autowired 注释来除去 setter 方法。当使用为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。
  • 构造函数中的 @Autowired:和Setter 方法中的一样。

默认情况下,@Autowired 注释意味着依赖是必须的,它类似于 @Required 注释,然而,你可以使用 @Autowired 的 (required=false) 选项关闭默认行为。

说了那么多理论知识,下面看一个例子,这个例子是之前用过的,只不过当时用的是xml配置的。
首先配置一下xml文件:

<?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" >

        <context:component-scan base-package="com.mss.test" />

 </beans>

我们把我们当前的包添加到扫描中。

下面是我们之前用过的例子,我直接贴代码:

public interface InjectionDAO {

    public void save(String arg);

}
@Repository
public class InjectionDAOImpl implements InjectionDAO{

    public void save(String arg) {
        // TODO Auto-generated method stub
        System.out.println("向数据库保存数据:"+arg);
    }

}
public interface InjectionService {

    public void save(String arg);

}
@Service
public class InjectionServiceImpl implements InjectionService {

    @Autowired
    InjectionDAO injectionDAO;

//  public InjectionServiceImpl(InjectionDAO injectionDAO) {
//      this.injectionDAO = injectionDAO;
//  }

    public void save(String arg) {
        // TODO Auto-generated method stub
        System.out.println("处理数据"+arg);
        arg=arg+":已处理";
        injectionDAO.save(arg);
    }

}

可以看到我把在xml中的配置放到了注解中,首先是InjectionDAOImpl 上加了@Repository注释,@Repository对应数据访问层Bean。然后在InjectionServiceImpl 上加@Service注解,@Service对应的是业务层Bean。在InjectionServiceImpl 中的InjectionDAO上添加Autowired注释让InjectionDAO自动注入,我们也可以添加在构造函数上:

@Service
public class InjectionServiceImpl implements InjectionService {


    InjectionDAO injectionDAO;
    @Autowired
    public InjectionServiceImpl(InjectionDAO injectionDAO) {
        this.injectionDAO = injectionDAO;
    }

    public void save(String arg) {
        // TODO Auto-generated method stub
        System.out.println("处理数据"+arg);
        arg=arg+":已处理";
        injectionDAO.save(arg);
    }

}

效果都是一样的,下面写一个测试类:

@RunWith(BlockJUnit4ClassRunner.class)
public class TestOneInterface extends UnitTestBase{

    public TestOneInterface() {
        super("classpath*:spring-ioc.xml");
    }

    @Test
    public void testAutoWriter() {
        InjectionService injectionService=super.getBean("injectionServiceImpl");
        injectionService.save("山水");
    }
}

运行结果:

处理数据山水
向数据库保存数据:山水:已处理

如果我们把InjectionDAOImpl的注解删掉在运行会怎么样呢,

警告: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'injectionServiceImpl' 

可以看到运行错误,错误原因是创建injectionServiceImpl失败,还记得@Autowired的required属性吗,我们给injectionServiceImpl的Autowired注释添加required=false属性:

@Service
public class InjectionServiceImpl implements InjectionService {


    InjectionDAO injectionDAO;

    @Autowired(required=false)
    public InjectionServiceImpl(InjectionDAO injectionDAO) {
        this.injectionDAO = injectionDAO;
    }

    public InjectionServiceImpl() {

    }

    public void save(String arg) {
        // TODO Auto-generated method stub
        System.out.println("处理数据"+arg);
        arg=arg+":已处理";
        injectionDAO.save(arg);
    }

}

由于创建bean时一定会调用一个构造函数,所以我们创建一个空的构造函数,运行结果没有报错。

@Autowired注解数组

@Autowired可以用来注解数组,这样可以自动注入符合条件的成员。
我们将两个具有相同超类型的类注入到一个数组中。
首先定义一个超类型接口:

public interface BeanInterface {

}

接着实现两个实现超类型的类:

@Component
public class BeanImplOne implements BeanInterface {

}
@Component
public class BeanImplTwo implements BeanInterface {

}

接着创建一个存放数组的类,这个类中用List和Map两种方式存储,Map中的key必须是String,他存储bean的id:

@Component
public class BeanInvoker {

    @Autowired
    private List<BeanInterface> list;

    @Autowired
    private Map<String, BeanInterface> map;

    public void say() {
        if (null != list && 0 != list.size()) {
            System.out.println("list...");
            for (BeanInterface bean : list) {
                System.out.println(bean.getClass().getName());
            }
        } else {
            System.out.println("List<BeanInterface> list is null !!!!!!!!!!");
        }

        System.out.println();

        if (null != map && 0 != map.size()) {
            System.out.println("map...");
            for (Map.Entry<String, BeanInterface> entry : map.entrySet()) {
                System.out.println(entry.getKey() + "      " + entry.getValue().getClass().getName());
            }
        } else {
            System.out.println("Map<String, BeanInterface> map is null !!!!!!!!!!");
        }   
    }

}

最后写个测试类:

@RunWith(BlockJUnit4ClassRunner.class)
public class TestOneInterface extends UnitTestBase{

    public TestOneInterface() {
        super("classpath*:spring-ioc.xml");
    }

    @Test
    public void test() {
        BeanInvoker invoker=super.getBean("beanInvoker");
        invoker.say();
    }
}

我们看一下运行结果:

list...
com.mss.test.multibean.BeanImplOne
com.mss.test.multibean.BeanImplTwo

map...
beanImplOne      com.mss.test.multibean.BeanImplOne
beanImplTwo      com.mss.test.multibean.BeanImplTwo

我们也可以指定数组存储的顺序,只需要在成员类上添加@Order注解指定顺序就可以了。

@Order(1)
@Component
public class BeanImplOne implements BeanInterface {

}
@Order(2)
@Component
public class BeanImplTwo implements BeanInterface {

}

@Qualifier

修改一下之前的代码:

@Component
public class BeanInvoker {

    @Autowired
    @Qualifier("beanImplTwo")
    private BeanInterface beanInterface;

    public void say() {

        if (null != beanInterface) {
            System.out.println(beanInterface.getClass().getName());
        } else {
            System.out.println("beanInterface is null...");
        }

    }

}

BeanInterface有两个实现类,我们怎么确定注入的是哪一个呢,这时就需要@Qualifier这个注解了,@Qualifier(“beanImplTwo”)中的beanImplTwo是bean的名称,所以 @Autowired 和@Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。@Autowired 可以对成员变量、方法以及构造函数进行注释,而@Qualifier 的标注对象是成员变量、方法入参、构造函数入参。看一下运行结果:

com.mss.test.multibean.BeanImplTwo

可以看到注入的是我们指定的bean,@Qualifier在方法和构造函数中使用的方法如下:

@Component
public class BeanInvoker {

    private BeanInterface beanInterface;

    @Autowired
    public void setBeanInterface(@Qualifier("beanImplTwo")BeanInterface beanInterface) {
        this.beanInterface = beanInterface;
    }

    @Autowired
    public BeanInvoker(@Qualifier("beanImplTwo")BeanInterface beanInterface) {
        this.beanInterface = beanInterface;
    }

    public void say() {

        if (null != beanInterface) {
            System.out.println(beanInterface.getClass().getName());
        } else {
            System.out.println("beanInterface is null...");
        }

    }

}
发布了65 篇原创文章 · 获赞 24 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/shanshui911587154/article/details/79069519