一、@Bean
1、配置類
@Configuration public class MainConfig { @Bean public Person person(){ return new Person(); } }
注意:通過@Bean的形式是使用的話, bean的默認名稱是方法名,可以使用 @Bean(value="bean的名稱") 去指定bean的名稱;
2、測試類:
public class MainClass { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); //Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
System.out.println(ctx.getBean("person")); } }
二、@ComponentScan
在配置類上增加 @ComponentScan 注解,來進行包掃描;
@ComponentScan 注解 配合 @Controller、@Service、@Component、@Reposity 使用,將類注冊到IOC容器中;
1、basePackages 包掃描的路徑
@Configuration //掃描com.yufeng.componentscan包下的所有
@ComponentScan(basePackages = {"com.yufeng.componentscan"}) public class MainConfig { }
2、excludeFilters,排除
(a)FilterType.ANNOTATION : 排除注解
(b)FilterType.ASSIGNABLE_TYPE:排除具體的類
@Configuration //掃描com.yufeng.componentscan包下的所有, 排除有@Controller注解的類, 排除類TuService
@ComponentScan(basePackages = {"com.yufeng.componentscan"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {TuService.class})}) public class MainConfig { }
(c)FilterType.CUSTOM:自定義規則去排除
@Configuration //掃描com.yufeng.componentscan包下的所有, 按照自定義規則排除
@ComponentScan(basePackages = {"com.yufeng.componentscan"}, excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {MyTypeFilter.class})}) public class MainConfig { }
自定義的規則(實現 TypeFilter 接口):類名中包含dao的需要排除
public class MyTypeFilter 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; //返回true, 則需要去過濾掉, 所以類名中包含dao的類不會被加載到IOC容器中
} return false; } }
3、includeFilters 包含,在包掃描的當前路徑下只加載 includeFilters 包含的,其他的不加載;
注意:需要把 useDefaultFilter 屬性設置為 false (true表示全表掃描)
@Configuration //--------------包含 includeFilters -------------
@ComponentScan(basePackages = {"com.yufeng.componentscan"}, includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {Person.class})}, useDefaultFilters = false) public class MainConfig{ }
4、測試類
public class MainClass { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); Arrays.stream(ctx.getBeanDefinitionNames()) .forEach(name -> System.out.println("bean的自定義: " + name)); ctx.close(); } }
三、配置Bean的作用域對象
1、在不指定 @Scope 的情況下,所有的bean都是單實例的bean,而且是餓漢加載(容器啟動實例就創建好了)
@Bean public Person person() { return new Person(); }
2、指定@Scope為 prototype 表示為多實例的,而且還是懶漢模式加載(IOC容器啟動的時候,並不會創建對象,而是在第一次使用的時候才會創建)
@Bean @Scope(value = "prototype") public Person person() { return new Person(); }
3、@Scope指定的作用域方法取值
(a)singleton 單實例的(默認)
(b)prototype 多實例的
(c)request 同一次請求
(d)session 同一個會話級別
單實例的Bean要實現懶加載,可以使用 @Lazy 注解 (主要針對單實例的bean 容器啟動的時候,不創建對象,在第一次使用的時候才會創建該對象)
@Bean @Lazy public Person person() { return new Person(); }
四、@Conditional 進行條件判斷
有二個組件TulingAspect 和 TulingLog ,我的TulingLog組件是依賴於TulingAspect的組件
自定義加載條件的類 TulingCondition,該類 實現Condition接口
public class MyCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory(); //判斷容器中是否有tulingAspect的組件
if(beanFactory.containsBean("tulingAspect")) { return true; } return false; } }
配置類的配置
@Configuration public class ConditionConfig { //當前容器中有tulingAspect的組件,那么tulingLog才會被實例化
@Bean @Conditional(value = MyCondition.class) public TulingLog tulingLog() { return new TulingLog(); } @Bean public TulingAspect tulingAspect() { return new TulingAspect(); } }
運行結果如下:
注意:@Configuration 配置類中的 Bean 加載是有順序的, 越在前越更早的被加載;
五、往IOC 容器中添加組件的方式
1、通過 @CompentScan + @Controller @Service @Respository @compent
適用場景: 針對我們自己寫的組件可以通過該方式來進行加載到容器中。
2、通過 @Bean 的方式來導入組件(適用於導入第三方組件的類)
3、通過 @Import 來導入組件 (導入Bean的id為全類名路徑)
(1)方式一: @Import(value = {Person.class, Car.class})
配置類:
@Configuration @Import(value = {Person.class, Car.class}) public class MainConfig { }
運行結果:
(2)方式二:按照全類名導入,通過@Import 的 ImportSeletor 類 (導入Bean的id為全類名路徑)
實現 ImportSelector 接口,按照類的全路徑名導入
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{"com.yufeng.importanno.component.Dog"}; } }
配置類:
@Configuration //@Import(value = {Person.class, Car.class})
@Import(value = {Fish.class, MyImportSelector.class}) public class MainConfig { }
運行結果
(3)方式三: 通過 @Import 導入 ImportBeanDefinitionRegister 的實現類 (可以指定bean的名稱)
實現 ImportBeanDefinitionRegister 接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) { //創建一個bean定義對象
RootBeanDefinition beanDefinition = new RootBeanDefinition(Car.class); //把bean定義對象導入到容器中
beanDefinitionRegistry.registerBeanDefinition("car", beanDefinition); } }
配置類
@Configuration //@Import(value = {Person.class, Car.class}) //@Import(value = {Fish.class, MyImportSelector.class})
@Import(value = {MyImportSelector.class, MyImportBeanDefinitionRegistrar.class, Person.class}) public class MainConfig { }
運行結果
4、通過實現 FacotryBean 接口來實現注冊組件
實現 FactoryBean 接口,重寫 getObject、getObjectType、isSingleton 方法;
public class CarFactoryBean implements FactoryBean { //返回bean的對象
@Override public Car getObject() throws Exception { return new Car(); } //返回bean的類型
@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 Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); Arrays.stream(ctx.getBeanDefinitionNames()) .forEach(name -> System.out.println("bean名字: " + name));
System.out.println("--------------"); Object bean = ctx.getBean("carFactoryBean"); System.out.println(bean.getClass().getName()); System.out.println("--------------"); Object bean2 = ctx.getBean("&carFactoryBean"); System.out.println(bean2.getClass().getName()); } }
運行結果
結論:
(1)實現 FactoryBean 接口的類,注冊到IOC容器中,使用 bean名稱 獲取到的是Bean的 getObject 方法返回的對象;
(2)實現 FactoryBean 接口的類,注冊到IOC容器中,要想獲取到Bean本身,則在 bean名稱 之前加一個 & ;
六、Bean的初始化和銷毀方法
什么是bean的生命周期?
bean的創建----->初始化----->銷毀方法
由容器管理Bean的生命周期,我們可以通過自己指定bean的初始化方法和bean的銷毀方法?
1、方法一:使用 @Bean 注解的 initMethod 和 destroyMethod
(1)組件:
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() 方法------"); } }
(2)配置類,使用 @Bean 注解的 initMethod 和 destroyMethod
@Configuration public class MainConfig { @Bean(initMethod = "init", destroyMethod = "destroy") public Car car() { return new Car(); } }
(3)測試類
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); /*Arrays.stream(ctx.getBeanDefinitionNames()) .forEach(name -> System.out.println("bean名字: " + name));*/ ctx.close(); } }
運行結果:
注意:
(1)針對單實例bean的話,容器啟動的時候,bean的對象就創建了,而且容器銷毀的時候,也會調用Bean的銷毀方法;
(2)針對多實例bean的話,容器啟動的時候,bean是不會被創建的而是在獲取bean的時候被創建,而且bean的銷毀不受 IOC容器的管理;
2、通過 實現 InitializingBean 和 DisposableBean 接口,並重寫bean的初始化以及銷毀方法 ;
(1)組件實現 InitializingBean 和 DisposableBean 接口
@Component public class Person implements InitializingBean, DisposableBean { public Person() { System.out.println("--Person的構造方法--"); } @Override public void destroy() throws Exception { System.out.println("Person DisposableBean的destroy()方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("Person InitializingBean的 afterPropertiesSet方法"); } }
(2)配置類
@Configuration @ComponentScan(basePackages = "com.yufeng.beanlifecyle") public class MainConfig { @Bean(initMethod = "init", destroyMethod = "destroy") public Car car() { return new Car(); } }
運行結果:
3、通過JSR250規范 提供的注解@PostConstruct 和@ProDestory標注的方法
(1)組件
@Component public class Book { public Book() { System.out.println("Book的構造方法"); } @PostConstruct public void myInit(){ System.out.println("Book 的標注 @PostConstruct 的方法"); } @PreDestroy public void myDestroy() { System.out.println("Book 的標注 @PreDestroy 的方法"); } }
運行結果:
4、通過Spring的 BeanPostProcessor 的 bean的后置處理器會攔截所有bean創建過程
(1)postProcessBeforeInitialization 在 init 方法之前調用
(2)postProcessAfterInitialization 在 init 方法之后調用
實現 BeanPostProcessor 接口
@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; } }
運行結果
七、通過@Value +@PropertySource來給組件賦值
配置類
@Configuration @PropertySource(value = {"classpath:person.properties"}) //指定外部文件的位置
public class MainConfig { @Bean public Person person() { return new Person(); } }
組件
public class Person { //通過普通的方式
@Value("司馬") private String firstName; //spel方式來賦值
@Value("#{28-8}") private Integer age; //通過讀取外部配置文件的值
@Value("${person.lastName}") private String lastName; }
八、自動裝配
1、@AutoWired的使用
(1)@AutoWired:
自動裝配首先時按照類型進行裝配,若在IOC容器中發現了多個相同類型的組件,那么就按照 屬性名稱 來進行裝配 ;
比如,容器中有二個 TulingDao 類型的組件 一個叫 tulingDao 一個叫 tulingDao2,
那么我們通過@AutoWired 來修飾的屬性名稱為tulingDao 時,那么那就加載容器的tulingDao組件,若屬性名稱為 tulignDao2 那么他就加載的時tulingDao2組件。
(2)@AutoWired 和 @Qualifier(value) 配合使用
假設我們需要指定特定的組件來進行裝配,我們可以通過使用 @Qualifier("tulingDao") 來指定裝配的組件或者在配置類上的 @Bean 加上 @Primary 注解
@Autowired @Qualifier("tulingDao") private TulingDao tulingDao2;
注意:若容器中既沒有 tulingDao也沒有 tulingDao2 時, 那么在裝配的時候就會拋出異常 ;
若我們想不拋異常 ,我們需要指定 required為false的時候可以了:@Autowired(required = false)
2、@Resource 使用 (JSR250規范)
功能和 @AutoWired 的功能差不多一樣,但是 不支持@Primary 和@Qualifier的支持 ,由J2EE提供,需要導入包javax.annotation.Resource。
@Resource 默認按照ByName自動注入,@Resource有兩個重要的屬性:name和type,而Spring將@Resource注解的name屬性解析為bean
的名字,而type屬性則解析為bean的類型。所以,
如果使用name屬性,則使用byName的自動注入策略;
如果使用type屬性時則使用byType自動注入策略。
如果既不指定 name也不制定type屬性,這時將通過反射機制使用byName自動注入策略。
3、@InJect(JSR330規范)
需要導入jar包依賴
功能和支持 @Primary 功能 ,但是沒有 Require=false 的功能
@Inject 默認 By type 自動注入, 可以 通過@Qualifier 顯式指定 ByName 注入。
@Autowired:Spring定義的;@Resource,@inject都是java規范。
九、通過實現XXXAware接口的實現,來從IOC容器中獲取 Bean
我們自己的組件,需要使用spring ioc的底層組件的時候, 比如 ApplicationContext等,我們可以通過實現XXXAware接口來實現