和 XML 配置文件一样,注解本身并不能执行,注解本身仅仅只是做一个标记,具体的功能是框架检测到注解标记的位置,然后针对这个位置按照注解标记的功能来执行具体操作。
本质上:所有一切的操作都是Java代码来完成的,XML和注解只是告诉框架中的Java代码如何执行。
Spring 为了知道程序员在哪些地方标记了什么注解,就需要通过扫描的方式,来进行检测。然后根据注解进行后续操作。
org.springframework spring-context 5.3.1 junit junit 4.12 test
@Component:将类标识为普通组件
@Controller:将类标识为控制层组件
@Service:将类标识为业务层组件
@Repository:将类标识为持久层组件
问:以上四个注解有什么关系和区别?
通过查看源码我们得知,@Controller、@Service、@Repository这三个注解只是在@Component注解的基础上起了三个新的名字。
对于Spring使用IOC容器管理这些组件来说没有区别。所以@Controller、@Service、@Repository这三个注解只是给开发人员看的,让我们能够便于分辨组件的作用。
注意:虽然它们本质上一样,但是为了代码的可读性,为了程序结构严谨我们肯定不能随便胡乱标记。
创建控制层组件
@Controller
public class UserController {
}
创建接口UserService
public interface UserService {
}
创建业务层组件UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
}
创建接口UserDao
public interface UserDao {
}
创建持久层组件UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao {
}
情况一:最基本的扫描方式
情况二:指定要排除的组件(用得多)
情况三:仅扫描指定组件
@Test
public void testAutowireByAnnotation(){ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");UserController userController = ac.getBean(UserController.class);System.out.println(userController);UserService userService = ac.getBean(UserService.class);System.out.println(userService);UserDao userDao = ac.getBean(UserDao.class);System.out.println(userDao);
}
在我们使用XML方式管理bean的时候,每个bean都有一个唯一标识,便于在其他地方引用。现在使用注解后,每个组件仍然应该有一个唯一标识。
默认情况:
类名首字母小写就是bean的id。例如:UserController类对应的bean的id就是userController。
自定义bean的id:
可通过标识组件的注解的value属性设置自定义的bean的id
@Service(“userService”)//默认为userServiceImpl
public class UserServiceImpl implementsUserService {}
参考基于xml的自动装配
在UserController中声明UserService对象
在UserServiceImpl中声明UserDao对象
**在成员变量上直接标记@Autowired注解即可完成自动装配,不需要提供setXxx()方法。**以后我们在项目中的正式用法就是这样。
@Controller
public class UserController {@Autowiredprivate UserService userService;public void saveUser(){userService.saveUser();}
}
public interface UserService {void saveUser();
}
@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserDao userDao;@Overridepublic void saveUser() {userDao.saveUser();}
}
public interface UserDao {void saveUser();
}
@Repository
public class UserDaoImpl implements UserDao {@Overridepublic void saveUser() {System.out.println("保存成功");}
}
@Autowired注解还可以标记在构造器和set方法上,完成自动装配
@Controller
public class UserController {private UserService userService;@Autowiredpublic UserController(UserService userService){this.userService = userService;}public void saveUser(){userService.saveUser();}
}
@Controller
public class UserController {private UserService userService;@Autowiredpublic void setUserService(UserService userService){this.userService = userService;}public void saveUser(){userService.saveUser();}
}
a) 默认通过 byType 的方式,在IOC容器中通过类型匹配某个bean为属性赋值,如果不存在类型匹配的话直接报NoSuchBeanDefinationException
b) 当有多个 bean 的类型能匹配到,其会转换为 byName 的方式,根据@Autowired标记位置成员变量的变量名作为bean的id进行匹配。
c) byType和byName都失效的时候,即byType有多个 bean 的类型能匹配到,但byName和其中任何一个类型相同的比较id都不一样,则报 noUniqueBeanDefinationException.
d) 要解决c),可以使用@Qualifier注解:根据@Qualifier注解中指定的名称作为bean的id进行匹配
当执行:
@Test
public void test3(){ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");UserController userController = ioc.getBean(UserController.class);userController.saveUser();
}
其中userService成员变量进行扫描的时候
@Autowired
private UserService userService;
会找到两个bean,类别都为UserService,同时默认byName要找的是userService,配置的bean的id却为userServiceAAA,也不等于userService,因此报错:
我们可以在指定的地方使用下面的注解,强行指定要匹配的id:
package com.jxz.spring.controller;import com.jxz.spring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;@Controller("jxzController")
public class UserController {@Autowired@Qualifier("userServiceAAA")private UserService userService;public void saveUser(){userService.saveUser();}
}
当匹配不到相同类型的Bean完成自动装配的时候,会报 NoSuchBeanDefinationException 的错误,这是因为
@Autowire(required = true)
,必须完成自动装配,不然直接报错。当修改为
@Autowire(required = false)
的时候,表示不是必须自动状态,找不到Bean则使用默认值,和之前使用XML自动装配的时候的情况一样。
比如:将依赖的UserDaoImpl注释掉,报NoSuchBeanDefinationException
package com.jxz.spring.dao.impl;import com.jxz.spring.dao.UserDao;
import org.springframework.stereotype.Repository;//@Repository
public class UserDaoImpl implements UserDao {@Overridepublic void saveUser() {System.out.println("保存成功");}
}
在对应的调用方修改:
@Service
public class UserServiceImpl implements UserService {@Autowired(required = false)private UserDao userDao;public void saveUser(){userDao.saveUser();}
}
因为要用到private UserDao userDao,但是却不能完成自动装配,因此使用默认值null,于是 userDao.saveUser();报java.lang.NullPointerException