Spring注解开发 学习

所有的组件都应该放在Ioc容器中 ,组件之间的关系通过容器来进行自动装配(依赖注入)

AnnotationConfigApplicationContent

组件添加

@ComponentScan

设置扫描的路径包扫描 java8 可以写多个

@Configuration

高速Spring 这个是一个配置类

@Bean

给容器注册一个Bean;类型为返回值类型, id 默认是用方法名id

//配置类==配置文件
@Configuration //高速Spring 这个是一个配置类
@ComponentScan(value = "com.ly")//设置扫描的路径包扫描 java8 可以写多个
// ComponentScan.Filter[] includeFilters() 指定扫描的时候只包含哪些组件
// ComponentScan.Filter[] excludeFilters() :指定扫描的时候按照什么规则排除哪些组件
//@ComponentScan.Filter(type = FilterType.ANNOTATION // 排除的规则类型
//FilterType.ANNOTATION  :安装注解 
//FilterType.ASSIGNABLE_TYPE : 按照给定的类型
//FilterType.ASPECTJ : 使用ASPECTJ表达式
//FilterType.REGEX : 使用正则表达式
//FilterType.CUSTOM : 使用自定义规则(使用比较多) filter.TypeFilter} implementation 实现类
,classes = {Controller.class, Service.class} //规则
,useDefaultFilters = false) //禁用默认规则
@ComponentScans({ // 如果是jdk8以下, 可以使用指定多个ComponentScan
        
}) 
public class MainConfig {
	
	//给容器注册一个Bean;类型为返回值类型, id 默认是用方法名id
	@Bean("指定id名称")
	public Person person() {
		
		return new Person("张三", 22);
	}

}
		ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
		
		Object object = applicationContext.getBean("person");
		
		System.out.println(object);
		
//自定义过滤类型  @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
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);

        if (className.contains("er")) {
            return true;
        }

        return false;
    }
}
@Scope 调整作用域*
@Lazy 懒加载

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

@Configuration
public class MainConfig {

	//@Scope(value = "prototype")  
		//prototype : 多实例 :ioc启动调用方法调用对象放在容器中 ,每次获取的时候会调用方法创建对象-->注意声明周期不会自动销毁  
		//singleton :单实例(默认值) : ioc容器启动会调用方法放倒ioc容器中  
		//request : 同义词请求创建一个实例  *基本不用  
		//session : 同一个session创建一个实例 *基本不用  
    @Lazy
    @Bean
    public Person person() {
        return new Person("李四", 33);       
    }
}

@Conditional: 按照一定的条件进行判断 , 满足条件给容器注册Bean

//config中bean
    @Scope(value = "prototype")
    @Conditional(WindowsCondition.class)
    @Bean
    public Person person() {
        return new Person("张三" ,22);
    }

自定义判定条件

配置类中配置自定义判断条件将bean加入到Ioc容器中

