Spring注解配置
注解技术从JDK5.0推出,之后很多框架开始提供注解配置形式。Spring框架从2.5版本开始支持注解配置。注解配置的优点:简单、快捷。
7.1组件扫描功能
Spring可以按指定的包路径扫描内部的组件,当发现组件类定义前有一下的注解标记,会将该组件纳入Spring容器中。
1)@Component(其他组件)
2)@Controller(Action组件,负责调Service)
3)@Service(Service组件,负责调DAO,处理一些额外逻辑)
4)@Repository(DAO组件,负责访问数据库)
- 注意事项:
- 括号中的为推荐用法,上述4个注解任意用也可以,但不符合规范。
- 注解只能用在类定义前、方法定义前、成员变量定义前!
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>
- 注意事项:
- 上述标记将组件扫描到容器后,id属性默认是类名首字母小写。如果需要自定义id值,可以使用@Repository("自定义id值"),其他注解也同理。
- 类的命名和变量的命名要规范!首字母大写,第二个字母要小写!否则在使用框架时会有冲突或无法识别,如类名为JDBCCostDAO时无法识别它的id值:jDBCCostDAO,此时以它的类名作为id值却可识别:JDBCCostDAO。
- 默认采用singleton模式创建Bean对象,如果需要改变,可以使用
@Scope("prototype")定义。
- 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值")
- 注意事项:注入标记在成员变量定义前,但@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{
//……方法体内容没变…… } }
- 注意事项:@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文件。