我们前面在使用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...");
}
}
}