美文网首页
Spring高级特性之注解驱动开发上篇

Spring高级特性之注解驱动开发上篇

作者: 以南之南_b9a1 | 来源:发表于2019-03-11 21:16 被阅读0次

如何你没有了解过Spring Boot和Spring Cloud 那么 这篇文章就是你了解Spring Boot和Spring Cloud 的垫脚石。

1.给容器中注册组件

加入spring-context 包

      <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.12.RELEASE</version>
        </dependency>

新建一个Class 为配置类 并且在类(称为配置类,一个为Xml 方式 ,一个为配置类的方式)上加入Configuration 告诉Spring 这个一个配置类

@Configuration
public class MainConfig {

    //给容器注册一个bean;类型为返回值得类型,Id默认为方法名称作为ID,
    @Bean
    public Person person(){
        return new Person("zhangsan",20);
    }
}

那么如何测试呢?
我们可以利用AnnotationConfigApplicationContext

   public static void main(String [] args){
        AnnotationConfigApplicationContext applicationContext =  new      AnnotationConfigApplicationContext(MainConfig.class);
        Person person  = applicationContext.getBean(Person.class);
        System.out.println(person);
     }
使用CompoentScan自动扫描组件
@ComponentScan(value = {"com.zhh"})
@Configuration
public class MainConfig {

    //给容器注册一个bean;类型为返回值得类型,Id默认为方法名称作为ID,
    @Bean
    public Person person01(){
        return new Person("zhangsan",20);
    }
}

测试代码:

public class IOCTest {

    @SuppressWarnings("resource")
    @Test
    public void testIOCTest(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String [] beanNames = applicationContext.getBeanDefinitionNames();
        for(String str:beanNames){
            System.out.println(str);
        }
//结果为:bookDao person01 mainConfig
    }
}

这里可以使用excludeFilters 排除一些bean 也可以使用IncludeFilters 包含一些bean

@ComponentScan(value = {"com.zhh"},excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
        //这里排除 Controller 和 Service
})

@ComponentScan(value = {"com.zhh"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
        //这里只包含 Controller 同时需要禁用默认的过略规则
},useDefaultFilters = false)
@Configuration
public class MainConfig {

    //给容器注册一个bean;类型为返回值得类型,Id默认为方法名称作为ID,
    @Bean
    public Person person01(){
        return new Person("zhangsan",20);
    }
}

同时也可以自定义TypeFilter指定过滤规则

   @ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
//FilterType.CUSTOM 自定义过滤规则 过滤规则有子类MyFilter 实现TypeFilter接口
public class MyTypeFilter implements TypeFilter {
    /**
     *
     * @param metadataReader 读取到当前正在扫描类的信息
     * @param metadataReaderFactory 可以获取到其他任何类的信息
     * @return
     * @throws IOException
     */
    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);
        if(className.contains("er")){//如果ClassName 包含er 咋返回true
            return true;
        }
        return false;
    }
}
使用Scope设置组件作用域 @Scope
@Configuration
public class MainConfig2 {

    //默认为单实例 无论多少次获取都是同一个对象

    /**
     * prototype :多实例的  ioc容器启动 并不会去调用方法创建对象放在容器中,每次获取的时候才会创建
     * singleton : 单实例(默认的) ioc容器启动后,会创建对象放到IOC容器中,以后每次获取就是直接从容器(map.get)一个对象
     * request:同一个请求创建一个实例
     * session:同一个session创建一个事例
     * @return
     */
    @Scope
    @Bean
    public Person person(){
        return new Person("lisi",23);
    }
}
Lazy-bean 设置懒加载

懒加载:针对单实例bean:默认在容器启动的时候创建对象
容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化

    @Lazy
    @Bean
    public Person person(){
        return new Person("lisi",23);
    }
按照条件注册Bean @Conditional:能按照一定的条件进行判断,满足条件给容器中注册Bean.
    @Conditional({LinuxCondition.class})
    @Bean
    public Person person(){
        return new Person("lisi",23);
    }

    @Conditional({WindowsCondition.class})
    @Bean
    public Person Person02(){
        return new Person("zhangsan",23);
    }