public class WindowsCondition implements Condition {
    /**
     *
     * @param context  判断条件能使用的上下文环境
     * @param metadata 注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //是否windows系统

        //1. 能获取到ioc使用的Beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2. 获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //3. 获取当前环境信息(环境变量虚拟机变量)
        Environment environment = context.getEnvironment();
        //4. 获取Bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");

        if (property.contains("Windows")) {
            return true;
        }

        return false;
    }
}
  • 包扫描 + 组件标注注解 : 只要标注了@Controller ,@Service ,@Respsitory ,@Component
  • @Bean [第三方包里面的组件]

@Import [快速的给容器中导入组件]

@Import(要导入到容器中的组件):容器中酒会自动注册这个组件,id默认是全类名
@Configuration
@Import(Color.class) //可以单个或多个
public class MainConfig {

}
//---------------------------------------------------------
public class MyImportSelector implements ImportSelector {
    /**
     *
     * @param importingClassMetadata:当前标注@Import注解的类的所有信息
     * @return 就是到导入到容器中的组件全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[0];
    }
}
@ImportSelector:返回需要导入到组件的全类名数组 ==>使用多

//@Configuration
//@Import(Color.class)
@Import(MyImportSelector.class)
public class MainConfig {

    @Scope(value = "prototype")
    @Bean
    public Person person() {
        return new Person("李四", 33);
        
    }

}
//---------------------------------------------------------
public class MyImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata:当前标注@Import注解的类的所有信息
     * @return 就是到导入到容器中的组件全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//        return new String[0];
			//方法不能返回null,可以返回空数组
        return new String[]{"com.ly.bean.Blue", "com.ly.bean.Green", "com.ly.bean.Red"};


    }
}

@ImportBeanDefinitionRegistrar:Bean定义注册类 , 手工注册bean

@Configuration
//@Import(Color.class)
@Import({ MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MainConfig {

   @Scope(value = "prototype")
   @Bean
   public Person person() {
       return new Person("李四", 33);

   }

}
//---------------------------------------------------------
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

  /**
   * @param importingClassMetadata:当前类的注解信息
   * @param registry  :BeanDefinition注册类
   *                  把所有需要添加到容器中的Bean:调用
   *                  BeanDefinitionRegistry.registerBeanDefinition 是手工注册
   */
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      boolean red = registry.containsBeanDefinition("com.ly.bean.Red");
      boolean blue = registry.containsBeanDefinition("com.ly.bean.Blue");

      if (red && blue) {
         //指定Bean定义信息,(Bean类型,...)
          RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);
          //注册一个Bean , 并指定Bean名
          registry.registerBeanDefinition("rainBow", rootBeanDefinition);
         }


   }
}
使用Sping提供的FactoryBean(工程模式获取Bean) ==>使用多
		 1. 获取到的是工厂Bean 调用getObject创建的对象  
		 2. 获取工厂Bean本身,我们需要给id前边加一个"&" : &colorFactory
