基于Annotation的spring

1,@Configuration注解,表明这是一个配置类

	相当于一个application.xml配置文件

2,@Bean 向容器中注册一个bean,类型为返回值类型,id默认是用方法名作为id,也可以指定id-》@Bean(“person”) 表示id为person

	相当于xml中的<bean id="xxx"  class="com.atsun.xxx">

在IOC容器启动时,会将方法的返回对象放入到容器中 默认方法名作为组件id

XML配置

    <bean id="person" class="com.atsun.bean.Person">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="20"></property>
    </bean>
JAVA配置类
@Configuration
@Import({Color.class,MyImportSelect.class,MyImportBeanDefinitionRegistrar.class}) //快速引入组件	
public void MyConfig{
	
	@Bean("person")
	public Person getPerson(){
		
		return new Person("zhangsan",20);
	}
}

public Person{
	........
}

public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};








@Configuration	声明这是一个配置类
@Bean		给容器中注册一个bean,类型为返回值类型。id默认为方法名,bean注解中可传入参数指定id
@Import		快速给容器中导入一个组件	
			1)@Import({Color.class}) 容器中就会自动注册这个组件,id默认时全类名
			2@Import({MyImportSelect.class}:ImportSelector:返回需要导入的组件的全类名数组	引入多个Bean
					实现ImportSelect接口重写selectImports方法返回要导入的组件的全类名数组	
			3)@Import({ImportBeanDefinitionRegistrar})
					自定义类实现ImportBeanDefinitionRegistrar接口,手动注册bean到容器中
使用Spring提供的FactoryBean(工厂Bean接口)
	实现工厂bean接口(FactoryBean)
	默认获取到的bean是工厂bean调用getObject()方法创建的对象,
	要获取工厂bean本身,我们需要给id前面加上&   xxxxx.getBean("&colorFactoryBean")
					
			



2------@Import({MyImportSelect.class}//返回值就是要导入到容器中的组件的全类名
    //AnnotationMetadata    当前标注@Import注解的类的所有注解信息
public class MyImportSelect implements ImportSelector {
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.atsun.Person1","com.atsun.Person2"};
    }
}


3----3)@Import({MyImportBeanDefinitionRegistrar.class})
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean flag = registry.containsBeanDefinition("com.atsun.bean.Color");//在容器中是否有这个bean
        if(flag){
        注册bean-->
            //指定bean的定义信息(可以指定Bean的类型,作用域等)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Red.class);
            //注册一个bean,指定bean名称
            registry.registerBeanDefinition("red",beanDefinition);
        }
    }
}

 //虽然装配的是ColorFactoryBean,但实际返回的是工厂bean调用getObject()方法创建的对象
    @Bean
    public ColorFactoryBean colorFactoryBean(){
        return new ColorFactoryBean();
    }
//创建spring定义的工厂bean
public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一个color对象,这个对象会添加到容器中
    public Color getObject() throws Exception {
        return new Color();
    }

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

    //是单例吗 true 是单实例,在容器中保存一份
    //          false   多实例 每次获取都会创建一个新的bean(就是调用getObject()方法)
    public boolean isSingleton() {
        return true;
    }
}

@PropertySource

datasource.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
/**
 * spring的配置类
 * 用于替代xml配置
 */
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
}

@Bean

作用:
	它写在方法上,表示把当前方法的返回值存入spring的ioc容器。
	同时还可以出现在注解上,作为元注解来使用。		
属性:
	name:
		用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定该属性时,默认值是当前方法的名称。
	value:
		此属性是在4.3.3版本之后加入的。它和name属性的作用是一样的。
	autowireCandidate:
		用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用。不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。
	initMethod:
		用于指定初始化方法。
	destroyMethod:
		用于指定销毁方法。
使用场景:
	通常情况下,在基于注解的配置中,我们用于把一个类存入spring的ioc容器中,首先考虑的是使用@Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类,此时无法在类上添加@Component注解,这时就需要此注解了。
示例:
	例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,在上面加注解,此时就可以使用@Bean注解配置。

@Import

