Spring注解配置

注解技术从JDK5.0推出,之后很多框架开始提供注解配置形式。Spring框架从2.5版本开始支持注解配置。注解配置的优点:简单、快捷。

7.1组件扫描功能

Spring可以按指定的包路径扫描内部的组件,当发现组件类定义前有一下的注解标记,会将该组件纳入Spring容器中。

1)@Component(其他组件)

2)@Controller(Action组件,负责调Service)

3)@Service(Service组件,负责调DAO,处理一些额外逻辑)

4)@Repository(DAO组件,负责访问数据库)

  1. 注意事项:
  2. 括号中的为推荐用法,上述4个注解任意用也可以,但不符合规范。
  3. 注解只能用在类定义前、方法定义前、成员变量定义前!

7.2组件扫描的使用方法

step1:在applicationContext.xml配置文件中开启组件扫描配置


<!-- 开启组件扫描,base-package指定扫描包路径。使用前提:要有xmlns:context命名空间的引入。base-package="org.tarena"这么写,则dao和action都能被扫描-->

<context:component-scan base-package="org.tarena" />

step2:在要扫描的组件的类定义前使用上述注解标记即可。例如在JdbcCostDAO类前使用


@Repository
public class JdbcCostDAO implements CostDAO {
       public JdbcCostDAO(){        System.out.println("创建CostDAO对象");        }
       @PostConstruct//等价于设置了init-method="方法名"属性
       public void myinit(){     System.out.println("初始化CostDAO对象");            }
       @PreDestroy//等价于设置了destroy-method="方法名"属性
       public void mydestroy(){      System.out.println("销毁CostDAO对象");        }
      ……              ……


@Repository等价于原来配置文件中的:

<bean id="jdbcCostDAO" class="org.tarena.dao.JdbcCostDAO"></bean>

加上@Scope("prototype")等价于原配置文件中的:


<bean id="jdbcCostDAO" scope="prototype" class="org.tarena.dao.JdbcCostDAO"></bean>


  1. 注意事项:
  2. 上述标记将组件扫描到容器后,id属性默认是类名首字母小写。如果需要自定义id值,可以使用@Repository("自定义id值"),其他注解也同理。
  3. 类的命名和变量的命名要规范!首字母大写,第二个字母要小写!否则在使用框架时会有冲突或无法识别,如类名为JDBCCostDAO时无法识别它的id值:jDBCCostDAO,此时以它的类名作为id值却可识别:JDBCCostDAO。
  4. 默认采用singleton模式创建Bean对象,如果需要改变,可以使用

@Scope("prototype")定义。

  1. lazy-init="true"属性只能在<beans>根元素定义了,没有对应的注解。

step3:创建TestAnnotation类,用于测试注解

@Test     //组件扫描
public void test1(){        String conf="/applicationContext.xml";
       ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
       //获取扫描到容器的Bean对象
       CostDAO costDAO=(CostDAO)ac.getBean("jdbcCostDao");
       costDAO.delete();//组件扫描,默认的id为类名首字母小写!且默认单例模式   }

7.3注入注解标记使用方法

如果容器中两个符合要求可被注入同一个组件的Bean对象,可以采用下面注解标记:

1)@Resource,默认按类型匹配注入(JDK自带的)。若有多个符合要求的类型,则报错:匹配不唯一,那么就需要采取按名称注入的方式,它的使用格式为:


                    @Resource(name="需要注入的Bean对象id值")。


2)@Autowired,默认按类型匹配注入(Spring提供的)。若有多个符合要求的类型,则采取按名称注入的方式,它的使用格式为:


                    @Autowired

                    @Qualifier("需要注入的Bean对象id值")


  1. 注意事项:注入标记在成员变量定义前,但@Resource也可以在set方法前使用!

3)案例:id为hibernateCostDao的Bean对象和id为costDao的Bean对象,都符合CostDAO接口,在CostAction组件中注入,那么此时将会报错:匹配不唯一。解决如下:

step1:修改CostActin,添加注入标记


@Controller("costAction")
@Scope("prototype")
public class CostAction {
       //@Resource//将costDao注入,按类型匹配注入,JDK自带的
       //@Autowired//将costDao注入,按类型匹配注入,Spring提供的
       //@Resource(name="hibernateCostDao")//当有多个符合要求的类型,则按名称注入
       @Autowired
       @Qualifier("hibernateCostDao")//当有多个符合要求的类型,则按名称注入
       private CostDAO costDAO;
       public void setCostDAO(CostDAO costDAO) {  this.costDAO = costDAO;     }
      ……       ……                                                                                                  }


step2:在TestAnnotation类,添加方法测试注入标记


@Test     //注入标记测试
public void test2(){        String conf="/applicationContext.xml";
       ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
       CostAction costAction=(CostAction)ac.getBean("costAction");
       costAction.execute();                                                                                         }


step3:可正常执行,如果没写注入标记则报错:NullPointerException

7.4 AOP注解标记使用方法

step1:在applicationContext.xml配置文件中开启AOP注解


<aop:aspectj-autoproxy /><!--之前的配置可都删除,只留根元素-->

step2:在方面组件中,使用下面注解标记:

1)首先使用@Component将组件扫描到Spring容器。

2)然后使用@Aspect将组件定义为方面组件。

3)之后定义一个空方法(方法名随便起)在方法前使用@Pointcut定义切入点表达式。

4)最后在方面组件的处理方法前使用@Around、@Before、@AfterReturning、@AfterThrowing、@After

例如:修改5.7案例step4中的LoggerBean类


@Component//将组件扫描到Spring容器
@Aspect//将该组件定义为方面组件
public class LoggerBean {
       //定义切入点
       @Pointcut("within(org.tarena.action..*)")
       public void mypoint(){}//主要目的是使用@Pointcut标记,id则为它的方法名mypoint
       //采用环绕通知
       @Around("mypoint()")//方法即为下面的方法名
       public Object logger(ProceedingJoinPoint pjp) throws Throwable{
       //……方法体内容没变……                                                          }                   }


  1. 注意事项:@Pointcut注解在JDK1.7中不能识别,只能把切入点表达式写在通知中:

@Around("within(org.tarena.action..*)")。而此用法JDK1.6也支持。

step3:再次执行3.3案例step3,则也可正常执行

step4:把6.3案例step2中的ExceptionBean修改为使用AOP注解


@Component//将组件扫描到Spring容器
@Aspect//将该组件定义为方面组件
public class ExceptionBean {
       Logger logger=Logger.getLogger(Exception.class);
       @Pointcut("within(org.tarena.action..*)")
       public void mypoint(){}
       @AfterThrowing(pointcut="mypoint()",throwing="ex")//方法名即为下面方法的名字
       public void exec(Exception ex){    //……方法体内容没变……         }                   }

step5:执行6.3案例step3,则正常执行,控制台显示空指针异常,异常信息也被写入HTML文件。