注解标记和扫描
1、注解的标记
若IOC容器所扫描的类型被指定的注解所标识,此时该类型就会被作为bean交给IOC容器管理。
注解就是一个标记,本身没有功能,注解是通过反射来解析的
被指定的注解:在spring中标识组件的注解有4个:
@Component:将类标识为普通组件 @Controller:将类标识为控制层组件 @Service:将类标识为业务层组件 @Repository:将类标识为持久层组件
以上四个注解,功能没有任何区别,作用都是将注解标记的类标识为IOC容器的bean。但是对于程序员来说,可以通过不同的注解标识不同的组件
注解标记:注意不能给SecretBookService接口加注解,要给它的实现类加注解才对(不是说不可以给接口加注解,是可以给接口加注解的)
@Controller
public class SecretBookController {
SecretBookService secretBookService;
public void showSecretBook(){
secretBookService.showSecretBook();
}
}
@Service
public class SecretBookServiceImpl implements SecretBookService {
@Override
public void showSecretBook() {
System.out.println("恭喜宿主绑定成功");
}
}
2、默认id
通过 注解+扫描 所配置的组件,在IOC容器中有默认的id,即 类名所对应的小驼峰
若要自定义bean的id,可以使用标识组件的四个注解的value属性设置自定义的bean的id
3、注解的扫描
在applicationContext.xml中,加入扫描注解的标签<component-scan>
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!-- 扫描组件-最基本的扫描方式-->
<context:component-scan base-package="com.atguigu.spring" >
</context:component-scan>
</beans>
测试代码:
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); SecretBookController secretBookController = ac.getBean(SecretBookController.class); SecretBookServiceImpl secretBookServiceImpl = ac.getBean(SecretBookServiceImpl.class); System.out.println(secretBookController); System.out.println(secretBookServiceImpl);
测试结果:说明SecretBookController,SecretBookServiceImpl确确实实被IOC容器所管理了
4、扫描排除
context:exclude-filter:排除对组件的扫描,即不扫描某些类型
type:设置排除和包含的方式,type="annotation|assignable"
annotation:表示通过注解的类型进行排除和包含 assignable:表示通过类的类型进行排除和包含
type=“annotation”右键复制注解的全类名
annotation:表示通过注解的类型进行排除和包含
<context:component-scan base-package="com.atguigu.spring"> <!-- 根据注解排除--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
type=“assignable”右键复制类的全类名
assignable:表示通过类的类型进行排除和包含
<context:component-scan base-package="com.atguigu.spring"> <!-- 根据类的类型排除--> <context:exclude-filter type="assignable" expression="com.atguigu.spring.controller.SecretBookController"/> </context:component-scan>
5、扫描包含
context:include-filter:包含对组件的扫描,即只扫描某些类型
<context:component-scan base-package="com.atguigu.spring" use-default-filters="false"> <!-- 只扫描通过context:include-filter所设置的组件--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
use-defaultfilters="false" 表示不使用组件扫描的默认规则(即不扫描指定包下的类型)只扫描通过context:include-filter所设置的组件
默认规则:将指定包下所有的类型全部扫描
但是,要是只想扫描Controller包下的组件,下面这种方式他不香吗
<context:component-scan base-package="com.atguigu.spring.controller" >
</context:component-scan>
自动装配
1、自动装配(XML方式)
自动装配:装配就是注入,自动为类类型(接口、抽象类也行)的属性赋值。自动装配指ref的属性
根据指定的策略,把IOC某一个类型匹配的bean赋值给 “自动装配的bean的ref属性”。
通过autowire设置自动装配的策略
autowire="default|no|byType|byName"
default|no:效果一致,表示不装配<bean id="heroController" class="com.atguigu.spring.controller.HeroController" autowire="byType"></bean>
byType:根据自动装配的属性的类型自动装配,根据类型去匹配某个兼容类型的bean,有且只能有一个类型匹配的bean
若一个都没有,则没有任何一个bean能够为当前属性自动赋值,则当前属性为默认值
若有多个bean能够为当前属性赋值,会抛出异常NoUniqueBeanDefinitionException
byName:将自动装配的属性的属性名作为bean的id在IOC容器中匹配相对应的bean进行赋值
要保证类型匹配的情况下,根据byName匹配某个bean为属性赋值
若没有任何一个bean的id和属性名一致,则不装配,即当前属性为默认值
在 applicationContext.xml中,只写了如下。注意我并没有使用<property>。
<bean id="heroController" class="com.atguigu.spring.controller.HeroController" autowire="byType"></bean>
<bean id="heroService" class="com.atguigu.spring.service.impl.HeroServiceImpl" autowire="byType"></bean>
<bean id="heroDao" class="com.atguigu.spring.dao.impl.HeroDaoImpl" autowire="byType"></bean>
测试代码如下:
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");
HeroController heroController = ac.getBean(HeroController.class);
heroController.saveHero();
HeroController中,一定要加set方法。
public class HeroController {
HeroService heroService;
public void setHeroService(HeroService heroService) {
this.heroService = heroService;
}
public void saveHero(){
heroService.saveHero();
}
}
HeroService heroService; 并没有给赋初值,但是结果也没有报空指针异常,反而调到了底层dao里的方法!原因便是自动装配了,
从 applicationContext.xml中可知,HeroController开启了自动装配。则会自动为类类型的属性赋值,即自动为heroService赋值。赋值的策略是byType,即在IOC容器中根据自动装配的属性的类型匹配某个bean为当前属性自动赋值。
2、@Autowired使用演示
自动装配 @Autowired加到成员变量上,不再需要set方法。
当然,@Autowired注解也可以标记在构造器和set方法上吗,但是没必要,花里胡哨的
@Controller
public class SecretBookController {
@Autowired
SecretBookService secretBookService;
public void showSecretBook(){
secretBookService.showSecretBook();
}
}
此时applicationContext.xml中,真的是孤苦伶仃。只配置了包扫描
测试代码:
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml"); SecretBookController secretBookController = ac.getBean(SecretBookController.class); secretBookController.showSecretBook();
测试结果:成功调到了SecretBookServiceImpl的方法
3、@Autowired原理
通过@Autowired实现自动装配的功能
@Autowired:先根据byType方式,若通过类型匹配到了多个bean,会转为byName的方式匹配bean,为属性赋值。
a>先通过byType的方式,根据自动装配的属性的类型,匹配“IOC容器中的bean”为属性赋值
①在IOC容器中没有任何一个类型匹配的bean,此时抛出异NoSuchBeanDefinitionException
测试:删除@Service注解,再跑上面程序。
②可以在@Autowired注解中设置属性required=false,该属性默认值为true,为true表示当前属性必须能够自动装配,否则直接抛出异常NoSuchBeanDefinitionException,若设置为false,则表示能装配则装配,若没有匹配的bean,则使用默认值
b>若通过类型匹配到了多个bean,此时转换为byName的方式匹配bean,为属性赋值
①若将要自动装配的属性的属性名作为bean的id匹配,没有匹配成功,此时抛出异常NoUniqueBeanDefinitionException
测试:
@Autowired SecretBookService secretBookService;
此时IOC容器中有俩个SecretBookServiceImpl类型的bean,beanid分别是xml方式配置的service和默认的secretBookServiceImpl
②解决多个bean的id和要自动装配的属性的属性名都不匹配(就是我们上面那种情况),有两种解决方案:
方式一:
通过标识组件的注解的value属性设置bean的自定义的id,和要自动装配的属性的属性名一致
方式二:
通过@Qualifier注解的value属性设置要为属性自动装配的bean的id
或者
4、@Resource
@Resource 和@Autowired都是用来自动装配,放在属性字段上。@Autowired用的更多。
1、@Resource是JDK原生的注解,@Autowired是Spring2.5 引入的注解
2、@Resource默认byname实现,找不到名字,再通过bytype实现。@Autowired是通过bytype自动装配的
摆脱XML配置?
@Value
那属性的注入呢?比如说name的注入,用@Value等价于xml里的:
<property name="name" value="kuangshen">
@Scope
当然作用域也是可以用注解@Scope指定的,如下把User类设置为原型模式
@Component
@Scope("prototype")
public class User{
}
@Configuration和@Bean
代表这是一个配置类(会在之后springboot中详解)
- @Configuation等价于<beans></beans>
- @Bean等价于<bean></bean>
- @ComponentScan等价于<context:component-scan base-package=”com.dxz.demo”/>
@Configuration //配置类,本身也是@Component
@ComponentScan("com.cn.spring")
public class LongConfig {
@Bean //相当于xml配置中的<bean/>
public UserDao getUser(){ //getUser方法名,就相当于bean标签的id属性
return new UserDao(); //返回注入的bean的对象
}
}
那这种纯Java的配置方式,怎么取到容器里注册的bean呢
@Configuration //配置类,本身也是@Component
@ComponentScan("com.cn.spring")
public class LongConfig {
@Bean //相当于xml配置中的<bean/>
public UserDao getUser(){ //getUser方法名,就相当于bean标签的id属性
return new UserDao(); //返回注入的bean的对象
}
public static void main(String[] args) {
//通过AnnotationConfig上下文来获取容器,通过LongConfig.class(配置类)加载
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(LongConfig.class);
UserDao dao = (UserDao)context.getBean("getUser");
dao.add();
}
}