import导入的类在容器中的bean名称是全限定类名

ImportSelector和ImportBeanDefinitionRegistrar介绍


特别说明:
	我们在注入bean对象时,可选的方式有很多种。
	例如:
		我们自己写的类,可以使用@Component,@Service,@Repository,@Controller等等。
		我们导入的第三方库中的类,可以使用@Bean(当需要做一些初始化操作时,比如DataSource),也可以使用@Import注解,直接指定要引入的类的字节码。
		但是当我们的类很多时,在每个类上加注解会很繁琐,同时使用@Bean或者@Import写起来也很麻烦。此时我们就可以采用自定义ImportSelector或者ImportBeanDefinitionRegistrar来实现。顺便说一句,在SpringBoot中,@EnableXXX这样的注解,绝大多数都借助了ImportSelector或者ImportBeanDefinitionRegistrar。在我们的spring中,@EnableTransactionManagement就是借助了ImportSelector,@EnableAspectJAutoporxy就是借助了ImportBeanDefinitionRegistrar。
		
共同点:
	他们都是用于动态注册bean对象到容器中的。并且支持大批量的bean导入。
区别:
	ImportSelector是一个接口,我们在使用时需要自己提供实现类。实现类中返回要注册的bean的全限定类名数组,然后执行ConfigurationClassParser类中中的processImports方法注册bean对象的。
	ImportBeanDefinitionRegistrar也是一个接口,需要我们自己编写实现类,在实现类中手动注册bean到容器中。 可以使用包扫描注册到容器中

注意事项:
	实现了ImportSelector接口或者ImportBeanDefinitionRegistrar接口的类不会被解析成一个Bean注册到容器中。
	同时,在注册到容器中时bean的唯一标识是全限定类名,而非短类名。

PropertySource

作用:
	用于指定读取资源文件的位置。注意,它不仅支持properties,也支持xml文件,并且通过YAML解析器,配合自定义PropertySourceFactory实现解析yml配置文件(详情请参考第五章第8小节自定义PropertySourceFactory实现YAML文件解析)。
属性:
	name:
		指定资源的名称。如果没有指定,将根据基础资源描述生成。
	value:
		指定资源的位置。可以是类路径,也可以是文件路径。
	ignoreResourceNotFound:
		指定是否忽略资源文件有没有,默认是false,也就是说当资源文件不存在时spring启动将会报错。
	encoding:
		指定解析资源文件使用的字符集。当有中文的时候,需要指定中文的字符集。
	factory:
		指定读取对应资源文件的工厂类,默认的是PropertySourceFactory。
使用场景:
	我们实际开发中,使用注解驱动后,xml配置文件就没有了,此时一些配置如果直接写在类中,会造成和java源码的紧密耦合,修改起来不方法。此时一些配置可以使用properties或者yml来配置就变得很灵活方便。

2,注解扫描

	XML	配置

<!--    包扫描 只要标注了@Controller  @Service @Component   @Repository  都会被自动扫描加入容器-->
    <context:component-scan base-package="com.atsun" use-default-filters="false">
        <context:exclude-filter type="annotation" expression=""/>
        <context:include-filter type="annotation" expression=""/> 只包含,需要禁用默认规则
    </context:component-scan>

    <bean id="person" class="com.atsun.bean.Person">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="20"></property>
    </bean>

JAVA配置类


作用:
	用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