@Configuration
//@Import(Color.class)
@Import({ MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
public class MainConfig {

    @Scope(value = "prototype")
    @Bean
    public Person person() {
        return new Person("李四", 33);

    }

    @Bean
    public ColorFactory colorFactory() {
        return new ColorFactory();
    }

}
 //---------------------------------------------------------
//创建一个Spring定义的FactoryBean
public class ColorFactory implements FactoryBean<Color> {
    /**
     * 返回一个bean对象,这个对象会添加到容器中
     * @return
     * @throws Exception
     */
    @Override
    public Color getObject() throws Exception {
        return new Color();
    }

    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    /**
     * 是否是单例
     * @return
     * true:这个bean是单实例,在容器中保存一份
     * false:多实例,每次获取都会创建一个新的bean
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}
    @Test
    public void test03() {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);

        Object colorFactory = applicationContext.getBean("colorFactory");
        Object contextBean = applicationContext.getBean("&colorFactory");
        System.out.println(colorFactory.getClass());
        System.out.println(contextBean.getClass());

    }

Bean的生命周期

			Bean创建 ---初始化---销毁的过程   
		容器管理bean的声明周期;  
		我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法  
		构造(对象创建)  
				* 单实例: 在容器启动的时候创建
				* 多实例: 在每次获取的时候创建  
		初始化:   
			    对象创建完成,并赋值好,顶用初始化方法  
		销毁 :  
				单实例: 在容器关闭的时候进行销毁
				多实例: 容器不会管理这个bean; 容器不会调用销毁, (需要的时候创建,但不会管理)
1. 指定初始化和销毁方法
			1. xml中, init-method="" destroy-method=""  
			2. 通过@Bean指定initMethod 和destroyMethod ;
2. 通过让Bean实现InitializingBean(定义初始化逻辑) ,DisposableBean(定义销毁逻辑)
@Component
public class Cat implements InitializingBean, DisposableBean {


    @Override
    public void afterPropertiesSet() throws Exception {

        System.out.println("Cat..innit");

    }

    @Override
    public void destroy() throws Exception {
        System.out.println("Cat..destroy");
    }
}
//----------------------------------------------------------
@Configuration
@ComponentScan("com.ly.bean")
public class CatConfig {
}
//----------------------------------------------------------

public class CatTest {

    @Test
    public void test01() {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CatConfig.class);
        System.out.println("容器创建完成");
        context.close();
        //Object cat = context.getBean("cat");


    }
}
3. 可以使用JSR-250定义的两个注解
	@PostConstruct:在bean创建完成并且属性赋值完成; 来执行初始化方法
	@PreDestroy:在容器销毁bean之前通知我们进行清理工作
@Component
public class Dog {
    public Dog() {

        System.out.println("Dog...construct");
    }
    //对象创建并赋值之后调用
    @PostConstruct
    public void init() {
        System.out.println("Dog ...init");
    }
    //容器移除对象之前
    @PreDestroy
    public void destroy() {
        System.out.println("Dog ... destory");
    }
}
4. BeanPostProcessor(interface):Bean的后置梳理起:
		在bean初始化后进行一些处理工作: 
		postProcessBeforeInitialization:初始化之前进行后置操作  
		postProcessAfterInitialization: 初始化之后进行后置操作 
@Component  //将后置处理加入到容器中 ,就能进行工作
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     *
     * @param bean 容器创建好的实例 the new bean instance
     * @param beanName 实例名称
     * @return 可以返回传来的对象.也可以包装后返回
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization---->"+beanName);
        return bean;
    }

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

源码解析 BeanPostProcess原理

debug 查看程序运行过程
populateBean(beanName, mbd, instanceWrapper); //给bean 进行属性赋值  
{  
applyBeanPostProcessorsBeforeInitialization  

//遍历得到容器中所有的BeanPostProcessor:挨个执行befoInitialization, 一旦返回null,跳出for循环,不会执行后边的BeanPostProcessor.postProcessBeforeInitialization()
for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}

invokeInitMethods(beanName, wrappedBean, mbd);  //执行自定义初始化方法
	applyBeanPostProcessorsAfterInitialization  
}  
Spring底层对BeanPostProcess的使用

bean赋值 ,注入其他组件 @Autowired 生命周期注解 @Async
ApplicationContextAwareProcessor 注入ioc容器

组件赋值

@Value 属性赋值

  1. 基本数值
  2. 可以使用SpEL表达式 : #{ }
  3. 可以写${ }:取出配置文件中的值(在运行的环境变量的值)

@PropertySource(value=={“classpath:/xxx.properties”})

可以读取外部配置文件中的 k/v 保存到运行环境变量中;加载完外部配置文件后,使用${ }去除配置文件中的值
@propertySource

组件注入

自动装配: Autowired @Resource @Inject

	Spring利用依赖注入(DI) , 完成对IOC容器中各个组件的依赖关系赋值
1. @Autowired
1. 默认按照类型去容器中查找对应的组件
2. 如果找到多个相同的组件, 再将属性的名称做为组件id去容器中查找
3. @Qualifier 指定使用组件的id
4. 自动装配默认一定要将属性值赋值好, 如果没有就会找错
	@autowired(required =false);
5.@primary: 让Spring进行自动装配的时候使用首选Bean,也可以使用Qualifier指定需要装配的Bean的名称
2. Spring还支持使用@Resource(JRS250)和InJect(JSR330) [Java 规范]
1. @Resource : 可以和@Autowired一样实现自动装配, 默认按照名称装配
		没有支持@Primary功能没有支持@Autowired(required=false)
2.需要导入javax.Inject包和@Autowired一样,没有required=flase 

推荐还是使用@Autowired
AutowiredAnnotationBeanPostProcessor: 解析完成自动装配功能

3.@Autowired 方法、构造器位置自动装配
  1. @Autowired: 可标注在构造器,参数,方法, 属性
  2. 标注在方法上(set()方法上), Spring容器创建当前对象,就会调用方法,完成赋值,方法使用的参数,自定义类型的值从IOC容器中获得。
  3. 标注在构造器
    1. 默认加载ioc容器中的组件, 容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
    2. 标在有参构造器上:Spring容器启动调用有参构造器,构造器要用的组件也是从IOC中获得
  4. 参数位置 : 也是从容器中获取的参数组件的值。 如果一个有参,@Autowired可以省略,参数组件可以自动从容器中获取
  5. @Bean标注的方法创建对象的时候,方法参数的值从容器中获取

Aware注入: 回调函数的风格, 自定义组件想要使用Spring 容器底层的一些组件

自定义实现xxxAware 在创建对象的时候,会调用接口规定的方法注入到相关的组件;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tUxcSRzr-1589265334803)(_v_images/20181120113106198_17027.png)]

ApplicationContextAware :获取 IOC 容器

有相关的processor来进行处理
XXXAware: 功能使用xxxProcessor

@profile

Spring为我们提供的可以提供当前环境,动态激活和切换一系列组件的功能
例如: 多数据源切换
开发环境、测试环境、生产环境;

  1. 加了环境标识的Bean,必有这个环境被激活的时候才能注册到容器中;默认发;default
    1. 使用命令行参数的形式:在虚拟机参数位置 -Dspring.profiles.active=test
    2. 代码的形式:
      1. 创建ioc容器
      2. 设置需要激活的环境
      3. 注册主配置类
      4. 启动刷新容器
  2. 卸载配置类上,只有是指定环境的时候,整个配置类里面的配置才能生效
  3. 没有标注环境标识的bean,在任何环境下都是加载的;

总结

重点 包扫面
@conditional
@import 与高级模式 spring底层使用非常多

AOP

使用

AOP:动态代理
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行是编程方式:

  1. 导入AOP模块;Spring AOP:
  2. 定义业务逻辑类 : 日志
  3. 定义一个切面类(AspectsClass):切面类里面的方法需要动态感知逻辑类中的方法运行到那里
    1. 前置通知(@Before):目标方法运行前通知
    2. 后置通知(@Aftre):目标方法运行结束后通知(无论方法是正常结束还是异常结束都调用)
    3. 返回通知(@AfterReturning):目标方法正常运行结束后通知
    4. 异常通知(@AfterThrowing):目标方法出现异常通知
    5. 环绕通知(@Around):动态代理,手动推进目标方法运行(JoinPoint.procced())
  4. 给切面类的目标方法标注何时何地运行(通知注解)
  5. 将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
  6. 必须告诉Spring哪个是切面类(给切面类加一个注解:@Aspect)
  7. 给配置类中加@EnableAspectJAutoProxy:开启基于注解的AOP模式
    在Spring中很多的@EnableXXX; 都是开启xxx功能
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y8XzPFrr-1589265334807)(_v_images/20181120163736309_1842.png)]
    JoinPoint参数一定要出现在参数表的第一位
    三步:
  8. 将业务逻辑组件和切面类都加入到容器中:告诉Spring哪个是切面类(@Aspect)
  9. 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
  10. 开启基于注解的aop模式;@EnableAspectJAutoProxy

原理(看给容器中注册了什么组件,这个组件什么时候工作,这个组件工作时候的功能)

  1. @EnableAspectJAutoProxy是什么:
    @Import(AspectJAutoProxyRegistrar.class):给容器导入
    利用AspectJAutoProxyRegistrar自定义给容器中注册bean:
    internalAutoProxyCreator–>AnnotationAwareAspectJAutoProxyCreator
    给容器中注册一个AnnotationAwareAspectJAutoProxyCreator
  2. AnnotationAwareAspectJAutoProxyCreator
    ----->AspectJAwareAdvisorAutoProxyCreator
    ---------->AbstractAutoProxyCreator
    ==========>implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
    ===============

声明式事务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值