Spring-注解驱动

组件注册:

1.定义配置类(相当于配置文件)

@Configuration//告诉Spring这是配置类(配置文件)
public class MainConfig {
    //    将组件注册到容器中
    @Bean()//组件类型就是方法返回值类型,组件id默认就是方法名
    public Person person() {
        return new Person("zh", 21);
    }
}

2.包扫描

/**
 *  @ComponentScans(value={
 *  @ComponentScan(value = "com.zh",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class, Repository.class})},
 *  useDefaultFilters = false)
 *  })
 * 自动包扫描:
 * @ComponentScan value:指定要扫描的包
 * excludeFilters =Filter[]:配置过滤规则
 * includeFilters=Filter[]:配置只包含规则
 * useDefaultFilters = false取消默认规则(扫描所有)
 *FilterType.ANNOTATION:按照注解
 *FilterType.ASSIGNABLE_TYPE:按照给定类型
 *FilterType.ASPECTJ:使用ASPECTJ表达式
 *FilterType.REGEX:使用正则指定
 *FilterType.CUSTOM:使用自定义规则
 */
@ComponentScan(value = "com.zh", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class, Repository.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)},
        useDefaultFilters = false)
@Configuration
public class MainConfig {
    @Bean()
    public Person person() {
        return new Person("zh", 21);
    }
}

3.组件作用域、懒加载

@Configuration
public class MainConfig2 {
    /**
     * @Scope:设置组件作用域
     * singleton:单实例,容器启动时创建对象
     *            以后每次获取都是直接从容器中获取(t同一个对象)
     * prototype:多实例,从容器中获取时,创建对象
     *            每次获取的时候才会调用方法创建
     * request:同一个请求创建一个实例
     * session:同一个session创建一个实例
     *
     * 懒加载:
     *      单实例bean:默认在容器启动时创建对象
     *      懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
     */
    @Scope()
    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("person组件创建成功");
        return  new Person("zh2",12);
    }
}

4.@Conditional

LinuxCondition.class

public class LinuxConditional implements Condition {
    /**
     * ConditionContext:判断条件能使用的上下文环境
     * AnnotatedTypeMetadata:注释信息
     */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //1.获取刀ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //2.获取类加载器
        ClassLoader classLoader = conditionContext.getClassLoader();
        //3.获取bean定义的注册类
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
        //可以判断容器中bean的注册情况,也可以给容器中注册bean
        boolean person = registry.containsBeanDefinition("Person");
        //4.获取当前环境信息
        Environment environment = conditionContext.getEnvironment();
        if (environment.getProperty("os.name").contains("linux")) {
            return true;
        }
        return false;
    }
}

WindowConditional.class

public class WindowsConditional implements Condition {
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        if (environment.getProperty("os.name").contains("Windows")){
            return true;
        }
        return false;
    }
}

1.加到类上时,满足条件,容器中的所有组件才会注册

@Conditional({WindowsConditional.class})
@Configuration
public class MainConfig3 {
    @Bean("person")
    public Person person01() {
        return new Person("zh", 12);
    }
}

2.加到方法上时,根据条件判断,该组件是否能注册到容器中

@Configuration
public class MainConfig3 {
    @Bean("person")
    public Person person01() {
        return new Person("zh", 12);
    }
    
    @Conditional({WindowsConditional.class})
    @Bean("bill")
    public Person person02() {
        return new Person("bill", 64);
    }

    @Conditional(LinuxConditional.class)
    @Bean("linux")
    public Person person03() {
        return new Person("linux", 54);
    }

}

3.测试

@Test
public  void testConditional(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
   //动态获取环境变量的值:Windows 10
    String property = context.getEnvironment().getProperty("os.name");
    System.out.println(property);
    String[] beanNamesForType = context.getBeanNamesForType(Person.class);
    for (String name : beanNamesForType) {
        System.out.println(name);
    }
    Map<String, Person> beansOfType = context.getBeansOfType(Person.class);
    System.out.println(beansOfType);
}

5.@Import(快速注册组件)

/**
 *容器中注册组件:
 * 1.包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
 *         (只适用于自己写的类)
 * 2.@Bean(导入第三方报的组件)
 * 3.@Import[快速给容器导入一个组件]
 *  1.@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名
 *  2.ImportSeletor:返回需要导入的组件的全类名数组
 *  3.ImportBeanDefinitionRegistrar:手动注册bean到容器中
 */

1.@Import