LinuxCondition.java

public class WindowsCondition implements Condition {
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Windows")){ //如果当前运行环境为Windows10 则返回true
            return true;
        }
        return false;
    }
}

WindowsCondition.java

public class LinuxCondition implements Condition {
    /**
     *
     * @param conditionContext 判断条件能使用的上下文环境
     * @param annotatedTypeMetadata
     * @return
     */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        //TODO 是否是Linux 系统
        //获取IOC容器的BeanFacotry
        ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
        //获取类加载器
        ClassLoader classLoader =  conditionContext.getClassLoader();
        //获取当前运行环境
        Environment environment = conditionContext.getEnvironment();
        //获取bean 定义的注册类
        BeanDefinitionRegistry beanDefinitionRegistry = conditionContext.getRegistry();

        String properties =  environment.getProperty("os.name");
        if(properties.contains("Linux")){
                return true;
        }
        return false;
    }
}

@Condition 可以放在类上 如果放在类上,则表示 满足当前条件 则装配该类

使用import 给容器快速导入一个组件

1.包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)
2.@Bean[导入的第三方里面的组件]
3.@Import 快速给容器导入一个组件:容器中就会注册一个组件,ID默认是全类名

@Configuration
@Import(Color.class)
//Import 导入组件 Id默认组件的全类名
public class MainConfig2 {
}

interface importSelector:返回需要导入的组件的全类名数组;

@Configuration
@Import({Color.class,MyImportSelector.class})
//Import 导入组件 Id默认组件的全类名
public class MainConfig2 {
}

MyImportSelector.java

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        //返回值,就是导入到容器中的组件的全类名
        return new String[]{"com.zhh.Person","com.zhh.Color"};
    }
}

使用ImportBeanDefinitionRegistrar 手动注册bean到容器中

 @Configuration
@Import({Color.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
//Import 导入组件 Id默认组件的全类名
public class MainConfig2 {
}

MyImportBeanDefinitionRegistrar.java

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     *
     * @param annotationMetadata 当前类注册信息
     * @param beanDefinitionRegistry BeanDefinition注册类
     */
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
          boolean definition =   beanDefinitionRegistry.containsBeanDefinition("red");
          boolean definition2  = beanDefinitionRegistry.containsBeanDefinition("blue");
          if(definition && definition2){ //如果当前IOC容器有Red 类和 Blue类 就注册rabow类
              //指定bean 名称
              RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rabow.class);
              beanDefinitionRegistry.registerBeanDefinition("rabow",rootBeanDefinition);
          }
    }
}
使用Spring提供的FactoryBean(工厂bean)

1.默认获取到的是工厂bean调用getObject 创建对象
2.要获取工厂Bean本身,我们需要给Id前面加一个& 标识

//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
    //返回一个Color对象,在容器中保存一份
    public Color getObject() throws Exception {
        return new Color();
    }
    public Class<?> getObjectType() {
        return Color.class;
    }
    //来控制它是否是单例 true 为单例,false 为多实例
    public boolean isSingleton() {
        return true;
    }
}

@Bean
public ColorFactoryBean colorFactoryBean{
  return new ColorFactoryBean();
}

IOCTest.java
 @Test
    public void testIOCTest(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        Object object = applicationContext.getBean("colorFactoryBean");
        System.out.println("->>>"+object);
        //输出结果  ->>>com.zhh.bean.Color@4f638935  
        //虽然你装配的是ColorFactoryBean 但是你获取的是Color
    }
Bean指定初始化和销毁方法

初始化:对象创建完成,并赋值好,调用初始化方法
销毁:单实例:容器关闭的时候 ,多实例:容器不会管理这个bean,容器不会调用销毁方法
1.指定初始化和销毁方法

    @Bean(initMethod = "init",destroyMethod = "destory")
    public Car car(){
        return new Car();
    }
