整体思路
基于XML配置的时候,我们需要先建立一个XML文件,然后再让ApplicationContext去主动加载该XML文件。
按照这个思路,注解驱动开发也是这个流程,首先用@Configuration注解标记一个配置类(充当XML配置文件),然后在配置类中用@Bean注解去标记方法(用方法充当<bean/>节点),最后用AnnotationConfigApplicationContext取代ClassPathXml-
ApplicationContext。
@Bean
@Bean表示一个方法实例化、配置或者初始化一个Spring IOC容器管理的新对象。
@Component
@Component自动被comonent扫描。表示被注解的类会自动被component扫描。
@Repository
@Repository用于持久层,用来表示一个持久层bean,即数据访问层DAO组件。
Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。
这个在学习Spring Data时会说明。
@Service
@Service表示被注解的类是位于业务层的业务component。
Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。
@Controller
@Controller表明被注解的类是控制component,主要用于展现层。
Spring MVC采用经典的三层分层控制结构,在持久层、业务层、控制层分别采用@Repository、@Service、@Controller注解来对分层中的相关类进行注解。
@Controller标记的类就是一个Spring MVC中的Controller对象,分发处理器会扫描使用该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是处理请求的处理器(可以理解为Struts的action类与方法)。
@Controller标记的类,要么在XML中进行定义,要么在<context:component-scan/>中进行自动扫描。
<context:component-scan/>
之前通过<context:component-scan/>只会发现@Controller、@Service、@Repository、@Component注解。
默认use-default-filters="true",即默认扫描@Component注解及其子注解,可以配置filter过滤只扫描哪些注解、不扫描哪些注解。
@ComponentScan自动扫描组件
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service;
/** * @Configuration:标准配置类 * @ComponentScan:发现@Service、@Repository、@Component注解,该注解作用类似于<context:component-scan/>, * 其中的value参数用来指定扫描的包路径, * includeFilters则是包含什么,其要求是一个ComponentScan.Filter[] * excludeFilters则是不包含什么,其要求是一个ComponentScan.Filter[],其中type = FilterType.ANNOTATION表达的意思是按照注解进行过滤, * 而classes是一个数组,里面可以填写各种注解的class对象 * 这里可以参考之前的XML配置,基本与XML是完全一样的。 * 2020 - 07 - 16 * 16:21 */ @Configuration @ComponentScan( value = "com.spring.annotation", excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}) }) public class MainConfig {
/** * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致) * */ @Bean public Persion persion(){ return new Persion("ZhangSan",30); } } |
@Scope组件作用域
Bean默认都是单实例,通过@Scope来改变这种情况。默认单实例的时候,IOC容器启动的时候就会创建对象放到IOC容器中,以后每次获取都是直接从容器中获取。
多实例情况下,每次获取的时候才会调用方法,来创建对象,每调用一次方法,就新创建一个对象实例。
/** * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致) * */ @Bean @Scope("prototype") public Persion persion(){ return new Persion("ZhangSan",30); } |
@Lazy懒加载
只能用于单实例的Bean。因为单实例Bean会在IOC容器启动的时候就被初始化,所谓懒加载,就是IOC容器启动时暂不创建对象,直到第一次获取该Bean的时候再去创建对象并初始化。
/** * @Bean,类型为返回值的类型,id默认是方法名(大小写与方法名完全一致) * */ @Bean @Lazy public Persion persion(){ return new Persion("ZhangSan",30); } |
@Conditional按照条件注册Bean
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); } |
该注解可以用在类上,也可以用在方法上;参数是Condition接口的实现类即可。
当用于注解类的时候,则只有满足条件,这个类中所有的方法才能被注册Bean。
当用于注解方法的时候,则只有满足条件,这个方法才能被注册Bean。
@Bean @Lazy @Conditional(MyConditional.class) public Persion persion(){ return new Persion("ZhangSan",30); } |
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; import sun.misc.OSEnvironment;
public class MyConditional implements Condition { /** * ConditionContext:判断条件能使用的上下文环境 * AnnotatedTypeMetadata:注解信息 * */ @Override public boolean matches( ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { // 获取IOC使用的BeanFactory ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); // 获取类加载器 ClassLoader classLoader = conditionContext.getClassLoader(); // 获取环境 Environment environment = conditionContext.getEnvironment(); // 获取bean定义的注册类 BeanDefinitionRegistry beanDefinitionRegistry = conditionContext.getRegistry(); String osName = environment.getProperty("os.name"); if(osName.toUpperCase().contains("WINDOW")){ return true; } return false; } } |
@Import给容器快速导入一个组件
如果想把一个类导入进来,可以使用@Import注解。该注解只能用在类文件上,而至于被引入的类,不管其有没有注解,都可以被引入(特别是第三方框架里的文件,因为第三方的jar文件,你是无法修改源代码的)。
id默认是类的全类名。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Import { Class<?>[] value(); } |
@Import(ImportTest.class) @Configuration @ComponentScan( value = "com.spring.annotation", excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}) }) public class MainConfig { } |
扩展:
可以通过ImportSelector接口来返回一个数组,数组元素就是类文件的全类名,然后@Import注解就会解析这个数组,然后去加载对应的类文件。
还可以通过ImportBeanDefinitionRegistrar接口,直接自己来注册一个Bean,不需要@Import注解。
使用FactoryBean注册组件
注意&符的使用。