加载注解解析器组件
使用注解作为配置文件是Spring2.5提供的功能,在Spring3中提供了纯java配置的方式
如果要使用注解,必须加载注解解析器组件,让Spring容器解析注解
注解本身毫无意义,是注解解析器通过反射使之有了意义
在applicationContext.xml配置文件中加载注解解析器组件,使用注解会简化配置文件,但无法脱离配置文件,因为初始化Spring容器时,会读取配置文件
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--加载注解解析器组件-->
<!--告知Spring扫描包,并对注解进行解析,Spring会扫描当前包以及子包-->
<context:component-scan base-package="com.xupt"/>
</beans>
将bean交给Spring容器
在Spring中存在4个注解可将bean交给Spring容器,4个注解本质上是一样的
@Component:用于组件类的bean,即不属于以下三个中的任何一个,就使用该注解
@Service:用于业务逻辑,即Service层中的类
@Repository:用于数据库访问层,即DAO层中的类
@Controller:用于控制层,即Servlet,SpringMVC时使用
将这些注解标注在类上就可以将该类bean交给Spring容器
AccountDaoDB类
import org.springframework.stereotype.Repository;
//通过value属性设置该bean对象的beanid
@Repository("accountDaoDB")
public class AccountDaoDB implements IAccountDao {
@Override
public void save(String name) {
System.out.println("AccountDaoDB save method"+name);
}
}
AccountService类
import com.xupt.dao.IAccountDao;
import org.springframework.stereotype.Service;
@Service
public class AccountService implements IAccountService {
private IAccountDao accountDao;
public void register(String name) {
accountDao.save(name);
}
}
此时运行测试类,可以从Spring容器中取出托管的bean对象,但是还不能调用AccountService类的register方法,会报空指针异常。因为还没用注入成员变量accountDao。
测试类
import com.xupt.dao.AccountDao;
import com.xupt.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIOC {
@Test
public void testAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService=context.getBean("accountService",AccountService.class);
System.out.println(accountService);
AccountDao accountDao = context.getBean("accountDao", AccountDao.class);
System.out.println(accountDao);
}
}
说明
1.以上4个注解均只能标注类
2.使用以上4个注解交给Spring容器的bean的beanid默认按照类名的驼峰命名法,但也可以通过value属性指定beanid
3.以上4个注解用法相同,不强制限制标注对应的类,但语义不同,在不同的层次中使用对应注解标注类
注入对象
Spring中提供了两个注入对象的注解
@Autowired:使用最多,按照类型注入对象,相当于autowire属性的值byType
可标注CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE
当Spring容器中该类型对象存在两个及两个以上时,会报错。可以结合@Qualifier注解按照名称注入
@Qualifier:与@Autowired注解结合按照名称(beanid)注入对象
可标注FIELD、METHOD、PARAMETER、TYPE、ANNOTATION_TYPE
该注解不能单独使用,否则会出现异常,必须结合@Autowired注解
JavaEE规范中的注入对象注解,需要导入javax.annotation的jar包
@Resource:可以按照类型也可以按照名称注入对象,默认按照名称
可标注TYPE,、FIELD,、METHOD
通过设置name属性指定注入对象beanid;通过设置type属性来指定注入对象的类型
@Autowired、@Qualifier注解的使用示例
import com.xupt.dao.IAccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class AccountService implements IAccountService {
@Autowired
//通过该注解设置注入对象的beanid
@Qualifier("accountDaoDB")
private IAccountDao accountDao;
public void register(String name) {
accountDao.save(name);
}
}
@Resource注解的使用示例
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class AccountService implements IAccountService {
//默认按照类型注入对象,通过成员变量的类型来寻找bean
@Resource
private IAccountDao accountDao;
public void register(String name) {
accountDao.save(name);
}
}
//设置name属性指定注入对象的beanid
@Resource(name="accountDaoDB")
//设置type属性指定注入的类型,type属性的值为字节码文件对象
@Resource(type=AccountDaoDB.class)
使用注解标注成员变量时,是会打破Java的封装性的,因为成员变量是私有的,不允许在类的外部被直接访问,而注解通过反射直接访问成员变量,为成员变量注入了对象。
Spring中可以通过@Value注解将外部的值动态注入到Bean中
resources目录下存在配置文件db.properties
username=abc
password=123
首先需要在Spring容器中加载该配置文件
<context:property-placeholder location="db.properties"/>
BeanService类
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class BeanService {
//可以直接赋值,但是这样没有意义,相当于private String username="abc";
//@Value("abc")
@Value("${username}")
private String username;
public String getUsername() {
return username;
}
}
在测试类中获取该类bean对象,并调用getUsername方法
import com.xupt.service.BeanService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestIOC {
@Test
public void testAnnotation(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BeanService beanService=context.getBean("beanService", BeanService.class);
/*输出的值与配置文件中的值不符
因为Spring中存在一个配置文件,文件中也存在键username
如果注入配置文件中的password对应的值输出后无异常*/
System.out.println(beanService.getUsername());
}
}
为了避免多个配置文件中存在名称一样的键,可以也命名键名时加上文件前缀,因为文件名称是唯一的,如下:
db.username=abc
db.password=123
作用域、初始化、销毁
@Scope:用于指定bean对象的作用域,默认为singleton
可标注TYPE、METHOD,一般标注TYPE
该注解的两个属性scopeName和value互为别名,即设置其中一个属性的值,另一个属性自动被设置为同样的值
@PostConstruct:用于标注初始化方法
@PreDestroy:用于标注销毁方法
@Scope注解由Spring提供,@PostConstruct和@PreDestroy注解由JavaEE提供,且只能标注METHOD
import com.xupt.dao.IAccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Service
//将作用域设置为原型模式,一般bean对象的作用域都设置为单例模式
//原型模式的使用,比如一个类用于获取Connection,每次都需要获取一个新的Connection对象。
@Scope("prototype")
public class AccountService implements IAccountService {
@Autowired
@Qualifier("accountDaoDB")
private IAccountDao accountDao;
public void register(String name) {
accountDao.save(name);
}
//标注初始化方法
@PostConstruct
public void init(){
System.out.println("init...");
}
//标注销毁方法
@PreDestroy
public void destroy(){
System.out.println("destroy...");
}
}
探究@Scope注解中互为别称的属性
@Scope注解的代码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
@AliasFor:用于标注自定义注解的两个属性,使这两个属性互为别名
互为别名的属性属性值类型、默认值都是相同的,互为别名的注解必须成对出现,且必须定义默认值
Spring3中纯Java配置方式
纯Java配置方式将不再使用xml配置文件
@Configuration:用于标注类,被标注的类成为配置类,可替换xml配置文件
@ComponentScan:用于标注配置类,定义扫描的路径,相当于<context:component-scan>标签
@PropertySource:用于标注配置类,加载指定的属性配置文件,默认从classes目录下寻找,相当于<context:property-placeholder>标签
@Bean:用于标注配置类中的方法,将方法的返回值存放在Spring容器中,默认的beanid为方法的名称
如果程序中引用了第三方的类,需要将该类对象交给Spring容器,但是不能修改类的源代码,就需要使用@Bean注解
在config包下创建ApplicationConfig类作为配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import java.util.Date;
@Configuration
@ComponentScan("com.xupt")
@PropertySource("db.properties")
public class ApplicationConfig {
//bean对象的beanid默认为方法名,即"createDate",可以通过value值指定beanid
@Bean("springIocDate")
public Date createDate(){
return new Date();
}
}
测试类
import com.xupt.config.ApplicationConfig;
import com.xupt.service.AccountService;
import com.xupt.service.BeanService;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestIOC {
@Test
public void testAnnotation(){
//AnnotationConfigApplicationContext容器用于解析被@Configuration标注的类,可通过反射获取标注该类的注解
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
AccountService accountService=context.getBean("accountService",AccountService.class);
accountService.register("java");
BeanService beanService=context.getBean("beanService",BeanService.class);
System.out.println(beanService.getUsername());
Date date = context.getBean("springIocDate", Date.class);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
}
}
xml配置文件和注解配置方式的比较
xml配置文件
优点
1.降低耦合,配置与代码相分离,使容易扩展
2.对象之间的关系清晰
3.xml配置文件比注解功能齐全
缺点
配置文件配置工作量较大
注解配置方式
优点
1.在.class文件中,可以降低维护成本
2.提高开发效率
缺点
1.如果对注解进行修改,需要重新编译整个工程
2.业务类之间的关系不如XML配置那样清晰
3.程序中过多的注解对于代码的简洁度有一定影响
4.注解功能没有xml配置文件齐全