文章目录
1 配置组件(Configure Components)
注解名称 | 说明 |
---|---|
@Configuration | 把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean |
@ComponentScan | 在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的context:component-scan |
@Scope | 用户指定scope 作用域的 ,用于类上 |
@Lazy | 表示延迟初始化 |
@Conditional | Spring4 开始提供的,作用:按照一定条件进行判断,满足条件则给IoC容器注册Bean |
@Import | 导入外部资源 |
生命周期控制 | 1. @PostConstruct :用于指定初始化方法(用于方法上) 2. @PreDestory :用于指定注销方法(方法上)3. @DependsOn:定义Bean初始化及注销时的顺序 |
@Configuration 注解
从 Spring3.0 ,@Configuration用于定义 配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,并用于构建bean定义,初始化Spring容器。
注意:@Configuration注解的配置类有如下要求:
- @Configuration不可以是final类型;
- @Configuration不可以是匿名类;
- 嵌套的configuration必须是静态类。
## myconfig class
import com.gupaoedu.project.entity.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public Person person(){
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
}
## myconfig class
## mytest class
import com.gupaoedu.project.entity.Person;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test(){
//Ioc,控制反转
ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
Object bean1 = app.getBean("person");
Object bean2 = app.getBean("person");
// 由于 == 结果为 true
Person bean = app.getBean(Person.class);
System.out.println(bean1 ==bean2);
System.out.println(bean == bean1);
}
}
## mytest class
## myentity class
import org.springframework.stereotype.Component;
@Component
public class Person {
private String name;
private int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
}
## myentity class
@ComponentScan 注解
@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中
相当于之前的 context:component-scan
扫描到文件中带有@Service,@Component,@Repository,@Controller等这些注解的类
好下面咱们就先来简单演示一下这个例子
## 在包 controller下新建一个MyController带@Controller注解如下:
@Controller
public class MyController {
@Autowired
private MyService myService;
}
## 在包 service下新建一个MyController带@Service注解如下:
@Service
public class MyService {
}
## 在包 dao下新建一个MyController带@Repository注解如下:
1、myDao类:
@Repository
public class MyDao {
}
2、Person
public class Person {
}
## 新建一个配置类如下:
@Configuration
//将value中的目录下的文件扫描出来
@ComponentScan(
value = "com.xxx.project")
public class MyConfig {
}
## 新建测试方法如下:
public class MyTest {
@Test
public void test(){
//Ioc,控制反转
ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
String [] beanNames = app.getBeanDefinitionNames();
System.out.println(Arrays.toString(beanNames).replace("\\[|\\]","").replace(",","\n"));
}
}
## 结果
myConfig、 myController、 myDao、 myService 都会被扫描到,parson却没有,因为没有添加注解,如果想要parson也扫描到IoC容器中,只需要在类上添加一个 @Component 注解即可
上面只是简单的介绍了@ComponentScan注解检测包含指定注解的自动装配,接下来让我们来看看@ComponentScan注解的更加详细的配置,在演示详细的配置之前,让我们先看看@ComponentScan的源代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
* 对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
* @return
*/
@AliasFor("basePackages")
String[] value() default {};
/**
* 和value一样是对应的包扫描路径 可以是单个路径,也可以是扫描的路径数组
* @return
*/
@AliasFor("value")
String[] basePackages() default {};
/**
* 指定具体的扫描的类
* @return
*/
Class<?>[] basePackageClasses() default {};
/**
* 对应的bean名称的生成器 默认的是BeanNameGenerator
* @return
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* 处理检测到的bean的scope范围
*/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
/**
* 是否为检测到的组件生成代理
* Indicates whether proxies should be generated for detected components, which may be
* necessary when using scopes in a proxy-style fashion.
* <p>The default is defer to the default behavior of the component scanner used to
* execute the actual scan.
* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver}.
* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
*/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
/**
* 控制符合组件检测条件的类文件 默认是包扫描下的 **/*.class
* @return
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
/**
* 是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的
* @return
*/
boolean useDefaultFilters() default true;
/**
* 指定某些定义Filter满足条件的组件 FilterType有5种类型如:
* ANNOTATION, 注解类型 默认
ASSIGNABLE_TYPE,指定固定类
ASPECTJ, ASPECTJ类型
REGEX,正则表达式
CUSTOM,自定义类型
* @return
*/
Filter[] includeFilters() default {};
/**
* 排除某些过来器扫描到的类
* @return
*/
Filter[] excludeFilters() default {};
/**
* 扫描到的类是都开启懒加载 ,默认是不开启的
* @return
*/
boolean lazyInit() default false;
}
a.演示basePackageClasses参数,如我们把配置文件改成如下:
### 扫包路径改为了 com.xxx.project.dao , dao层,只有myDao外加basePackageClasses指定的myService 加入到了spring容器中
@ComponentScan(value="com.xxx.project.dao",useDefaultFilters=true,basePackageClasses=myService.class)
@Configuration
public class MainScanConfig {
}
结果如下:
myConfig
myDao
myService
b.演示includeFilters参数的使用如下:
在service 层下新建一个myService2类如下:注意没有带@Service注解
public class myService2{
}
##配置类改成:
@ComponentScan(value="com.zhang",useDefaultFilters=true,
includeFilters={
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={myService2.class})
})
@Configuration
public class myconfig{
}
##结果为:
myConfig
myController
myDao
myService
myService2
##myService2同样被加入到了spring容器
c.自定义的实现了TypeFilter的MyTypeFilter类如下:
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader 获取当前正在操作的类的信息
* @param metadataReaderFactory 获取上下文中所有的信息
* @return
* @throws IOException
*/
@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+"---------");
//获取类名中包含er的
if(className.contains("er")){
System.out.println("++++++++++"+className+"++++++++++");
return true;
}
return false;
}
}
##修改主配置如下:
@Configuration
//将value中的目录下的文件扫描出来
@ComponentScan(
value = "com.gupaoedu.project",
includeFilters = {@Filter( type = FilterType.CUSTOM,value = {MyTypeFilter.class})},// 扫描自定义规则的类
useDefaultFilters=false
)
public class MyConfig {
}
结果:
myConfig
myController
myService
myService2
##将类名中不带有er字符串的bean过滤掉了
@Scope注解
@Scope注解的目的是用来调节作用域
@Scope(“prototype”)//多实例,IOC容器启动创建的时候,并不会创建对象放在容器在容器当中,当你需要的时候,需要从容器当中取该对象的时候,就会创建。
@Scope(“singleton”)//单实例 IOC容器启动的时候就会调用方法创建对象,以后每次获取都是从容器当中拿同一个对象(map当中)。
@Scope(“request”)//同一个请求创建一个实例
@Scope(“session”)//同一个session创建一个实例
例如:
@Configuration
public class MyConfig {
/**
* scope注解
* prototype 原型,多例
* singleton 单例 默认
* request 主要应用于webm模块,同一个请求只创建一个实例
* session 主要应用于web模块,同一个session值创建一个对象
*
* @return
*/
@Scope("prototype")
@Bean
public Person person(){
return new Person("tom",19);
}
}
@Lazy 注解
Spring IoC (ApplicationContext) 容器一般都会在启动的时候实例化所有单实例 bean 。如果我们想要 Spring 在启动的时候延迟加载 bean,即在调用某个 bean 的时候再去初始化,那么就可以使用 @Lazy 注解。
value 取值有 true 和 false 两个 默认值为 true
true 表示使用 延迟加载, false 表示不使用,false 纯属多余,如果不使用,不标注该注解就可以了。
@Lazy注解注解的作用主要是减少springIOC容器启动的加载时间
当出现循环依赖时,也可以添加@Lazy
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
System.out.println(" 对象被创建了.............");
this.name = name;
this.age = age;
}
// 省略 getter setter 和 toString 方法
}
##配置类:
@Configuration
public class MyConfig {
@Lazy
@Bean
public Person person(){
System.out.println("将对象添加到IOC容器中");
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
}
## 测试类:
public class MyTest {
@Test
public void test(){
//Ioc,控制反转
ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("IoC容器初始化成功!!");
Object bean1 = app.getBean("person");
}
}
结果:
IoC容器初始化成功!!
将对象添加到IOC容器中
如果不接@Lazy则结果是:
将对象添加到IOC容器中
IoC容器初始化成功!!
@Conditional注解
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
首先创建分别创建LinuxConditional 和 WinConditional ,他们都实现Condition 接口并重写matches 方法
##WinConditional
public class WinConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
if(property.contains("Windows")){
return true;
}
return false;
}
}
##LinuxConditional
public class LinuxConditional implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
if(property.contains("Linux")){
return true;
}
return false;
}
}
##配置类:
@Configuration
public class MyConfig {
@Conditional(WinConditional.class)
@Bean
public Person tom(){
System.out.println("将对象tom添加到IOC容器中");
return new Person("jack",19);
}
@Conditional(LinuxConditional.class)
@Bean
public Person james(){
System.out.println("将对象James添加到IOC容器中");
return new Person("James",17);
}
}
##测试方法:
/**
*TODO 启动增加项:
* vm options 一栏添加:-ea -Dos.name=Linux
*/
public class MyTest {
@Test
public void test(){
//Ioc,控制反转
ApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("IoC容器初始化成功!!");
//假设操作系统是Windows ,就加载jack
//如果操作系统是Linux,那就加载James
}
}
@Import注解
import 注解,将第三方的类添加到Ioc容器中
给Ioc容器中注册Bean的方式
- @Bean 直接导入
- @ComponentScan 默认扫描(@Controller,@Dao,@service,@Repostory,@Component)
- Import 快速给容器导入Bean的方式
a. @Import 直接导入
b. 实现ImportSelector接口 自定义规则实现
c. 实现ImportBeanDefinitionRegistrar接口,获得BeanDefinitionRegistry对象,可以直接手动向IoC注入类 - FactoryBean 把需要注册的对象封装为FactoryBean
a. FactoryBean 负责把Bean对象注册到Iocd的Bean
b. BeanFactory 从Ioc中获取Bean对象
第一种用法:直接填class数组
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
@Bean
public Person person(){
System.out.println("将对象添加到IOC容器中");
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
}
第二种用法:实现ImportSelector接口 自定义规则实现
创建MyImportSelector 并实现 ImportSelector 接口,重写selectImports 方法,在返回的 字符串数组中添加需要注入的类包名即可。
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.XXX.project.entity.Company","com.XXX.project.entity.Member"};
}
}
##配置类:
需要将 MyImportSelector 输入到IoC容器中
@Configuration
@Import(value = {Cat.class,MyImportSelector.class})
public class MyConfig {
@Bean
public Person person(){
System.out.println("将对象添加到IOC容器中");
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
}
第三种用法:实现ImportBeanDefinitionRegistrar接口,获得BeanDefinitionRegistry对象,可以直接手动向IoC注入类
##创建MyImportBeanDefinitionRegistrar 并实现 ImportBeanDefinitionRegistrar 接口,重写registerBeanDefinitions方法.
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
*
* @param importingClassMetadata
* @param registry 向容器注入对象的方法
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//假设 包中如果声明了Company 和 Mamber 这两个类 ,我才把User类注入到Ioc容器中
boolean company = registry.containsBeanDefinition("com.XXX.project.entity.Company");
boolean member = registry.containsBeanDefinition("com.XXX.project.entity.Member");
if(company && member){
BeanDefinition beanDefinition = new RootBeanDefinition(User.class);
//方法说明: registerBeanDefinition 方法只是向beanDefinitionMap中put 值
registry.registerBeanDefinition("user",beanDefinition);
}
}
}
##配置类:
需要将 MyImportSelector 输入到IoC容器中
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
@Bean
public Person person(){
System.out.println("将对象添加到IOC容器中");
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
}
4.FactoryBean 把需要注册的对象封装为FactoryBean
##创建 MyFactoryBean 实现FactoryBean<T> 接口,并实现getObject() 、getObjectType() 、isSingleton() 方法
public class MyFactoryBean implements FactoryBean<Monkey> {
@Override
public Monkey getObject() throws Exception {
return new Monkey();
}
@Override
public Class<?> getObjectType() {
return Monkey.class;
}
/**
*是否是单例模式
**/
@Override
public boolean isSingleton() {
return true;
}
}
##配置类
@Configuration
@Import(value = {Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MyConfig {
@Bean
public Person person(){
System.out.println("将对象添加到IOC容器中");
/**
* 这里的new 并不是调用我们的构造方法创建的,
* 而是以此作为模板通过原型模式 创建出来的
*
*/
return new Person("tom",19);
}
@Bean
public MyFactoryBean monkey(){
return new MyFactoryBean();
}
}
生命周期控制
对Bean生命周期的监控
- 配置Bean注解参数
- 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
- 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
- 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
1. 配置Bean注解参数
## 实体类
public class Car {
public Car(){
System.out.println("调用Car的构造方法");
}
public void addOil(){
System.out.println("在行驶前加油!!");
}
public void close(){
System.out.println("停车熄火!!");
}
public void run(){
System.out.println("正在飙车");
}
}
## 配置类
@Configuration
@ComponentScan("com.gupaoedu.project.entity")
public class MyConfig {
@Lazy
@Bean(initMethod = "addOil",destroyMethod = "close")
public Car car(){
return new Car();
}
}
## 测试类
/**
* bean对象的生命周期
*结果:
* 调用Car的构造方法
* 在行驶前加油!!
* IoC容器初始化成功!!
* com.gupaoedu.project.entity.Car@33afa13b
* 停车熄火!!
*/
public class MyTest {
/**
* 对Bean生命周期的监控
* 1、 配置Bean注解参数
* 2、 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
* 3、 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
* 4、 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
*/
@Test
public void test(){
//Ioc,控制反转
AnnotationConfigApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("IoC容器初始化成功!!");
Object bean1 = app.getBean("car");
System.out.println(bean1 );
app.close();
}
}
2、分别实现InitialzingBean和DisposableBean接口 并重写destroy、afterPropertiesSet方法
## entity
//TODO [2]实现接口
@Component
public class Train implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("火车对象销毁");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("火车对象初始化");
}
}
## 配置类
@Configuration
@ComponentScan("com.gupaoedu.project.entity")//TODO [2]实现接口
public class MyConfig {
@Lazy
@Bean(initMethod = "addOil",destroyMethod = "close")
public Car car(){
return new Car();
}
}
## 测试类
/**
* bean对象的生命周期
*结果:
* 调用Car的构造方法
* 在行驶前加油!!
* IoC容器初始化成功!!
* com.gupaoedu.project.entity.Car@33afa13b
* 停车熄火!!
*/
public class MyTest {
/**
* 对Bean生命周期的监控
* 1、 配置Bean注解参数
* 2、 分别实现InitialzingBean和DisposableBean接口//TODO [2]实现接口
* 3、 使用注解@PostConstruct和@PreDestroy注解 //TODO [3] 使用注解
* 4、 自己写一个类,去实现BeanPostProcessor接口 //TODO [4] 实现BeanPostProcessor接口
*/
@Test
public void test(){
//Ioc,控制反转
AnnotationConfigApplicationContext app=new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("IoC容器初始化成功!!");
Object bean1 = app.getBean("car");
System.out.println(bean1 );
app.close();
}
}
2 赋值组件又名自动装配组件(Injection Components)
3 织入组件(Weave Components)
注解名称 | 说明 |
---|---|
ApplicationContextAware | 可以通过这个上下文环境得到Spring容器中的Bean |
BeanDefinitionRegistryPostProcessor | BeanDefinitionRegistryPostProcessor实现了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinittionRegistry的后处理器,用来注册额外的BeanDefinition |