使用InitializingBean 和DisposableBean 接口指定初始化和销毁
public class Cat implements InitializingBean,DisposableBean {
    public void destroy() throws Exception {
        //容器关闭之后就会调用
    }
    public void afterPropertiesSet() throws Exception {
        //bean创建完成会调用
    }
}
使用PostConstruct 和 PreDestroy 注解 调用初始化和销毁方法
public class Dog {
    public Dog(){}
    @PostConstruct
    public void init(){
        System.out.println("调用初始化方法");
    }
    @PreDestroy
    public void destory(){
        System.out.println("调用销毁方法");
    }
}
BeanPostProcess【interface】:bean 的后置处理器

在bean 初始化前后,进行处理工作
1.postProcessBeforeInitialization:在初始化前工作
2.postProcessAfterInitialization:在初始化后工作

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;
    }
}
属性赋值-@Value
@Date
public class Person {
    //使用value赋值
    //1.基本数值
    //2可以写SpEL;#{}
    //3可以写${}取出配置文件中的值(在运行环境中的值)
    @Value("张三")
    private String name;
    @Value("#{20-2}")
    private Integer age;
    
    @Value("${person.nickName}")
    private String nickName;
}
//使用@PropertySource 读取外部配置文件中的key/value保存到运行环境变量中 
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
    @Bean
    public Person person(){
        return new Person();
    }
}
自动装配
 
/**
 * 自动装配;
 *      Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
 * 
 * 1)、@Autowired:自动注入:
 *      1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
 *      2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
 *                          applicationContext.getBean("bookDao")
 *      3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
 *      4)、自动装配默认一定要将属性赋值好,没有就会报错;
 *          可以使用@Autowired(required=false);
 *      5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
 *              也可以继续使用@Qualifier指定需要装配的bean的名字,使用@Primary那么@Qualifier 就不能使用了
 *      BookService{
 *          @Autowired
 *          BookDao  bookDao;
 *      }
 *
 *
 */
@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
    "com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
    
    @Primary
    @Bean("bookDao2")
    public BookDao bookDao(){
        BookDao bookDao = new BookDao();
        bookDao.setLable("2");
        return bookDao;
    }
    
    /**
     * @Bean标注的方法创建对象的时候,方法参数的值从容器中获取
     * @param car
     * @return
     */
    @Bean
    public Color color(Car car){
        Color color = new Color();
        color.setCar(car);
        return color;
    }
}

Spring 还支持@Resource(JSR250) 和@Inject (JSR330) [java规范的注解]
@Resouce: 可以和@Autowired 一样完成自动装配的功能;默认是按照组件名称进行装配的;没有能支持@Primary功能,没有@Autowired(Reqiured=false)

@Inject: 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
@Autowired:Spring定义的; @Resource、@Inject都是java规范

@Autired同时可以标注在构造器上,参数和方法,以及属性上;都是从容器中获取参数组件的值
1.标注在方法上:@Bean +方法参数;参数从容器中获取
2.标在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取

Profile多环境搭建
/**
 * Profile:
 *      Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
 * 
 * 开发环境、测试环境、生产环境;
 * 数据源:(/A)(/B)(/C);
 * 
 * 
 * @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
 * 
 * 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
 * 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
 * 3)、没有标注环境标识的bean在,任何环境下都是加载的;
 */

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
    
    @Value("${db.user}")
    private String user;
    
    private StringValueResolver valueResolver;
    
    private String  driverClass;
    
    
    @Bean
    public Yellow yellow(){
        return new Yellow();
    }
    
    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }
    
    
    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }
    
    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser(user);
        dataSource.setPassword(pwd);
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");
        
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        // TODO Auto-generated method stub
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }

}

1.活环境可以在JVM参数位置加载-Dspring.profiles.active=test
2.代码的方式激活环境

public class IOCTest_Profile {
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = 
                new AnnotationConfigApplicationContext();
        //1、创建一个applicationContext
        //2、设置需要激活的环境
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //3、注册主配置类
        applicationContext.register(MainConfigOfProfile.class);
        //4、启动刷新容器
        applicationContext.refresh();
    }

}

相关文章

网友评论

      本文标题:Spring高级特性之注解驱动开发上篇

      本文链接:https://www.haomeiwen.com/subject/yiwwuqtx.html