@Import({Red.class, Blue.class, MyImportSeletor.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig3 {
    @Bean("person")
    public Person person01() {
        return new Person("zh", 12);
    }

    /**
     * @Conditional:按照一定的条件进行判断,满足条件给容器中注册bean
     * 如果系统是windows
     */

    @Conditional({WindowsConditional.class})
    @Bean("bill")
    public Person person02() {
        return new Person("bill", 64);
    }

    @Conditional(LinuxConditional.class)
    @Bean("linux")
    public Person person03() {
        return new Person("linux", 54);
    }

}

2.ImportSeletor

//自定义逻辑需要返回的组件
public class MyImportSeletor implements ImportSelector {
//    AnnotationMetadata:当前标注@Import注解的类的所有注解信息
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.zh.pojo.Purple","com.zh.pojo.Pink"};
    }
}

3.ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata:当前类注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类
     * 把所有需要添加到容器中的bean:调用
     * BeanDefinitionRegistry.registerBeanDefinition手工注册进来
     */
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        boolean definition = beanDefinitionRegistry.containsBeanDefinition("com.zh.pojo.Red");
        boolean definition2 = beanDefinitionRegistry.containsBeanDefinition("com.zh.pojo.Blue");
        if (definition && definition2) {
           //指定bean定义信息(Bean的类型)
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RawBall.class);
            //注册一个bean,指定bean名
            beanDefinitionRegistry.registerBeanDefinition("rawBall", rootBeanDefinition);
        }
    }
}

6.使用Spring提供的FactoryBean(工厂bean)

1.默认获取到的是工厂bean调用getObject创建的对象

2.要获取工厂bean本身,给id前面加&

context.getBean("&colorFactoryBean")

public class ColorFactoryBean implements FactoryBean<Color> {
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean....getObject");
        return new Color();
    }

    public Class<?> getObjectType() {
        return Color.class;
    }
//是否为单例
    public boolean isSingleton() {
        return false;
    }
}

Bean的生命周期:

bean创建==初始化=销毁的过程

容器管理bean的生命周期:

1.自定义初始化和销毁方法

容器在bean进行到当前生命周期的时候来调用自定义初始化和销毁方法

指定初始化和销毁方法

@Bean(initMthod="init",destroyMethod="destroy")

单实例:

1.创建:在容器启动时创建对象

2.初始化:对象创建完成,并赋值好,调用初始化方法

3.销毁:容器关闭时

多实例:

1.创建:在每次获取时创建对象

2.初始化:对象创建完成,并赋值好,调用初始化方法

3.销毁:容器不会管理这个bean,容器不会调用销毁方法

2.通过让bean实现InitializingBean(定义初始化逻辑)

​ DisposableBean(定义销毁逻辑)

3.可以使用JSR250:

@PostConstruct:在bean创建完成并且属性赋值完成:来执行初始化方法

@PreDestroy:在容器销毁bean之前通知我们进行清理工作

4.BeanPostProcessor[interface]:bean的后置处理器

在bean初始化前后进行一些处理工作

postProcessBeforeInitialization:在初始化之前工作

postProcessAfterInitialization:在初始化之后工作

调用构造方法=》初始化之前(后置处理器)》初始化=》初始化之后(后置处理器)==》销毁

/**
 * 后置处理器:初始化前后进行处理工作
 * 写好后加在容器中
 */
@Component
public class myBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization...."+beanName+"==>"+bean);
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization...."+beanName+"==>"+bean);
         return bean;
    }
}

组件赋值:

@Value

/**
 * 使用@Value赋值
 * 1.基本数值
 * 2.SPEL表达式:#{}
 * 3.引用外部配置文件(properties)中的值(在运行环境变量里面的值)
 */
public class Person {
    @Value("ZH")
    private String name;
    @Value("#{19+2}")
    private Integer age;
    @Value("${person.nickName}")
    private String nick;
    }

@Autowired

自动装配:
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值@AutoWired:自动注入
1.默认优先按照类型去容器中查找相应的组件:context.getBean(BookDao.class)
2.如果找到多个相同类型的组件,再将属性名作为组件id在容器中查找
3.@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件id
4.自动装配默认一定要将属性赋值,找不到组件将报错
可以使用@AutoWired(required=false)
5.@Primary:让Spring进行自动装配时,默认使用首选的bean
也可以继续使用@Qualifier指定需要装配的bean的名字

MainAutoWiredConfig:

@Configuration
@ComponentScan({"com.zh.controller","com.zh.service","com.zh.dao","com.zh.pojo"})
public class MainAutoWired {

@Bean("bookDao2")
@Primary
    public BookDao bookDao(){
    BookDao bookDao = new BookDao("3");
    bookDao.setLable("3");
//    return new BookDao("3");
	 return bookDao;
}

pojo.BookDao

//名字默认为小写
@Repository
public class BookDao {
   private String lable="1";

    public String getLable() {
        return lable;
    }