属性:
	value:
		用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
	basePackages:
		它和value作用是一样的。
	basePackageClasses:
		指定具体要扫描的类的字节码。 扫描的是指定类所在包下的所有子包
	nameGenrator:
		指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
	scopeResolver:
		用于处理并转换检测到的Bean的作用范围。
	soperdProxy:
		用于指定bean生成时的代理方式。默认是Default,则不使用代理。详情请参考第五章第5小节ScopedProxyMode枚举。
	resourcePattern:
		用于指定符合组件检测条件的类文件,默认是包扫描下的  **/*.class 当前包及其子包,任意类(*.clsaa)
	useDefaultFilters:
		是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。
	includeFilters:
		自定义组件扫描的过滤规则,用以扫描组件。
		FilterType有5种类型:
            ANNOTATION, 注解类型 默认
            ASSIGNABLE_TYPE,指定固定类
            ASPECTJ, ASPECTJ类型
            REGEX,正则表达式
            CUSTOM,自定义类型
        详细用法请参考第五章第6小节自定义组件扫描过滤规则
	excludeFilters:
		自定义组件扫描的排除规则。
	lazyInit:
		组件扫描时是否采用懒加载 ,默认不开启。
使用场景:
	在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。
细节:
	在spring4.3版本之后还加入了一个@ComponentScans的注解,该注解就是支持配置多个@ComponentScan。

spring默认命名规则 首字母小写

在这里插入图片描述

在这里插入图片描述先找自己指定的
在这里插入图片描述
解析注解信息

BeanDefinition test1 = ctx.getBeanDefinition("appConfig");
       if(test1 instanceof AnnotatedBeanDefinition){
           AnnotatedBeanDefinition meta = (AnnotatedBeanDefinition) test1;
           AnnotationMetadata metadata = meta.getMetadata();
           Set<String> annotationTypes = metadata.getAnnotationTypes();
           for (String annotationType : annotationTypes) {
               AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationType, false));
               Object value = annotationAttributes.get("value");

               if (value instanceof String) {
                   
                   System.out.println(((String) value));
               }
           }

       }

默认规则:

在这里插入图片描述

在这里插入图片描述


@Configuration
@ComponentScan(value = "com.atsun",includeFilters = {
       /* @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),*/
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})	自定义扫描,实现TypeFilter 接口
},useDefaultFilters = false)
public class MainConfig {

    @Bean(name = "person")
    public Person getPerson(){
        return new Person("张三",18);
    }
}



/*
        @ComponentScan  value:指定要扫描的包
            --excludeFilters =Filter[] 排除那些组件,例如:排除包含Controller和service注解的
            --includeFilters =Filter[] 包含那些组件    需要禁用默认规则useDefaultFilters = false
               --FilterType.ANNOTATION          按照注解
               --FilterType.ASSIGNABLE_TYPE     按照给定的类型
               --ASPECTJ,                       按照切入点表达式
               --REGEX,                         按照正则匹配
               --CUSTOM;                        自定义过滤规则
*/


----》自定义过滤规则	CUSTOM
//MetadataReader 读取到当前正在扫描的类信息
//MetadataReaderFactory 可以获取到其他任何类信息
public class MyTypeFilter implements TypeFilter {
    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注解

@Configuration
public class MyConfug2 {

   

    @Scope(value = "singleton")
    @Lazy	对单实例启用
    @Bean(value = "person")
    public Person getPerson(){

        return new Person("zhangsan",22);
    }
}

 //默认时单实例的 
 	   @Scope     
		     prototype 多实例的 			
		    		IOC容器启动并不会去调用方法创建对象放入容器中
		    		每次获取的时候才会调用方法创建对象,每次获取都会创建一个对象
		    	
		     singleton单实例 默认值  	
		     		IOC容器启动会调用方法创建对象放入到ioc容器中  
		  			以后每次获取直接从容器中拿       
		    
		     request 同一次请求创建一个    
		     session 同一个session创建一个

     @Lazy   懒加载,对单实例有效
     *  单实例bean 默认在容器启动时创建对象
     *  懒加载     容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化

按照条件判断给容器中注册组件

@Conditional({参数必须为Condition类型}) Condition是一个接口 所以需要继承Contidion,并实现matches方法,返回true则满足
public @interface Conditional {
Class<? extends Condition>[] value();
}
既可以作用在方法上又可以作用在类上


@Configuration
//类中组件统一设置,满足当前条件,这个类中的配置的说有bean才能生效
@Conditional({LishiCon.class})	如果LishiCon-->matches返回true则当前类中注入的bean有效
public class MyConfug2 {


    @Scope(value = "singleton")
    @Lazy       //懒加载 只对单实例有效
    @Bean(value = "person")
    public Person getPerson() {

        return new Person("zhangsan", 22);
    }
	
