下面是@Configuration注解的源码翻译部分:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* 1、显式指定与此配置类关联的Spring bean定义的名称。如果未指定(常见情况),将自动生成bean名称。
* 2、只有通过组件扫描获取配置类或直接提供给{@link AnnotationConfigApplicationContext}时,才应用自定义名称。
* 3、如果配置类注册为传统的XML bean定义,那么bean元素的名称/id将优先。
* @return the specified bean name, if any
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
String value() default "";
}
具体看@Configuration的作用是什么?
//配置类:代替bean.xml配置文件
@Configuration
public class MainConfig {
//给spring容器注册组件,类型就是返回值的类型Person,id为方法名person
@Bean
public Person person(){
System.out.println("person方法执行了");
return new Person();
}
//给spring容器注册组件,类型就是返回值的类型Dog,id为方法名dog
@Bean
public Dog dog(){
person();
System.out.println("dog方法执行了");
return new Dog();
}
}
public class MainTest {
public static void main(String[] args) {
//初始化容器
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MainConfig.class);
//获取容器中含有的组件名称
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
}
}
MainTest类输出的结果:
//当初始化容器时,就会执行MainConfig类中的方法,并且发现person()方法只执行了一次,按照常理应该执行2次
person方法执行了
dog方法执行了
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
//容器中只有下面的三个组件
mainConfig
person
dog
问题:person()方法明明调用了两次,但是却只执行了一次,保证了@Bean的对象时单例,它是如何实现的?
原因:@Configuration这个注解的作用就是为MainConfig类加上Cglib动态代理,保证容器中的对象都是单例的
1、自动扫描组件
//将这个组件加入到IOC容器中
@Repository
public class BookDao {
}
public class BookService {
}
//将这个组件加入到IOC容器中
@Controller
public class BookController {
}
//配置类代替bean.xml配置文件
@Configuration
//将这个包下面的加了@Repository、@Service、@Controller、@Component注解的组件都加入容器中
@ComponentScan(value = "com.hh")
public class MainConfig {
//将对象person放入spring容器中
@Bean
public Person person(){
return new Person();
}
//将对象dog放入spring容器中
@Bean
public Dog dog(){
return new Dog();
}
}
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MainConfig.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
}
}
运行结果:
可以看出:这些组件加到了IOC容器中:而Service层的类BoorService由于没有使用@Service注解,没有被加入容器中
mainConfig
bookController
bookDao
person
dog
2:指定扫描规则
下面是@ComponentScan注解的部分源码,翻译了一下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
*1、指示是否自动检测用{@code@Component}注释的类
*/
boolean useDefaultFilters() default true;
/**
1、指定哪些类型适合进行组件扫描。
2、进一步将候选组件集从{@link#base packages}中的所有内容,缩小到与给定过滤器匹配的基本包中的所有内容。
3、 注意,如果指定,这些过滤器将应用于默认过滤器之外。
4、 指定的基包下与给定过滤器匹配的任何类型都将包括在内,即使它与默认过滤器不匹配(即没有用{@code@Component}注释)。
*/
Filter[] includeFilters() default {};
/**
* 指定哪些类型不适合进行组件扫描。
*/
Filter[] excludeFilters() default {};
/**
* 指定是否应为延迟初始化注册扫描的bean。
* Default is {@code false}; switch this to {@code true} when desired.
*/
boolean lazyInit() default false;
/**
1、 声明要用作{@linkplain ComponentScan#includeFilters include filter} or {@linkplain ComponentScan#excludeFilters exclude filter}的类型过滤器。
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* 1、要使用的过滤器类型。
*
Default is {@link FilterType#ANNOTATION}.
* 默认FilterType.ANNOTATION
*/
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class>[] value() default {};
@AliasFor("value")
Class>[] classes() default {};
String[] pattern() default {};
}
具体使用规则:
//配置类代替bean.xml配置文件
@Configuration
/**
* 1、将这个包下面的添加了@Repository、@Service、@Controller、@Component注解的组件添加到容器中
* 2、自定义扫描规则---使用过滤器排除一些组件:按照type = FilterType.ANNOTATION, classes = {Controller.class}来排除,
* 3、自定义扫描规则---只扫描哪些组件:这个需要禁用掉默认的扫描规则才能生效
*/
@ComponentScan(value = "com.hh",
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})},useDefaultFilters = false)
public class MainConfig {
//将对象person放入spring容器中
@Bean
public Person person(){
return new Person();
}
//将对象dog放入spring容器中
@Bean
public Dog dog(){
return new Dog();
}
}
结果:
mainConfig
//只包含了@Service注解的组件
bookService
person
dog
3:自定义FilterType指定过滤规则
下面是 FilterType的源码:
public enum FilterType {
/**
* 筛选用给定注释标记的候选项。
* @see org.springframework.core.type.filter.AnnotationTypeFilter
*/
ANNOTATION,
/**
* 筛选可分配给给定类型的候选项。
* @see org.springframework.core.type.filter.AssignableTypeFilter
*/
ASSIGNABLE_TYPE,
/**
* 筛选与给定AspectJ类型模式表达式匹配的候选项。
* @see org.springframework.core.type.filter.AspectJTypeFilter
*/
ASPECTJ,
/**
* 筛选与给定正则表达式模式匹配的候选.
* @see org.springframework.core.type.filter.RegexPatternTypeFilter
*/
REGEX,
/** 使用给定的自定义筛选候选
* {@link org.springframework.core.type.filter.TypeFilter} implementation.
*/
CUSTOM
}
具体使用规则:
//配置类代替bean.xml配置文件
@Configuration
//按照给定的类型来过滤 type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}
@ComponentScan(value = "com.hh",
includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})},useDefaultFilters = false)
public class MainConfig {
//将对象person放入spring容器中
@Bean
public Person person(){
return new Person();
}
//将对象dog放入spring容器中
@Bean
public Dog dog(){
return new Dog();
}
}
自定义FilterType来过滤:
public class MyTypeFilter implements TypeFilter {
/**
* metadataReader:读取到的当前正在扫描的类的信息
* metadataReaderFactory:可以获取到其他任何类信息的
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// TODO Auto-generated method stub
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
下面是翻译后的@Scope注解的源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
/**
* Alias for {@link #scopeName}.
* @see #scopeName
*/
@AliasFor("scopeName")
String value() default "";
/**
1、指定用了Scope注解的组件/bean的作用域的名称。
2、 默认为空字符串,代表{@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @see #value
*/
@AliasFor("value")
String scopeName() default "";
/**
* 1、指定组件是否应配置为作用域代理,如果应配置,则代理应基于接口还是基于子类。
* 2、默认为{@link ScopedProxyMode#DEFAULT},这通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。
* 3、类似于Spring XML中的{@code }支持。
* @see ScopedProxyMode
*/
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
具体使用规则:
@Configuration
@ComponentScan("com.hh")
public class MainConfig2 {
/**
* prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request:同一次请求创建一个实例
* session:同一个session创建一个实例
*/
@Scope("prototype")
@Bean("person")
public Person person(){
System.out.println("person()方法执行了。。。。");
return new Person();
}
}
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println(applicationContext.getBean("person"));
System.out.println(applicationContext.getBean("person"));
}
}
结果:ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象;
person()方法执行了。。。。
Person(name=null, age=null)
person()方法执行了。。。。
Person(name=null, age=null)
@Configuration
@ComponentScan("com.hh")
public class MainConfig2 {
/**
* 单实例bean:默认在容器启动的时候创建对象;
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
*/
@Lazy
@Bean("person")
public Person person(){
System.out.println("person()方法执行了。。。。");
return new Person();
}
}
public class MainTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器创建完成.....");
}
}
结果:
IOC容器创建完成.....
@Import注解注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration},
* {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class>[] value();//这是一个数组形式
}
1:使用类名导入组件
@Data
public class Color {
private String color;
}
@Data
public class Red{
}
@Configuration
@Import({Color.class, Red.class})
public class MainConfig2 {
@Bean
public Person person(){
return new Person();
}
}
容器中的组件为:
mainConfig2
com.hh.bean.Color
com.hh.bean.Red
person
2:ImportSelector
@Configuration
@Import({Color.class, Red.class,MyImportSelector.class})
public class MainConfig2 {
@Bean
public Person person(){
return new Person();
}
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//需要导入类的全类名
return new String[]{"com.hh.bean.Yellow","com.hh.bean.Blue"};
}
}
结果:返回需要导入的组件的全类名数组;
mainConfig2
com.hh.bean.Color
com.hh.bean.Red
com.hh.bean.Yellow
com.hh.bean.Blue
person
3:ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar:手动注册bean到容器中
public class RainBow {
}
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
@Bean
public Person person(){
return new Person();
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//判断容器中是否包含这个组件
boolean definition1 = registry.containsBeanDefinition("com.hh.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.hh.bean.Blue");
if(definition1 && definition2){
//指定Bean名
RootBeanDefinition definition = new RootBeanDefinition(RainBow.class);
//给容器中注册Bean
registry.registerBeanDefinition("rainbow",definition);
}
}
}
给容器中加入组件的方法:
@ComponentScan注解+@Controller、@Service、@Repository、@Component(局限,是自己写的类)
@Bean(导入第三方组件)
@Import 快速的给容器中导入一个组件
FactoryBean
@Component
public class Car {
public Car(){
System.out.println("car constructor...");
}
public void init(){
System.out.println("car ... init...");
}
public void destory(){
System.out.println("car ... destory...");
}
}
/**
* 1、bean的生命周期由容器管理:创建---初始化---销毁
* 2、初始化和销毁方法可以自定义
* 3、
* 初始化时机:对象创建完成,调用初始化方法
* 对于单实例Bean的销毁时机:容器的关闭的时候
* 对于多实例Bean:不会管理销毁方法
*/
@ComponentScan("com.hh.bean")
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod ="init",destroyMethod = "destory")
public Car car(){
return new Car();
}
}
//测试类
public class IOCTest_LifeCycle {
@Test
public void test01(){
//创建ioc容器
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成...");
//关闭容器
applicationContext.close();
}
}
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
public interface DisposableBean {
void destroy() throws Exception;
}
@Component
public class Cat implements InitializingBean,DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化。。。。。");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁。。。。。");
}
}
@PostConstruct注解:对象创建后调用标注了这个注解的方法
@PreDestroy注解:容器移除之前调用标注了这个注解的方法
//对象创建后调用标注了这个注解的方法
public @interface PostConstruct {
}
//容器移除之前调用标注了这个注解的方法
public @interface PreDestroy {
}
@Component
public class Dog {
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
}
BeanPostProcessor:bean的后置处理器,在初始化前后进行一些处理工作
public interface BeanPostProcessor {
/**
* 在初始化之前进行一些处理工作
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* 在初始化之后进行一些处理工作
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
return bean;
}
}
容器中的组件在初始化之前和初始化之后都执行一些处理:
postProcessBeforeInitialization...cat=>com.hh.bean.Cat@105fece7
cat...init..
postProcessAfterInitialization...cat=>com.hh.bean.Cat@105fece7
car constructor...
postProcessBeforeInitialization...car=>com.hh.bean.Car@6b419da
car ... init...
postProcessAfterInitialization...car=>com.hh.bean.Car@6b419da
@Data
@Repository //默认使用类名首字母小写作为组件id,bookDao
public class BookDao {
private String lable = "1";
}
@Configuration
@ComponentScan({"com.hh.service","com.hh.dao","com.hh.controller","com.hh.bean"})
public class MainConifgOfAutowired {
//向容器中再加入一个组件bookDao2
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
@Service
@Data
public class BookService {
/**
* 1、默认按照类型(ByType)去容器中找对应的组件
* 2、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 (bookDao)
* 3、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id
* 4、@Primary:让Spring进行自动装配的时候,默认使用首选的bean
* 5、自动装配默认一定要将属性赋值好,没有就会报错;可以使用 @Autowired(required=false);
*/
@Qualifier("bookDao2")
@Autowired
private BookDao bookDao;
public void print(){
System.out.println(bookDao);
}
}
java规范的注解
@Service
public class BookService {
/**
* 1、默认按照属性名称到容器中找对应的组件进行注入(byName:bookDao)
* 2、可以指定组件的名称 @Resource(name="bookDao2")
* 3、没有required=false的功能,@Primary功能
*/
@Resource(name="bookDao2")
private BookDao bookDao;
}
@Service
public class BookService {
/**
* 1、@Inject:需要导入javax.inject的包,和Autowired的功能一样
* 2、没有required=false的功能
*/
private BookDao bookDao;
}
1:默认情况下,加在容器中的组件,在容器启动时就会调用无参构造函数来创建对象,然后初始化,把@Autowired标注在方法上,该方法如果有参数(组件),会使用autowired的方式(byType)在容器中查找是否有该组件
@Component
public class Boss {
private Car car;
//标注在方法上,该方法如果有参数(组件),会使用autowired的方式(byType)在容器中查找是否有该组件
@Autowired
public void setCar(Car car) {
this.car = car;
}
}
2:标注在带参构造函数上,如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件可以自动从容器中获取。
@Component
public class Boss {
private Car car;
//构造器要用的组件,从容器中获取
public Boss(Car car){
this.car = car;
System.out.println("Boss...有参构造器");
}
}
3:标注在参数上
@Component
public class Boss {
private Car car;
public void setCar(@Autowired Car car) {
this.car = car;
}
}
4:标注在方法上:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配
@Configuration
@ComponentScan({"com.hh.service","com.hh.dao","com.hh.controller","com.hh.bean"})
public class MainConifgOfAutowired {
//@Bean标注的方法创建对象的时候,方法参数的值从容器中获取
@Bean
public Color color(Car car){
Color color = new Color();
return color;
}
}
自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;
把Spring底层一些组件注入到自定义的Bean中;
@Data
@Component
public class Red implements ApplicationContextAware, BeanNameAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("当前IOC容器:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前Bean的名称:"+name);
}
}
本文是关于spring注解开发的相关内容,希望可以帮到初学spring的小伙伴哦!