    public BookDao() {
    }

    public void setLable(String lable) {
        this.lable = lable;
    }

    public BookDao(String lable) {
        this.lable = lable;
    }

    @Override
    public String toString() {
        return "BookDao{" +
                "lable='" + lable + '\'' +
                '}';
    }
}

BookService:

@Service
public class BookService {
   @Autowired
   @Qualifier("bookDao")
// @Resource
//@Inject
    private BookDao bookDao;

    public  void  getBookDao(){
       System.out.println(bookDao);
   }
}

@Resource

/**@Resource:
 *     可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装
 *配的
 *不能支持@Primary和@Autowired(required=false)
*/
@Service
public class BookService {
   @Qualifier("bookDao")
   @Resource
    private BookDao bookDao;

    public  void  getBookDao(){
       System.out.println(bookDao);
   }
}

@Inject

/**@Inject:
*	需要导入javax.inject的包,和Autowired的功能一样,可以使用*@Primary,不支持required=false
*
*/
@Service
public class BookService {
   @Autowired
   @Qualifier("bookDao")
// @Resource
//@Inject
    private BookDao bookDao;

    public  void  getBookDao(){
       System.out.println(bookDao);
   }
}

@ProperSource

@PropertySource/@ProperSources(引入外部配置文件)

/**
 *使用@PropertySouece读取外部配置文件中的k/v保存到运行的环境变量中,加载完外部配置文件后使用${}取出配置文件中的值
 */
@PropertySource(value = "person.properties")
@Configuration
public class MainConfigOfProperty {
 @Bean
    public Person person(){
     return new Person();
 }
}
public class Person {
	//获取外部文件的值
    @Value("${person.nickName}")
    private String nick;
}

@Profile

@Profile:Spring提供的可以根据当前环境,动态的激活和切换一系列组件的功能
开发环境、测试环境、生产环境
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
1.加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
2.写在配置类上,只有是指定的环境的时候,整个将配置类里的所有组件才会生效
3.没有标注环境局标识的bean,任何环境下都是加载的

1.代码方式

MainConfigOfProfile

@Profile("dev")
@Configuration
@PropertySource("db.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
  @Bean
  @Profile("test")
    public Color color(){
        return new Color();
    }

    @Value("${data.userName}")
    private String userName;
    private String driver;

    @Profile("test")
    @Bean("test")
    public DataSource dataSourceTest(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(userName);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
        dataSource.setDriverClass(driver);
        return dataSource;
    }
@Profile("dev")
    @Bean("dev")
    public DataSource dataSourceDev(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(userName);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
        dataSource.setDriverClass(driver);
        return dataSource;
    }
@Profile(("pro"))
    @Bean("pro")
    public DataSource dataSourcePro(@Value("${data.pwd}") String pwd) throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(userName);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/goods");
        dataSource.setDriverClass(driver);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        String s = stringValueResolver.resolveStringValue("${data.driver}");
        this.driver = s;
    }
}

test

/**
*代码方式激活某种环境
*	public AnnotationConfigApplicationContext(Class<?>... *annotationClasses){
*			this();
*			this.register(annotatedClasses);
*			this.refresh();
*}
*/
//1.创建一个applicationContext
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext();
//2.设置需要激活的环境
context.getEnvironment().setActiveProfiles("pro","dev")
//3.注册配置类
context.register(MainConfigOfProfile.class);
//4.启动刷新容器
context.refresh();
//5.获取特定环境中的bean
String[] names=context.getBeanDefinitionNames();
for(String name:names){
    System.out.println(name);
}

2.命令行参数方式

在虚拟机参数位置加装-Dspring.profiles.active=test

组件注入

@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

1.【标注在方法位置】@Bean+方法参数:参数从容器中获取,默认不写@Autowired

2.【放在构造器上】如果组件只有一个有参构造器,容器创建对象只能通过有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件可以自动装配

3,放在参数位置

//默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
@Component
public class Boss {
//    @Autowired
    private Car car;

    public Car getCar() {
        return car;
    }

    //构造器要用组件,都从容器中获取
//    @Autowired
    public Boss(Car car) {
        this.car = car;
        System.out.println("调用有参构造器");
    }
//    @Autowired
    //标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值
    //方法使用的参数,自定义类型的值从ioc容器中获取
    public void setCar(Car car) {
        this.car = car;
    }
  }

自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFacrory,xxx)

自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware

把Spring底层一些组件注入到自定义的Bean中

xxxAware:功能使用xxxProcessor:
.	ApplicationContextAware====>ApplicationContextAwareProcessor
posted @ 2021-04-26 11:31  Zh'Blog  阅读(124)  评论(0编辑  收藏  举报