	 @Bean("zhangsan")
    @Conditional({LishiCon.class})
    public Person person1(){

        return new Person("zhangsan",21);
    }

    @Bean("lishi")
    @Conditional(zhangsanCon.class)
    public Person person2(){

        return new Person("lishi",23);
    }
}

Condition.class

public class LishiCon implements Condition {


    /*
    *       conditionContext    判断条件能使用的上下文(环境)
    *       annotatedTypeMetadata   注释信息
    *
    *       如果容器中有person    则返回true,创建id为 zhangsan的容器
    * */
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
		 获取bean工厂
		 ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
		 获取环境变量
		  Environment environment = conditionContext.getEnvironment();
		   
		 //获取bean定义的注册类,所有的bean都在这里注册    能注册 移除 查询 bean
        BeanDefinitionRegistry registry = conditionContext.getRegistry();
		//在ioc容器中有person时生效
        boolean person1 = registry.containsBeanDefinition("person");
        System.out.println(person1+"-->");
        return person1;
    }
}


Bean的生命周期

bean创建-----------------》初始化-----------------------销毁过程
容器管理bean的生命周期
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法

	指定初始化和销毁方法
	xml		init-method="该类初始化方法名"		destroy-method="该类销毁方法名"

1、启动Spring容器
2、创建Bean对象 ----> 实际是在调用Bean对象的构造器
3、给Bean添加属性
4、调用Bean对象的初始化init-method
5、getBean获取某个bean对象,调用bean对象的某一个方法
6、调用Bean对象的销毁方法destory-method
7、Spring容器销毁

XML

<bean id="" class="" init-method="该类初始化方法名" destroy-method="该类销毁方法名"/>

注解

BeanPostProcessor.postProcessBeforeInitialization				初始化之前
	初始化		对象完成并赋值好,调用初始化方法。	多实例bean获取bean才创建容器,再进行初始化
BeanPostProcessor.postProcessAfterInitialization				初始化之后
	销毁		容器关闭的时候										多实例bean容器不会管理这个bean,容器不会调用销毁方法

1)指定初始化和销毁方法
		通过@Bean指定init-method和	destory-method
   public void test01(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfigofLifeCycle.class);
        System.out.println("IOC  created finish.......");

        //关闭容器
        applicationContext.close();
    }
    
@Configuration
public class MainConfigofLifeCycle {
    @Bean(initMethod = "init",destroyMethod = "destory")
    public Car car(){
        return new Car();
    }
}

public class Car {
    public Car(){
        System.out.println("car constructor....");
    }
    public void init(){
        System.out.println("car.....init....");
    }
    public void destory(){
        System.out.println("car .....destory...");
    }
}

控制台
	car constructor....					容器创建
	car.....init....					初始化
	IOC  created finish.......			创建完成
	car .....destory...					销毁

2)通过让bean实现InitalizingBean(定义初始化逻辑)
								DisposableBean(定义销毁逻辑)

@Component
public class Cat implements InitializingBean, DisposableBean {
    public Cat() {
        System.out.println("cat   constructor.....");
    }
    //销毁,单实例bean容器关闭后调用
    public void destroy() throws Exception {

        System.out.println("destory........");
    }
    //初始化   bena创建完成并且属性都赋好值以后调用
    public void afterPropertiesSet() throws Exception {

        System.out.println("afterPropertiesSet.....");
    }
}


@Configuration
@ComponentScan("com.atsun.bean")
public class MainConfigofLifeCycle {

   /* @Bean(initMethod = "init",destroyMethod = "destory")
    public Car car(){
        return new Car();
    }*/

}

    public void test01(){
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfigofLifeCycle.class);
        System.out.println("IOC  created finish.......");

        //关闭容器
        applicationContext.close();
    }

控制台
	cat   constructor.....
	afterPropertiesSet.....
	IOC  created finish.......
	destory........
3)使用JSR250
	@PostConstruct 在bean创建完成并且属性赋值完成来执行初始化
	@PreDistory		在容器销毁之前进行调用

@Component
public class Dog {
    public Dog() {
        System.out.println("Dog  constructor......");
    }

    //对象创建并赋值之后调用
    @PostConstruct
    public void init(){

        System.out.println("@PostConstruct.......");
    }

    //在容器移除对象之前回调
    @PreDestroy
    public void distory(){
        System.out.println("@PreDestroy.........");
    }
    
}

4)BeanPostProcessor【interface】	bean的后置处理器
		在bean的初始化前后进行一些处理工作
		--postProcessBeforeInitialization	bean 任何初始化方法之前	进行后置处理工作
		--postProcessAfterInitialization		bean 任何初始化之后进行	后置处理工作
/*
* 初始化前后进行处理
* @param    bean    	刚创建的bean实例
* 			beanName    这个bean实例在容器中的名字
*
* return 可以直接返回刚创建的bean,也可以包装bean之后再返回
* */

@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;
    }
}



控制台
	cat   constructor.....														创建bean对象
	postProcessBeforeInitialization..cat...com.atsun.bean.Cat@25359ed8			初始化方法之前
	afterPropertiesSet.....														初始化
	postProcessAfterInitialization..cat...com.atsun.bean.Cat@25359ed8			初始化方法之后
	Dog  constructor......														
	postProcessBeforeInitialization..dog...com.atsun.bean.Dog@12405818
	@PostConstruct.......
	postProcessAfterInitialization..dog...com.atsun.bean.Dog@12405818
	IOC  created finish.......
	@PreDestroy.........
	destory........

BeanPostProcessor原理

// Initialize the bean instance.初始化bean实例
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);											为bean进行属性赋值(get/set等)
			==========================================================================================================
			exposedObject = initializeBean(beanName, exposedObject, mbd);							调用初始化方法
		}

//初始化给定的bean实例,应用工厂回调
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
			遍历容器中的所有BeanPostProcessors挨个执行postProcessBeforeInitialization
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);		初始化之前,应用所有的。。。
		}
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);											执行初始化
		}
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);		初始化之后
		}

		return wrappedBean;
	}



遍历容器中的所有BeanPostProcessors挨个执行postProcessBeforeInitialization,返回null不会执行后面的
BeanPostProcessor.postProcessBeforeInitialization
	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {
		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

@Value注解
参数 1,基本数值
2,SPEL #{}
3,${} 取出配置文件的值(在运行时配置文件的参数都会放入换环境变量中)

  public void Test02(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
   
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);

        //获取环境变量,配置文件中的属性都会加载到环境变量中,可以根据key直接取出来
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        String property = environment.getProperty("person.nickName");
        System.out.println(property);
    }
properties
	person.nickName=xiaozhangsan
  
//PropertySource读取外部配置文件中的K/V保存到环境变量中;加载完为外部配置文件后用${}取出配置文件中的值
@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfigOfPropertyValues {
    @Bean
    public Person person(){
        return new Person();
    }
}

public class Person {
    //使用@Value赋值
    //1,基本数值
    //2,SPEL #{}
    //3,${} 取出配置文件的值(在运行环境变量的值--运行时都会放入换环境变量中)
    @Value("zhangsan")
    private String name;
    @Value("#{20-2}")
    private int age;

    @Value("${person.nickName}")
    private String nickName;



自动装配

Spring利用依赖注入(DI)完成对ioc容器中各个组件的依赖关系赋值
@Autowired:自动注入
------》required=false是否必须

     1--》	默认优先按照类型去容器中找对应的组件,找到就赋值
     2--》	如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
     3--》	没有找到就会报错	可以使用@Autowired(required=false)不是必须的,如果找到就装配找不到就不装配

@Qualifier(“组件id”)

	指定需要装配的组件id,而不是使用属性名

@Primary:让spring进行自动装配的时候,默认使用首选的bean

优先级 @Primary----》@Qualifier-----》@Autowired(默认)

@Configuration
@ComponentScan({"com.atsun.Service","com.atsun.Dao","com.atsun.controller"})
public class MainConfigOfAutowired {
    @Primary    //作为首选的
    @Bean
    public BookDao bookDao(){

        return new BookDao();
    }
}

Spring还支持使用@Resource(JSR250)和@Inject(JSR330) 【java规范】
@Resource:默认按照组件名称进行装配
@Inject:需要导入javax-inject依赖,和Autowiredz功能一样,没有required=false

@Autowired:能标注在构造器,参数,方法,属性;都是从容器中获取参数组件的值
1)标注再方法上,Spring容器创建当前对象,就会调用方法完成赋值,方法使用的参数,自定义类型的值从ioc容器中获取
2)标注在有参构造器上,自动注入构造器所需参数,如果组件只有一个有参构造器,这个有参构造器的antowired可以省略,参数位置的组件还是自动从容器中获取
3)标注在方法参数上

@Bean标注的方法创建对象的时候,方法参数值冲容器中获取(Car)

@Bean
public Color color(Car car/*自动注入,省略@Autowired*/){
	Color color=new Color();
	color.setCar(car);
	return color;
}

AOP

以前使用AOP注解需要在xml中开启基于注解的aop模式

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

现在使用配置类配置类

@EnableAsprctJAutoProxy		开启基于注解的aop模式
@Configuration
public class MainConfigOfAop{
	@Bean........
}


@Component
@Aspect
public class LogAspect {

	@Pointcut("execution(* com.atsun.service.Hello.*(..))")
	public void pointcut(){}




	@Before("pointcut()")
	public void hehe() {
		System.out.println("before ...");
	}

	@After("pointcut()")
	public void haha(JoinPoint joinPoint) {
		String name = joinPoint.getSignature().getName();
		System.out.println("After ...");
	}

	@AfterReturning(value = "pointcut()",returning = "rt")
	public void xixi(JoinPoint joinPoint, Object rt) {
		System.out.println("AfterReturning ...");
	}


	@AfterThrowing("pointcut()")
	public void thr() {
		System.out.println("AfterThrowing ...");
	}

	/*@Around("point()")
	public void xxx(ProceedingJoinPoint pj) {
		try {

			System.out.println(pj.getThis());
			System.out.println(pj.getTarget());
			System.out.println(pj.getSourceLocation());
			System.out.println("Around aaa ...");
			pj.proceed();
			System.out.println("Around bbb ...");
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
	}*/
}

AnnotationAwareAspectJAutoProxyCreator 的注册

注册了AnnotationAwareAspectJAutoProxyCreator的bean定义信息
在这里插入图片描述
在这里插入图片描述

  • 继承关系图
    • 可以看到它实现了bean的后置处理器 表明他可能会在bean的创建过程中做一些事情

在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator的创建

在创建spring工厂的过程中,会有一部注册bean的后置处理器

在这里插入图片描述

在这个方法中就会注册拦截Bean创建的BeanPostProcess。AnnotationAwareAspectJAutoProxyCreator就会在这里进行创建,放在容器中,

在这里插入图片描述

invokeInitMethods
BeanFactoryAware#setBeanFactory()接口的回调,父类的setBeanFactory被调用
AbstractAdvisorAutoProxyCreator#setBeanFactory()
后置处理器初始化对象的时候回调
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

创建完成后注册到bean工厂中(有两份,单例池和后置处理器map中)
在这里插入图片描述
在这里插入图片描述

AnnotationAwareAspectJAutoProxyCreator的调用

因为是bean的后置处理器,并且已完成对象的创建,在未来创建其他组件时就会拦截创建过程

已创建好了对象,并且已在容器中,因为是在注册后置处理器时触发的,但是其他的后置处理器可能还未进行初始化,在其他后置处理器进行初始化时就可以进行干预了(因为是调用工厂的getBean() ,所以必须触发一串增强逻辑)

以后只要所有人创建Bean,都会在所有人创建真正对象之前,先拦截尝试能不能返回代理对象

第一次调用

当前要创建的bean是否是不能创建代理的类,是就放入advisedBeans

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法会被后置处理器调用 (这也是第一次后置处理器的调用)
给BeanPostProcessors一个返回代理而不是目标bean实例的机会。(这里基本上后不会有后置处理器进行真正处理)
在这里插入图片描述

在这里插入图片描述
就会调用到 AbstractAutoProxyCreator#postProcessBeforeInstantiation,目的就是找出一些不需要进行代理的对象缓存起来,后面才会进行真正的代理
进入前会先判断当前bean是否在advisedBeans中 (advisedBeans保存了所有不需要进行增强的bean)

如果是下面这些类型的bean都会被放进advisedBeans中


isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName){
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return null;
}

boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
				Pointcut.class.isAssignableFrom(beanClass) ||
				Advisor.class.isAssignableFrom(beanClass) ||
				AopInfrastructureBean.class.isAssignableFrom(beanClass);

或者标注了Aspect注解
(AnnotationUtils.findAnnotation(clazz, Aspect.class) != null

或者是否需要跳过,获取候选的增强器,(切面里面的通知方法)
在这里插入图片描述

每一个通知方法的增强器是InstantiationModelAwarePointcutAdvisor类型,
遍历每个增强器,看是否是AspectJPointcutAdvisor类型,如果是就会返回true,就代表会放进advisedBeans中,也就是不会(适合)被代理的集合。我们的增强器显然不是这个类型

在这里插入图片描述
找到容器中所有组件, 遍历判断是否是切面(标注了Aspect注解),将切面类的方法进行解析,每个增强方法都封装成一个Advisor(InstantiationModelAwarePointcutAdvisorImpl),将所有Advisor封装成list集合返回

在这里插入图片描述
将找到的Advisors缓存起来,下次来的时候直接就返回封装的所有的增强方法的集合

在这里插入图片描述
获取切面的所有方法
在这里插入图片描述
将所有增强方法封装成InstantiationModelAwarePointcutAdvisor返回

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解析注解,根据不同的注解创建不同的对象 其中 After,AtAfterThrowing注解创建的对象实现了MethodInterceptor接口(后面就不需要适配器了)
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

第二次调用 创建代理

AbstractAutowireCapableBeanFactory#initializeBean

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

如果已被代理就直接返回
在这里插入图片描述

  • 在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

会去当前类型的所有增强器,
1)拿到所有增强器 前面解析类上的所有方法的注解就缓存起来了的 切入点
2)查看增强器能不能应用到这个对象上 并返回能应用的增强器 (用切入点表达式算每一个方法是否能匹配)
在这里插入图片描述

  • 遍历所有增强器,查看是否能应用到当前类上
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3) 拓展增强器 前面加一个MethodInterceptor
在这里插入图片描述

4)对增强器进行排序 决定了谁先执行谁后执行

在这里插入图片描述

5)返回能应用的增强器

  • 如果有能应用的增强器,那么进行创建代理

在这里插入图片描述

1)获取所有增强器(通知方法)
2)保存到proxyFactory
3)创建代理对象:spring决定使用那种动态代理的方式
JDK,CGLib

将当前类存入不需要被代理的集合,并返回代理对象,存入容器中
以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程

  • 创建代理工厂 使用代理工厂进行创建代理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

加入拦截器,放方法执行时就会调用这个类的intercept方法
在这里插入图片描述

将proxyFactory也放进了代理对象,返回代理对象

在这里插入图片描述

在这里插入图片描述

目标方法的执行

容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xXx)
在这里插入图片描述

1)CGLliProxy.DynamicAdvisedInterceptor.intercept拦截目标方法的执行
2)根据ProxyFactory对象获取将要执行的目标方法的拦截器链(增强器(保存信息)包装成拦截器(可以执行目标方法)) 适配器包装
在这里插入图片描述

包装After注解的类以实现了MethodInterceptor在上面解析注解时有说到
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3)没有拦截器链就直接执行目标方法
拦截器链 (每一个通知方法又被包装为拦截器,后来方法的执行都是利用MethodInterceptor机制)
4)如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等所有信息传入创建一个CglibMethodInvocation,并调用proceed方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
5)拦截器链的触发过程
如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后拦截器)执行目标方法;
链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器的机制:保证通知方法与目标方法的执行顺序

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值