初识注解:
①@ComponentScan:组件扫描
@ComponentScan在AppConfig.class中的四种写法:
①直接写@ComponentScan:意为扫描AppConfig.class所在的包以及所有子包中含有@Component注解的类并注册Bean。
②写一个包@ComponentScan("com.zzx.demo"):意为扫描指定包以及指定包的所有子包中含有@Component注解的类并注册Bean。
③写多个包@ComponentScan(basePackages = {"com.zzx.demo.web","com.zzx.demo.service","com.zzx.demo.dao"}):
意为扫描多个包以及这多个包的所有子包中含有@Component注解的类并注册Bean。
④写类@ComponentScan(basePackageClasses = {UserController.class, UserService.class, UserDao.class}):
意为扫描类所在的包以及类所在的包的所有子包中含有含有@Component注解的类并注册Bean。
AppConfig.class:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ContextConfiguration
@ComponentScan("com.zzx.demo")
public class AppConfig {
}
@Configuration:意为标注当前类是一个配置类。
②@Conponent:标注其是一个组件
@Component:
可以写为@Component("类id"),在里面写着的是标识当前类的类id,如果不写,则默认为当前类名的首字母小写的字符串是类id,例如:当前类为UserServiceNormal,则类id为:userServiceNormal。
该组件等同于在applicationContext.xml中的:
<bean id="userServiceNormal" class="com.zzx.demo.service.impl.UserServiceNormal"/>
@Controller
@Service
@Repository
这三个注解的本质与@Component是一样的,只是语义上更加明确了,明确的表示了当前的类在应用程序的哪个层次,扮演了什么样的功能。
同样的,它们也可以由程序员来写出当前类的‘类id’来标识当前类。
③@Autowired:自动装配
* @Autowired四种使用场景:
* 1.给成员变量【最便捷】
* 2.给成员变量的setter方法
* 3.给有参构造函数【效率最高】
* 4.给任意方法
//1.给成员变量
@Component
public class CDPlayer {
@Autowired
private CompactDisc cd;
@Autowired
private Power power;
public CDPlayer() {
super();
System.out.println("CDPlayer无参构造函数~");
}
public void play(){
power.supply();
cd.play();
}
}
//2.给成员变量的setter方法
@Component
public class CDPlayer {
private CompactDisc cd;
private Power power;
@Autowired
public void setCd(CompactDisc cd) {
this.cd = cd;
System.out.println("调用setCd……");
}
@Autowired
public void setPower(Power power) {
this.power = power;
System.out.println("调用setPower……");
}
public CDPlayer() {
super();
System.out.println("CDPlayer无参构造函数~");
}
public void play(){
power.supply();
cd.play();
}
}
//3.给有参构造函数
@Component
public class CDPlayer {
private CompactDisc cd;
private Power power;
public CDPlayer() {
super();
System.out.println("CDPlayer无参构造函数~");
}
@Autowired//将构造函数的参数自动装配到当前的类中
public CDPlayer(CompactDisc cd) {
this.cd = cd;
System.out.println("CDPlayer有参构造函数~");
}
@Autowired
public CDPlayer(CompactDisc cd, Power power) {
this.cd = cd;
this.power = power;
System.out.println("CDPlayer的多参数函数构造方法......");
}
public void play(){
power.supply();
cd.play();
}
}
//4.给给任意方法
@Component
public class CDPlayer {
private CompactDisc cd;
private Power power;
public CDPlayer() {
super();
System.out.println("CDPlayer无参构造函数~");
}
@Autowired
public void prepare(CompactDisc cd , Power power){
this.cd = cd;
this.power = power;
System.out.println("prepare方法被调用……");
}
public void play(){
power.supply();
cd.play();
}
}
//@Autowired的默认属性为required = true,意为必须能被spring找到,而如果那个类没有@Component注解,则不能被找到。
//如果将默认属性改为required = false,那么spring容器如果找不到的话,也不会出现错误。
//后接最下方if语句后面的注释
//例如:
//@Component
public class CompactDisc {
public CompactDisc() {
super();
System.out.println("CompactDisc无参构造函数~");
}
public void play(){
System.out.println("正在播放音乐!");
}
}
@Component
public class CDPlayer {
@Autowired(required = false)
private CompactDisc cd;
@Autowired
private Power power;
public CDPlayer() {
super();
System.out.println("CDPlayer无参构造函数~");
}
public void play(){
power.supply();
if (cd != null) { //那么相对应的此处,就要判断spring容器是否找到对应的类,能否创建出来对象
cd.play();
}
}
}
④@RunWith和@ContextConfiguration
//在pom.xml文件中,引入spring-test模块,就可以使用这两个注释:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)//有了这两个注解,这个ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);就可以省略了
public class AppTest {
@Autowired//再搭配自动注入,可以把CDPlayer player = context.getBean(CDPlayer.class);也省略了,这样test方法中就只留下player.play();
private CDPlayer player;
@Test
public void testPlay(){
player.play();
}
}
⑤自动装配的歧义性:
//当有一个接口,同时有两个或多个实现类的时候,自动装配就会产生歧义性,它会不知道装配哪一个实现类
//比如对于UserService.interface这个接口,同时有UserServiceNormal.class和UserServiceFestival.class这两个实现类,而在UserController.class中:
@Controller
public class UserController {
@Autowired
private UserService userService;
public void add(){
userService.add();
}
}
//使用了@Autowired,那么如果你使用idea,这个时候private UserService userService;下面就会产生一条红色波浪线,提示你不能Autowired。
//这个地方就是因为自动装配的歧义性,它不知道该使用两者中的哪一个。
//解决方法:
//1.给某一个实现类加一个注解@Primary,此时那条红线就会消失,因为它知道了去装配哪个,首选你注解了@Primary的那个实现类(有些时候不太适合,有一定的局限性)
//2.标识实现类的类id,并在自动装配@Autowired的地方加一个注解@Qualifier,例如:@Qualifier("userServiceFestival"),它就会装配UserServiceFestival.class这个实现类
//3.将UserService改为UserServiceNormal或者UserServiceFestival即可,例如:private UserServiceNormal userServiceNormal;(不推荐)
//4.使用@Resource注解代替@Autowired和@Qualifier,例如:@Resource(name="userServiceFestival")。
//重新总结一下解决方法:
/* 解决方法:
* 1.将UserService改为UserServiceNormal或者UserServiceFestival即可(不推荐)
* 2.使用首选Bean,在某个实现类上使用@Primary注解,将其设置为首选(有些时候不太适合,有一定的局限性)
* 3.使用限定符,即@Qualifier这个注解,使用限定符要和在实现类里的@Qualifier中的字符串一致,匹配后就会使用对应的实现类
* 4.使用限定符和类id,即在实现类通过@Component("id")为实现类设置id,在使用的时候,@Qualifier注解也可以通过@Component中的id来匹配
* 5.使用限定符和类id,即@Component不写id,spring会默认将实现类的类名(首字母小写)作为类id,其余同上
* 6.使用@Resource注解,可以同时代替@Autowired和@Qualifier("userServiceFestival")这两个注解(jdk的标准),注解中的name对应类id
*/