Spring注解开发系列Ⅰ--- 组件注册(上)
传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:
1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。
2、在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。
为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
1.@Configuration&@Bean给容器中注册组件
@Configuration可理解为用spring的时候xml里面的<beans>标签
@Bean可理解为用spring的时候xml里面的<bean>标签
xml版:
<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 http://www.springframework.org/schema/context/spring-context.xsd"> <!--原始方式-->
<bean id="person" class="com.wang.bean.Person">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
</beans>
注解版:
//在配置类里配置 @Configuration//告诉spring这是一个配置类 public class PersonConfig { @Bean(value = "person") //给spring容器注册一个bean,类型为返回值类型,id是默认是方法名为id,也可以使用value指定 public Person person(){ return new Person("lisi",20); } }
2.@ComponentScan-自动扫描组件&指定扫描规则
该注解会自动扫描包路径下面的所有@Controller、@Service、@Repository、@Component 的类
xml版:
<!--包扫描,只要注解了@Component,@Controller等会被扫描--> <context:component-scan base-package="com.wang" use-default-filters="false" > <!--排除某个注解,除了Controller其他类都会被扫描--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!--只包含某个注解--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
注解版:
@ComponentScan(value = "com.wang"/*excludeFilters = { //排除 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Service.class}) //排除Controller和Service },*/,includeFilters = { //只包含 @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Repository.class})
/*,@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})*/ //自定义注解
},useDefaultFilters = false) //这里要加useDefaultFilters=false让默认的过滤器失效
其中Filter的type的类型有:
1. FilterType.ANNOTATION 按照注解
2. FilterType.ASSIGNABLE_TYPE 按照类型 FilterType.REGEX 按照正则
3. FilterType.ASPECTJ 按照ASPECJ表达式规则
4. FilterType.CUSTOM 使用自定义规则
其中自定义规则类型过滤器需要实现TypeFilter接口:
package com.wang.config; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import java.io.IOException; //自定义类型过滤器 public class MyTypeFilter implements TypeFilter { //metadataReader读取到当前扫描到的类的信息 //metadataReaderFactory可以获取其他任何类的信息 @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前扫描类的信息 ClassMetadata classMetadata = metadataReader.getClassMetadata();//获取当前扫描类的类信息 Resource resource = metadataReader.getResource();//获取当前扫描类的资源信息 String className = classMetadata.getClassName(); System.out.println("className--->"+className); if (className.contains("Dao")){ return true; }else { return false; } } }
3.@Scop 设置组件作用域
xml版:
<bean id="person" class="com.wang.bean.Person" scope="prototype"> <property name="name" value="张三"></property> <property name="age" value="18"></property> </bean>
注解版:
@Bean
/**
* prototype 多例,ioc容器创建完成后,要获取的时候才会被调用,多次获取会多次调用
* singleton 单例(默认),ioc容器启动会调用方法创建对象放到ioc中,以后直接从容器中获取
* request 同一次请求创建一个实例
* session 同一个session创建一个实例
*
*/
@Scope("prototype")
public Person person(){
System.out.println("添加Person....");
return new Person("张三",23);
}
4.@Lazy 懒加载bean
xml版本:
<bean id="person" class="com.wang.bean.Person" scope="prototype" lazy-init="true">
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
注解版:
/** * * 单实例bean,默认在容器启动的时候创建对象 * 懒加载:容器启动不创建对象,第一次使用(获取)创建Bean,并初始化,第二次使用就使用第一次创建的对象 * */ @Lazy
5.@Conditional按照条件注册bean
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。下面给个例子,判断当前项目的运行环境,如果是Windows系统,注册id为win的bean,若是Linux系统,注册id为lin的bean
1).实现Condition接口
public class WindowsCondition implements Condition { /** * * @param conditionContext 判断条件能使用的上下文环境 * @param annotatedTypeMetadata 当前标注了condition的注释信息 * @return */ @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //1.获取ioc的BeanFactory ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); //2.获取类加载器 ClassLoader classLoader = conditionContext.getClassLoader(); //3.获取当前环境信息 Environment environment = conditionContext.getEnvironment(); //4.获取所有bean定义的注册类 BeanDefinitionRegistry registry = conditionContext.getRegistry(); String property = environment.getProperty("os.name"); if (property.contains("Windows")){ return true; } else { return false; } } }
package com.wang.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotatedTypeMetadata; public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { //3.获取当前环境信息 Environment environment = conditionContext.getEnvironment(); String property = environment.getProperty("os.name"); if (property.contains("Linux")){ return true; } else { return false; } } }
2).在Bean上配置注解
@Configuration public class ConditionConfig { /** * @Conditional({})按照一定条件进行判断,满足条件容器中注册bean, * 若放在类中,整个配置类中的bean满足条件才会被加载到容器中 * * 若是windows系统,注册win,若是linux注册lin */ @Bean("win") @Conditional(WindowsCondition.class) public Person person(){ return new Person("win",22); } @Bean("lin") @Conditional(LinuxCondition.class) public Person person2(){ return new Person("lin",11); } @Bean("person") public Person person3(){ return new Person("person",25); } }