一、@ComponentScan 进行包扫描
在配置类上写 @CompentScan 注解来进行包扫描
@Configuration @ComponentScan(basePackages = {"com.yufeng.testcompentscan"}) public class MainConfig { }
1、排除用法; excludeFilters( 排除标注@Controller注解 和 TestService类)
@Configuration @ComponentScan(basePackages = {"com.yufeng.testcompentscan"}, excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {TestService.class}) }) public class MainConfig { }
2、包含用法 includeFilters; 注意,若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的 @Controller @Service @Component @Repository 标注的类) ;
@ComponentScan.Filter type的类型:
- FilterType.ANNOTATION:注解形式的,@Controller @Service @Repository @Compent;
- FilterType.ASSIGNABLE_TYPE:指定类型的,@ComponentScan.Filter(type =FilterType.ASSIGNABLE_TYPE,value = {Test.class});
- FilterType.ASPECTJ:aspectj类型的(不常用);
- FilterType.REGEX:正则表达式的 (不常用);
- FilterType.CUSTOM:自定义的,实现 TypeFilter 接口;
FilterTyp.CUSTOM 的使用:
public class CustomFilterType implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类的注解源信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前类的class的源信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类的资源信息 Resource resource = metadataReader.getResource(); System.out.println("类的路径:"+classMetadata.getClassName()); if(classMetadata.getClassName().contains("dao")) { return true; } return false; } }
@ComponentScan(basePackages = {"com.yufeg.testcompentscan"}, includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM,value = CustomFilterType.class) },useDefaultFilters = false)
二、向IOC容器中添加组件的4种方式
1、@CompentScan + @Controller @Service @Respository @Compent;
2、@Bean 的方式导入组件(适用于导入第三方类的组件);
@Configuration @Import(value = {Person.class, Car.class}) public class MainConfig { }
(2)通过 @Import 导入实现 ImportSeletor 接口的类 (导入组件的id为全类名路径)
@Configuration @Import(value = {Person.class, Car.class, MyImportSelector.class}) public class MainConfig { }
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.yufeng.testimport.compent.Dog"};
}
}
(3)通过@Import 导入 ImportBeanDefinitionRegistrar 导入组件 (可以指定bean的名称)
@Configuration
@Import(value = {Person.class, Car.class, MyImportSelector.class, MyBeanDefinitionRegister.class})
public class MainConfig {
}
public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Cat.class); registry.registerBeanDefinition("cat",rootBeanDefinition); } }
4、通过实现 FactoryBean 接口来实现注册组件;
public class CarFactoryBean implements FactoryBean<Car> { @Override public Car getObject() throws Exception { return new Car(); } @Override public Class<?> getObjectType() { return Car.class; } @Override public boolean isSingleton() { return true; } }
@Configuration public class MainConfig { @Bean public CarFactoryBean carFactoryBean() { return new CarFactoryBean(); } }
public class MainClass { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); // 获取到 car 对象 Object bean = ctx.getBean("carFactoryBean"); System.out.println(bean); // 获取到 carFactoryBean 对象 Object bean2 = ctx.getBean("&carFactoryBean"); System.out.println(bean2); } }
三、Bean的作用域
1、在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了);
- singleton 单实例的(默认);
- prototype 多实例的;
- request 同一次请求;
- session 同一个会话级别;
四、@Conditional进行条件判断等
1、实现 Condition 接口;
public class TulingCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//判断容器中是否有myAspect的组件
if(context.getBeanFactory().containsBean("myAspect")) {
return true;
}
return false;
}
}
public class MainConfig { //@Bean public MyAspect myAspect() { return new MyAspect(); } @Bean @Conditional(value = MyCondition.class) public Car car() { return new Car(); } }
五、Bean的初始化方法和销毁方法
由容器管理Bean的生命周期,我们可以通过自己指定bean的初始化方法和bean的销毁方法 。
@Configuration public class MyConfig { @Bean(initMethod = "init", destroyMethod = "destroy") public Car car() { return new Car(); } }
public class Car { public Car() { System.out.println("Car的构造方法.........."); } // 初始化方法 public void init() { System.out.println("Car的初始化方法......init"); } // 销毁方法 public void destroy() { System.out.println("Car的销毁方法.....destroy"); } }
单实例bean的话,容器启动的时候,bean的对象就创建了,而且容器销毁的时候,也会调用Bean的销毁方法。
多实例bean的话,容器启动的时候,bean是不会被创建的而是在获取bean的时候被创建,而且bean的销毁不受IOC容器的管理。
@Configuration @ComponentScan(basePackages = {"com.yufeng.testbeanlifecycle.my"}) public class MyConfig { }
@Component public class Book { public Book() { System.out.println("book 的构造方法"); } @PostConstruct public void init() { System.out.println("book 的PostConstruct标志的方法"); } @PreDestroy public void destory() { System.out.println("book 的PreDestory标注的方法"); } }
3、通过实现 InitializingBean 和 DisposableBean 这两个接口的bean的初始化以及销毁方法;
@Configuration @ComponentScan(basePackages = {"com.yufeng.testbeanlifecycle.my"}) public class MyConfig { }
@Component public class Dog implements InitializingBean, DisposableBean { public Dog() { System.out.println("-- Dog 构造方法"); } // 在Bean初始化的时候执行,可以操作Bean的属性 @Override public void afterPropertiesSet() throws Exception { System.out.println("-- Dog 的 afterPropertiesSet"); } @Override public void destroy() throws Exception { System.out.println("-- Dog 的 destroy"); } }
4、通过 Spring的 BeanPostProcessor (bean的后置处理器 )会拦截所有bean创建过程;(可以用来操作bean的属性)
postProcessBeforeInitialization 在init方法之前调用; postProcessAfterInitialization 在init方法之后调用;@Configuration @ComponentScan(basePackages = {"com.yufeng.testbeanlifecycle.my"}) public class MyConfig { }
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor...postProcessBeforeInitialization:"+beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor...postProcessAfterInitialization:"+beanName); return bean; } }
@Component public class Dog implements InitializingBean, DisposableBean { public Dog() { System.out.println("-- Dog 构造方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("-- Dog 的 afterPropertiesSet"); } @Override public void destroy() throws Exception { System.out.println("-- Dog 的 destroy"); } }
启动后结果如下:
六、自动装配
1、@Autowired
配置类:
@Configuration @ComponentScan(basePackages = {"com.yufeng.testautowired.autowired.my"}) public class MainConfig { @Bean public Car car() { return new Car(); } @Bean public Car car2() { return new Car("car2-----noise"); } }
实体类:
public class Car { private String noise; public Car() {} public Car(String noise) { this.noise = noise; } // getter、 setter、toString方法 }
Service类:
@Service public class MyService { @Autowired private Car car2; public void aa() { System.out.println("--- test: " + car2); } }
启动类:
public class MainDemo { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); MyService myService = (MyService) ctx.getBean("myService"); myService.aa(); } }
运行结果:--- test: Car{noise='car2-----noise'}
结论:
(1)@Autowired 首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配。
(2)如果我们需要指定特定的组件来进行装配,我们可以通过使用 @Qualifier("car") 来指定装配的组件,或者在配置类上的 @Bean 加上 @Primary注解;
注意:@Qualifier 优先级高于 @Primary;
(3)@Autowired 可以标注在方法上、构造方法上、配置类的方法入参上;
a. @Qualifier 使用
@Service public class MyService { @Autowired @Qualifier("car") private Car car2; public void aa() { System.out.println("--- test: " + car2); } }
运行结果:--- test: Car{noise='null'}
b. @Primary
@Configuration @ComponentScan(basePackages = {"com.tuling.testautowired.autowired.my"}) public class MainConfig { @Bean public Car car() { return new Car(); } @Bean @Primary public Car car2() { return new Car("car2-----noise"); } }
2、@Resource (JSR250规范)
@Resource的作用相当于@Autowired,只不过@Autowired按照 byType自动注入,@Resource默认按照 byName 进行装配。
@Resource装配顺序:
- ①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
- ②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- ③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- ④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
3、@InJect(JSR330规范),需要导入jar包依赖;
功能和支持@Primary功能 ,但是没有Require=false的功能;
@Inject 没有 required 属性,因此在找不到合适的依赖对象时 inject 会失败,而 @Autowired 可以使用 required=false 来允许 null 注入。
<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
七、通过@Value +@PropertySource来给组件赋值
配置类:
@Configuration
@PropertySource(value = {"classpath:my.properties"})
public class MainConfig {
@Bean
public Person person() {
return new Person();
}
}
配置文件 my.properties:
person.lastName=吹雪
实体类:
public class Person { @Value("西门") private String firstName; @Value("#{28-2}") private Integer age; @Value("${person.lastName}") private String lastName; // getter 和 setter 的方法 }
运行结果:Person{firstName='西门', age=26, lastName='吹雪'}
八、自定义组件需要使用Spring IOC中的Bean,可以通过实现 XXXAware 接口来实现
实现 ApplicationContextAware 和 BeanNameAware 接口:
@Component
public class CustomCompent implements ApplicationContextAware,BeanNameAware {
private ApplicationContext applicationContext;
@Override
public void setBeanName(String name) {
System.out.println("current bean name is :【"+name+"】");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
九、通过@Profile注解 来根据环境来激活标识不同的Bean
@Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效;
@Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活没有标志为@Profile的 bean 不管在什么环境都可以被激活;
配置类:
@Configuration @PropertySource(value = {"classpath:ds.properties"}) public class MainConfig implements EmbeddedValueResolverAware { @Value("${ds.username}") private String userName; @Value("${ds.password}") private String password; private String jdbcUrl; private String classDriver; @Override public void setEmbeddedValueResolver(StringValueResolver resolver) { this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}"); this.classDriver = resolver.resolveStringValue("${ds.classDriver}"); } @Bean @Profile(value = "test") public DataSource testDs() { return buliderDataSource(new DruidDataSource()); } @Bean @Profile(value = "dev") public DataSource devDs() { return buliderDataSource(new DruidDataSource()); } @Bean @Profile(value = "prod") public DataSource prodDs() { return buliderDataSource(new DruidDataSource()); } private DataSource buliderDataSource(DruidDataSource dataSource) { dataSource.setUsername(userName); dataSource.setPassword(password); dataSource.setDriverClassName(classDriver); dataSource.setUrl(jdbcUrl); return dataSource; } }
配置文件 ds.properties :
ds.username=root ds.password=123456 ds.jdbcUrl=jdbc:mysql://localhost:3306/test ds.classDriver=com.mysql.jdbc.Driver
启动类:
public class MainClass { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().setActiveProfiles("test","dev"); ctx.register(MainConfig.class); ctx.refresh(); for(String beanName: ctx.getBeanDefinitionNames()) { System.out.println("容器中的BeanName:"+beanName); } } }
结果: