Spring注解驱动开发

1 IOC容器

1.1 组件注册

@Configuration与@Bean注解

与以往使用xml作为spring的配置文件不同,现在我们可以直接手写一个配置类来为spring容器注入组件,具体使用方法如下:

//首先我们需要手写一个配置类,这个类就相当于我们之前的xml文件
//@Configration注解就标注了这个类是一个配置类
@Configuration
public class MyConfiguration{
	//使用@Bean注解来为容器中注入组件,括号中的字符串为注入组件的id
	@Bean("person")
	public Person person(){
		return new Peson();
	}
}

//开启基于注解的ioc容器的方法,使用AnnotationConfigApplicationContext
public class Mytest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
    }
}

@ComponentScan

扫描特定包下的所有组件,可以配置一定的过滤规则,指定扫描哪些组件或者指定排除哪些组件,通过@Filter注解来实现,可选的属性如下:

  • value:指定要扫描的包
  • excludeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}}:过滤规则,排除被@Controller和@Service标注的类
  • includeFilters = {@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}}:扫描的时候只包含哪些组件,只扫描被@Controller和@Service标注的类
  • useDefaultFilters = false : 不使用默认的扫描策略,在自定义扫描策略的时候,需要将此属性置为false
//扫描com.huhx.java包下的所有组件(被@Component、@Controller、@Service、@Repository标注的)
@ComponentScan(value = "com.huhx.java")
@Configuration
public class MyConfiguration{
	//使用@Bean注解来为容器中注入组件,括号中的字符串为注入组件的id
	@Bean("person")
	public Person person(){
		return new Peson();
	}
}

@Filter注解

type属性的可选值:

  • FilterType.ANNOTATION:按照注解过滤

  • FilterType.ASSIGNABLE_TYPE:按照给定的类型过滤

  • FilterType.ASPECTJ:使用ASPECTJ表达式过滤(不常用)

  • FilterType.REGEX:使用正则表达式过滤(不常用)

  • FilterType.CUSTOM:使用自定义规则过滤(重点)

    自定义过滤规则的方法:

    1. 自己实现一个类去实现TypeFilter接口

      public class MyTypeFilter implements TypeFilter{
      	/**
      	 * metadataReader:读取到的当前正在扫描的类的信息
      	 * metadataReaderFactory:可以获取到其他任何类信息
      	 */
      	@Override
      	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
      		//获取当前类注解的信息
      		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
      		//获取当前正在扫描的类的类信息
      		ClassMetadata classMetadata = metadataReader.getClassMetadata();
      		//获取当前类资源(如类的路径)
      		Resource resource = metadataReader.getResource();
      		//去扫描类名中包含“er”的类进ioc容器
      		String className = classMetadata.getClassName();
      		if(className.contains("er")){
      			return true;
      		}
      	return false;
      }
      
      
    2. 然后去在@ComponentScan中去配置上该过滤规则

      @Component(includeFilters = {@Filter(type = FilterType.CUSTOM, class = {MyTypeFilter.class}}
      

@Scope(“prototype”)

ioc中的组件默认是单实例的,使用@Scope可以用来改变bean的作用域,我们可以配置成其他的值
1、ConfigurableBeanFactory#SCOPE_PROTOTYPE(prototype 多实例)

ioc容器启动时并不会调用方法创建对象放到ioc容器中,而是在第一次使用bean的时候完成初始化并将对象放入ioc容器

@Configuration
public class MyConfiguration{
	@Scope("prototype")
	@Bean("person")
	public Person person(){
		return new Peson();
	}
}

2、ConfigurableBeanFactory#SCOPE_SINGLETON(singleton 单实例-默认)

ioc容器启动时会调用方法创建对象放到ioc容器中

@Lazy 懒加载:

单实例bean,默认在容器启动时创建对象,使用懒加载模式,容器在启动时不会创建对象,而是在第一次使用(获取Bean)时创建对象并初始化。

@Configuration
public class MyConfiguration{
	@Lazy
	@Bean("person")
	public Person person(){
		return new Peson();
	}
}

@Conditional

SpringBoot底层大量使用的注解,可以按照一定的条件进行判断,满足条件给容器中注册bean

使用方式:
这个注解可以加在类上,也可以加在方法上,如果该条件注解放在类上,则满足当前条件,这个类中配置的所有bean都会生效,如果放在方法上,则对单独的类的装载起到限定作用,SpringBoot中已经提供了很多条件类,同时我们也可以自己实现自定义的条件类,我们需要定义一个条件类并实现Condition接口:

public class MyCondition implement Condition{
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata){
		//能获取到ioc使用的beanfactory
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//能获取类加载器
		ClassLoader classLoader = context.getClassLoader();
		//获取当前环境信息
		Environment environment = context.getEnvironment();
		//获取到bean定义的注册类
		BeanDefinitionRegistry registy = context.getBeanDefinitionRegistry();

		/**
		* 在这里可以用上面获取到的beanFactory、classLoader、environment、registy来配合实现自己的判断逻辑
		*/
		return false;
	}
}

定义好了我们的条件类之后,在主配置类注入bean时加上@Conditional注解,就可以按照条件注入了:

@Configuration
public class MyConfiguration{
	@Bean("person")
	@Conditional(MyCondition.class)
	public Person person(){
		return new Peson();
	}
}

@Import

  • 直接给容器中导入一个组件

    @Configuration
    @Import({Color.class, Person.class})
    public class MyConfiguration{
    	
    }
    
  • 给容器中导入一个MyImportSelector,该Selector是一个实现了ImportSelector接口的类

    @Configuration
    @Import(MyImportSelector.class)
    public class MyConfiguration{
    	
    }
    
    public class MyImportSelector implements ImportSelector {
        
        //重写该方法,返回需要导入到容器中的组件的全类名数组
        @Override 
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[0];
        }
    }
    
    
  • 给容器中导入一个MyImportBeanRegistry,该类是一个实现了ImportBeanDefinitionRegistrar接口的类,可以实现手动向容器中注册一个Bean

    @Configuration
    @Import(MyImportBeanRegistry.class)
    public class MyConfiguration{
    	
    }
    
    public class MyImportBeanRegistry implements ImportBeanDefinitionRegistrar {
        
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            
        }
    }
    
    

FactoryBean

创建一个Spring定义的FactoryBean,并将其加入到容器中

@Configuration
public class MyConfiguration{
	@Bean
	public UserFactoryBean userFactoryBean(){
		return new UserFactoryBean();
	}
}

public class UserFactoryBean implements FactoryBean<User> {
    //返回一个User对象,该对象会添加到容器中
    @Override
    public User getObject() throws Exception {
        return null;
    }

    //返回对象的类型
    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

虽然向容器中添加的是工厂bean(UserFactoryBean),但是实际上调用getObject方法时得到的是User对象,如果想要拿到工厂bean本身,需要在getObject()方法中传入id时加上‘&’前缀,如getObject(‘&userFactoryBean’)

总结注册组件的方式

  1. @ComponentScan包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)【主要用于我们自己写的类】
  2. @Bean【导入的第三方包里面的组件】
  3. @Import(MyClass.class)【快速给容器中导入一个组件】
    • 使用方式:@Import(Color.class, Person.class),给容器中导入Color类和Person类
    • 导入组件的名称默认为其全限定类名
  4. @Import(MyImportSelector.class)
    • 使用方式:定义一个类MyImportSelector去实现ImportSelector接口,重写其中的方法
    • 这种方式返回需要导入的组件的全类名的数组,然后会根据返回的数组中的类名将类加载至ioc容器
  5. @Import(MyImportBeanDefinitionRegistrar.class)
    • 使用方式:定义一个类MyImportBeanDefinitionRegistrar去实现ImportBeanDefinitionRegistrar接口,重写其中的方法
    • 这种方式是直接向ioc容器中注入组件,需要我们自己定义BeanDefination,并使用BeanDefinationRegistrar向ioc的bean注册表里直接添加bean组件
  6. 使用Spring提供的FactoryBean(工厂Bean)
    • 使用方式:定义一个类如ColorFactoryBean去实现FactoryBean,重写其中的方法,主要有getObject、getObjectType、isSingleton(如果要创建单例返回true、多例的返回false)
    • 需要将我们定义的工厂类ColorFactoryBean加入到ioc容器中,在主配置类里去配置
    • 虽然我们注册的是ColorFactoryBean,但是我们通过容器去获取ColorFactoryBean这个Bean的时候,真实返回的却是Color类,我们如果想要获取工厂bean本身,需要通过application.getBean("&ColorFactoryBean")来获取,在Bean的id前方加上“&”前缀

1.2 Bean的生命周期

1.2.1 Bean生命周期的定义

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

这里说的初始化方法并不是平时我们所说的类初始化,这个初始化方法是在类的构造器函数调用之后才会执行的。

1.2.2 Bean生命周期流程

1、构造(对象创建,调用构造函数)

  • 单实例:在容器启动的时候创建对象

  • 多实例:在每次获取的时候创建对象

2、BeanPostProcessor.postProcessBeforeInitialization()方法调用

3、初始化:对象创建完成,并赋值好,调用自定义初始化方法

4、BeanPostProcessor.postProcessAfterInitialization()方法调用

5、销毁:

  • 单实例:容器关闭的时候销毁bean

  • 多实例:容器不会管理这个bean,容器不会调用销毁方法

//bean生命周期展示
cat....创建 //Bean的构造器执行,先创建对象
postProcessBeforeInitialization....pojo.Cat@24c22fe...cat //BeanPostProcessor.postProcessBeforeInitialization()方法调用
cat...initialization //执行自定义的初始化逻辑
postProcessAfterInitialization......pojo.Cat@24c22fe...cat//BeanPostProcessor.postProcessAfterInitialization()
容器创建完成 
cat....destory //容器销毁时调用自定义的销毁方法

1.2.3 操控Bean生命周期各个阶段的方法

1、指定初始化和销毁方法:

  • 在需要注入的类中加入相应的初始化和销毁方法,然后在主配置类中进行配置时,在@Bean注解中指定相应的初始化方法和销毁方法

    public class Car{
    	Car(){}
    	public void init(){} //这是我们自定义的初始化方法
    	public void destroy(){}//这是我们自定义的销毁方法
    }
    @Configuration
    public class MyConfiguration{
    	@Bean(initMethod = "init", destroyMethod = "destroy")
    	public Person Car(){
    		return new Car();
    	}
    }
    
  • 通过让Bean实现InitializingBean接口(定义初始化逻辑),实现DisposableBean接口(定义销毁逻辑)并重写里边的方法

    public class Cat implements InitializingBean, DisposableBean {
        @Override
        public void destroy() throws Exception {
            System.out.println("定义你的销毁逻辑");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("定义你的初始化逻辑");
        }
    }
    
    @Configuration
    public class MyConfiguration{
    	@Bean("cat")
    	public Person Cat(){
    		return new Cat();
    	}
    }
    
  • 可以使用JSR250提供的注解

    1. @PostConstruct,在bean创建完成并且属性赋值完成,来执行初始化方法
    2. @PreDestroy,在bean将要被移除销毁之前通知我们进行清理工作
    public class Car{
    	Car(){}
    	@PostConstruct
    	public void init(){}
    	@PreDestroy
    	public void destroy(){}
    }
    
    

2、指定Bean后置处理器的方法

  • postProcessBeforeInitialization():在bean初始化方法(不是类的初始化)之前调用该方法

  • postProcessAfterInitialization():在bean完成初始化方法(不是类的初始化)之后调用该方法

    @Component
    public class MyBeanProcessor implements BeanPostProcessor {
        /**
         * @param bean : the new bean instance
         * @param beanName :the name of the bean
         * @return
         * @throws BeansException
         */
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("定义初始化方法调用之前的处理逻辑");
            return null;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("定义初始化方法调用之后的处理逻辑");
            return null;
        }
    }
    

1.2.4 BeanPostProcessor的工作原理

  1. 在所有初始化工作开始之前完成对bean的属性赋值(完成bean的创建)populateBean(beanName, mbd, instanceWrapper)

  2. 开始bean的初始化工作:initializeBean(beanName, exposedObject, mbd)

    • applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

      for (BeanPostProcessor processor : getBeanPostProcessors()) {
      				Object current = processor.postProcessBeforeInitialization(result, beanName);
      				if (current == null) {
      					return result;
      				}
      				result = current;
      			}
      
    • invokeInitMethods(beanName, wrappedBean, mbd):自己定义的初始化方法

    • applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

      for (BeanPostProcessor processor : getBeanPostProcessors()) {
      				Object current = processor.postProcessAfterInitialization(result, beanName);
      				if (current == null) {
      					return result;
      				}
      				result = current;
      			}
      

1.2.5 Spring底层对于BeanPostProcessor的应用

Spring底层关于Bean的赋值、注入其他组件、@Autowired,生命周期注解功能、@Async等等功能都使用到了BeanPostProcessor,我们去看一下BeanPostProcessor接口的实现类
在这里插入图片描述
可以举例研究几个,例如ApplicationContextAwareProcessor,他的postProcessBeforeInitialization方法如下所示:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		//这里首先判断下bean属不属于xxxAware类型的,如果不属于就直接返回了
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			acc = this.applicationContext.getBeanFactory().getAccessControlContext();
		}

		if (acc != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareInterfaces(bean);
				return null;
			}, acc);
		}
		else {
			invokeAwareInterfaces(bean);
		}

		return bean;
}

主要逻辑代码在invokeAwareInterfaces()方法中,如下:
就是判断bean属于哪个xxxAware类型,然后给这个bean注入相应的依赖,例如可以给bean传入当前的ioc容器,传入当前的Environment等等…

private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof EnvironmentAware) {
			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
		}
		if (bean instanceof EmbeddedValueResolverAware) {
			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
		}
		if (bean instanceof ResourceLoaderAware) {
			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
		}
		if (bean instanceof ApplicationEventPublisherAware) {
			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
		}
		if (bean instanceof MessageSourceAware) {
			((MessageSourceAware) bean).setMessageSource(this.applicationContext);
		}
		if (bean instanceof ApplicationContextAware) {
			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
		}
}

我们的bean要想使用这个类提供的前置处理来为自己注入ioc容器的依赖,只需要让我们的bean去实现ApplicationContextAware接口,然后重写其中的方法,比如我们想要为我们的bean注入当前的ioc容器,可以如下操作

@Component
public class Cat implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

其余各种大体的研究方式都差不多,有兴趣可以自己多去研究几个。

1.3 Bean属性赋值

在我们自己定义的bean中,如果我们不给类的属性赋值的话,一般来说Spring通过类的无参构造器创建出来的对象,是具有默认值的(引用类型为null,基本类型为基本类型的默认值)

```java
//定义一个组件,重写toString方法,以便后续查看属性赋值的情况
@Component
public class Cat {
    private String name;
    private String master;
	private int age;
	
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", master='" + master + '\'' +
                ", age=" + age +
                '}';
    }
}
//启动容器之后,我们来打印测试一下,spring默认创建出来的对象其属性的默认值是什么
public class Mytest {
    @Test
    public void test(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println("容器创建完成");
        Cat cat = (Cat) context.getBean("cat");
        System.out.println(cat);
        context.close();
    }
}

上面代码打印情况如下:
容器创建完成
Cat{name='null', master='null', age=0}

可以看到,引用类型默认赋值为null,普通类型默认赋值为其规定的默认值

1.3.1 @Value

@Value("mike")
private String name;
@Value("huhx")
private String master;
@Value("#{30 - 6}")
private int age;

打印结果为:
Cat{name='mike', master='huhx', age=24}
  • @Value(“张三”) 使用基本内容
  • @Value(#{20 - 8}) 使用使用EL表达式传入值,格式==#{}==
  • @Value(${person.name}) 传入配置文件中配置的值【properties文件】
    • 在主配置类上使用一个注解来导入配置文件@PropertySource(value = {“classpath:/xxx.properties”}

1.4 自动装配

Spring利用依赖注入完成对IOC容器中各个组件的依赖关系赋值

1.4.1 @Autowire自动注入

  1. 默认优先按照类型去容器中找对应的组件
  2. 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
  3. @Qualifier:使用该注解指定需要装配的组件的id,而不是使用属性名
  4. @Primary,让Spring进行自动装配的时候,默认使用首选的bean进行装配,同时也可以进一步使用@Qualifier来限定装配bean的名称
@Service
public class BookService {
    @Autowire
    private BookDao bookDao;
}

@Repository
public class BookDao {
    
}

1.4.2 @Autowire使用位置

  • 构造器

    如果只有一个有参构造器,这个有参构造器的@Autowire可以省略,参数为止的组件还是可以从容器中获取

    @Bean标注的方法创建对象的时候,方法参数的值从容器中直接获取,不需要标注@Autowire

    @Component
    public class Boss {
        private Car car;
        
        @Autowire
        public Boss(Car car) {
            this.car = car;
        }
        
        public Car getCar() {
            return car;
        }
        
        public void setCar(Car car) {
            this.car = car;
        }
    }
    
  • 方法

    @Component
    public class Boss {
        private Car car;
        
        
        public Boss(Car car) {
            this.car = car;
        }
        
        public Car getCar() {
            return car;
        }
        
        @Autowire
        public void setCar(Car car) {
            this.car = car;
        }
    }
    
  • 参数

1.4.3 Aware接口

当我们注入容器中的类想要获取ioc容器的实例对象、获取运行时环境等等参数的时候,Spring也提供了Aware接口来使bean可以实时获取到自己想要的内容。

xxxAware,他们各自的功能是通过xxxProcessor来实现的

1.4.4 @Profile

Profile:Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能。

开发环境、测试环境、生产环境中的组件是不太相同的,我们希望在不作大量改动的情况下来实现动态切换。

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,默认是default环境。

@Configuration
public class MyConfiguration{
	
	@Bean
    @Profile("test") //生产环境标识
	public Person person(){
		return new Peson();
	}
}

@Profile还可以写在配置类上,只有是在指定的环境时候,整个配置类里面的所有的配置才能开始生效

没有标注环境标识的bean,在认识环境下都是加载的

Question:如何来切换生产环境?

  • 通过代码

    //1.创建一个applicationcontext
    //2.设置需要激活的环境
    applicationContext.getEnvironment().setActiveProfiles("test");
    //3.注册主配置类
    applicationContext.register(MainConfig.class);
    //4.启动刷新容器
    applicationContext.refresh();
    
  • 使用命令行动态参数,在虚拟机参数位置加载

     -Dspring.profiles.active=test
    

2 AOP(面向切面编程)

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

2.1 功能测试

  1. 导入aop模块:Spring AOP,(spring-aspects)

  2. 定义一个业务逻辑类,假设我们现在要在在业务逻辑运行的时候实现将日志进行打印(方法之前,方法运行之后……)

    public class MathCalculator {
    
        public int div(int i, int j) {
            System.out.println("div方法执行,参数为:" + i + "," + j);
            return i / j;
        }
    }
    
  3. 定义一个日志切面类,切面类中的方法需要动态感知到业务类中的方法执行的不同阶段,并在不同阶段进行代码切入

    @Aspect
    public class LogAspectJ {
    
        @Pointcut("execution(public int com.huhx.blog.other.MathCalculator.*(..))")
        public void pointCut() {}
    
        @Before("pointCut()")
        public void logStart() {
            System.out.println("方法执行前");
        }
    
        @After("pointCut()")
        public void logEnd() {
            System.out.println("方法执行后");
        }
    
        @AfterReturning("pointCut()")
        public void logReturn() {
            System.out.println("方法返回后");
        }
    
        @AfterThrowing("pointCut()")
        public void logThrow() {
            System.out.println("方法出现异常");
        }
    }
    

    其中利用**@Pointcut**注解抽取了一个公共切面

    (代码切入)通知方式:

    • 前置通知(@Before):在目标业务方法执行前执行切面类中的某个方法
    • 后置通知(@After):在目标业务方法执行后执行切面类中的某个方法
    • 返回通知(@AfterReturning):在目标业务方法执行返回后执行切面类中的某个方法
    • 异常通知(@AfterThrowing):在目标业务方法执行出现异常时执行切面类中的某个方法
    • 环绕通知(@Around):动态代理,手动推进目标业务方法运行
  4. 给切面类中的各个方法标注何时何地运行(通知注解)

  5. 将切面类和业务逻辑类都加入到IOC容器中

    @EnableAspectJAutoProxy
    @Configuration
    public class MyAopConfig {
    
        @Bean
        public MathCalculator mathCalculator() {
            return new MathCalculator();
        }
    
        @Bean
        public LogAspectJ logAspectJ() {
            return new LogAspectJ();
        }
    
    }
    
  6. 告诉spring哪个是切面类(给切面类上加上一个注解**@Aspect**)

  7. 在配置类上加上**@EnableAspectJAutoProxy**注解【开启基于注解的aop功能】

功能测试结果,新建一个测试类,调用容器中MathCalculator中的div方法,得到如下结果:

方法执行前
div方法执行,参数为:1,1
方法返回后
方法执行后

如果方法出现异常,得到如下结果:

方法执行前
div方法执行,参数为:1,0
方法出现异常
方法执行后

可以在切面类的各个方法中传入**JoinPoint参数,来获取方法业务逻辑方法的名称等信息
在这里插入图片描述
同时在
@AfterReturning、@AfterThrowing注解中分别传入returningthrowing**参数可以分别获取到方法的返回结果以及抛出异常的详细信息。

@Aspect
public class LogAspectJ {

    @Pointcut("execution(public int com.huhx.blog.other.MathCalculator.*(..))")
    public void pointCut() {}

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        System.out.println(joinPoint.getSignature().getName() + "方法执行, 当前在方法执行前");
    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint) {
        System.out.println("参数列表为:" + Arrays.asList(joinPoint.getArgs())  + "   方法执行后");
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(Object result) {
        System.out.println("返回值为:" + result + "   方法返回后");
    }

    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logThrow(Exception exception) {
        System.out.println("方法出现异常, 异常信息为:" + exception);
    }
}

测试结果:

//正常返回时
div方法执行, 当前在方法执行前
div方法执行,参数为:1,1
返回值为:1   方法返回后
参数列表为:[1, 1]方法执行后

//出现异常时
div方法执行, 当前在方法执行前
div方法执行,参数为:1,0
方法出现异常, 异常信息为:java.lang.ArithmeticException: / by zero
参数列表为:[1, 0]   方法执行后

2.2 AOP原理探究

2.2.1 @EnableAspectJAutoProxy注解定义

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {

	/**
	 * Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
	 * to standard Java interface-based proxies. The default is {@code false}.
	 */
	boolean proxyTargetClass() default false;

	/**
	 * Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}
	 * for retrieval via the {@link org.springframework.aop.framework.AopContext} class.
	 * Off by default, i.e. no guarantees that {@code AopContext} access will work.
	 * @since 4.3.1
	 */
	boolean exposeProxy() default false;

}

可以看到该注解上利用**@Import注解给IOC容器中注入了一个AspectJAutoProxyRegistrar**组件,注入模式是采用@Import注解的第三种注入模式,直接向容器中添加bean的定义信息,我们来看一下AspectJAutoProxyRegistrar中复写的方法:

	@Override
	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

		AnnotationAttributes enableAspectJAutoProxy =
				AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
		if (enableAspectJAutoProxy != null) {
			if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

核心在第一句,我们来看一下这个方法中向容器中注入了什么组件,追踪代码,来到AopConfigUtil类下的这个方法中:

	AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
                                    ||
                                    ||
                                    \/
	registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
                                    ||
                                    ||
                                    \/
	registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
                                    ||
                                    ||
                                    \/
	@Nullable
	private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

        //AUTO_PROXY_CREATOR_BEAN_NAME =
		//	"org.springframework.aop.config.internalAutoProxyCreator"
        //如果registy中包含有上面那个名称定义的类,直接返回null
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        //如果没有上面那个类的定义,则新建一个该类的defination,名称为internalAutoProxyCreator
		//真实的类为AnnotationAwareAspectJAutoProxyCreator
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}

也就是说,通过使用@EnableAspectJAutoProxy注解,我们给容器中注入了一个==AnnotationAwareAspectJAutoProxyCreator==这个类,下面我们来研究下这个类起到了什么作用。

2.2.2 AnnotationAwareAspectJAutoProxyCreator类的作用

类的结构:
在这里插入图片描述
可以看到该类实现了SmartInstantiationAwareBeanPostProcessor接口与BeanFactoryAware接口

3 声明式事务

3.1 @Transactional注解

  1. 要支持事务功能,需要在配置类上使用**@EnableTransactionManagement**

  2. 给方法上标注@Transactional注解,表示当前方法是一个事务方法,如果该方法中涉及对数据库的修改操作,若该方法出现异常的时候,数据库会进行回滚。

  3. 在配置类中配置事务管理器(xxxTransactionManage)

3.2 事务的传播机制

  • REQUIRED:当前方法需要在一个事务中运行,如果方法运行时已经处在一个事务中,则加入到当前事务,否则就自己创建一个事务
  • SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外调用,该方法就在没有事务的环境下执行
  • MANDATORY:当前方法必须被已有事务的方法进行调用,否则会抛出异常
  • REQUIRES_NEW:不管是否存在事务,当前方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建
  • NOT_SUPPORTED:当前方法运行不需要事务,如果方法没有关联事务,则不会为他开启事务,如果当前方法在一个事务中被调用,该事务会被挂起,调用结束后,原先事务会恢复执行
  • NEVER:该方法不能在事务范围内执行。如果在就抛出异常,只有方法没有关联到任何事务,才能正常执行
  • NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按照REQUIRED属性执行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。

4 拓展原理

4.1 BeanFactoryPostProcessor

  • **BeanPostProcessor:**bean的后置处理器

  • BeanFactoryPostProcessor:beanFactory的后置处理器,在BeanFactory的标准初始化之后调用,所有的bean定义已经保存加载到了beanFactory,但是bean的实例还未创建

我们来测试一下BeanFactoryPostProcessor这个接口

  1. 首先定义一个类来实现BeanFactoryPostProcessor
  2. 重写其中的方法
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanFactoryPostProcessor.....");
        int count = beanFactory.getBeanDefinitionCount();
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        System.out.println("bean count: " + count);
        System.out.println("bean names: " + Arrays.asList(beanDefinitionNames));
    }
}

我们来看一下postProcessBeanFactory()这个方法的调用时机,它其实是在对象还没有创建之前调用的,定义一个Cat组件

@Component
public class Cat {
    @Value("mike")
    private String name;
    @Value("huhx")
    private String master;
    @Value("#{30 - 6}")
    private int age;

    public Cat(){
        System.out.println("cat init.......");
    }
    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                ", master='" + master + '\'' +
                ", age=" + age +
                '}';
    }
}

启动容器后的执行结果为:

MyBeanFactoryPostProcessor.....
bean count: 9
bean names: [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, myConfig, cat, myBeanFactoryPostProcessor, org.springframework.aop.config.internalAutoProxyCreator]
cat init.......
容器创建完成

可以看到,在Cat组件初始化之前,postProcessBeanFactory()方法已经被调用了,并且返回了当前容器中的bean数量已经bean的名称,但是这些bean都还没有被创建出来,只是有定义而已

【原理】

  1. ioc容器创建对象
  2. 刷新容器,调用refresh()方法中invokeBeanFactoryPostProcessors(beanFactory)方法。
    • 直接在BeanFacory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法
    • 在初始化创建其他组件前面执行

4.2 BeanDefinitionRegistryPostProcessor

这个接口是BeanFactoryPostProcessor的子接口,它内部有一个方法:
postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
这个方法在所有bean定义信息将要被加载,但是bean实例还未创建的时候执行,因此这个方法调用的时机比BeanFactoryPostProcessor接口中的方法要早,我们可以利用它来给容器中再额外添加一些组件。

BeanDefinitionRegistry是bean注册信息的保存中心,以后BeanFactory就是按照BeanDefinationRegistry里面保存的每一个bean定义信息创建bean实例

简单测试一下:我们直接写一个类去实现BeanDefinitionRegistryPostProcessor 接口,重写里边的两个方法:

@Component
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("postProcessBeanFactory.....");
        int count = beanFactory.getBeanDefinitionCount();
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        System.out.println("bean count: " + count);
        System.out.println("bean names: " + Arrays.asList(beanDefinitionNames));
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("postProcessBeanDefinitionRegistry......");
        System.out.println("当前容器中bean的个数 : " + registry.getBeanDefinitionCount());

        //向容器中新注册一个组件
        BeanDefinition beanDefinition = new RootBeanDefinition(Div.class);
        registry.registerBeanDefinition("hello", beanDefinition);

        System.out.println("添加后容器中bean的个数:" + registry.getBeanDefinitionCount());
    }
}

启动容器后的测试结果:

postProcessBeanDefinitionRegistry......
当前容器中bean的个数 : 10
添加后容器中bean的个数:11
postProcessBeanFactory.....
bean count: 11
bean names: [org.springframework.context.annotation.internalConfigurationAnnotationProcessor, org.springframework.context.annotation.internalAutowiredAnnotationProcessor, org.springframework.context.annotation.internalCommonAnnotationProcessor, org.springframework.context.event.internalEventListenerProcessor, org.springframework.context.event.internalEventListenerFactory, myConfig, cat, div, myBeanFactoryPostProcessor, org.springframework.aop.config.internalAutoProxyCreator, hello]
cat init.......
容器创建完成

可以看到明显postProcessBeanDefinitionRegistry()方法的执行是在postProcessBeanFactory()方法之前的,并且我们新加的一个bean,“hello”也被添加到了容器中

【原理】

  1. 创建ioc容器
  2. refresh() -> invokeBeanFactoryPostProcessors()
  3. 从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
    • 依次触发所有的postProcessBeanDefinitionRegistry()方法
    • 再来触发BeanFactoryPostProcessor中的postProcessBeanFactory()方法

4.3 ApplicationListener

用于监听容器中发布的事件。完成事件驱动模型开发

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener

监听ApplicationEvent及其下面的子事件,当有事件发布的时候,其中onApplicationEvent()方法就会被调用。

使用步骤:

  1. 写一个监听器来监听某个事件(ApplicationEvent及其下面的子事件),实现ApplicationListener接口并且实现里边的方法
  2. 把监听器加入容器
  3. 只要容器中有相关事件的发布,我们就能监听到这个事件:
    • ContextRefreshedEvent:容器刷新完成(所有的bean都完成创建)会发布这个事件
    • ContextClosedEvent:关闭容器会发布这个事件
  4. 发布一个事件:applicationContext.publishEvent()

【事件发布的流程】:
1、容器创建对象:refresh()
2、finishRefresh():容器刷新完成
3、publishEvent(new ContextRefreshedEvent(this));

  • 获取事件的多播器(派发器):getApplicationEventMulticaster()

  • multicastEvent派发事件

  • 获取到所有的ApplicationListener

    1. 如果有Executor,可以支持使用Executor进行异步派发
    executor.execute(() -> invokeListener(listener, event)); 
    
    1. 否则,同步的方式直接执行listener方法,invokeListener(listener, event);拿到listener回调onApplicationEvent()方法

4.4 @EventListener

该注解可以让任何方法来监听spring容器中的事件,跟实现Listener接口是一样的

原理:

5 Spring容器的启动流程

  1. prepareRefresh():刷新前的预处理

    • initPropertySources() :初始化一些属性设置;留给子类去重写的方法,可以自定义的去进行一些属性设置
    • getEnvironment().validateRequiredProperties() :检验属性的合法等
    • this.earlyApplicationEvents = new LinkedHashSet<>() :保存容器中的一些早期事件
  2. obtainFreshBeanFactory():获取BeanFactory;

    • refreshBeanFactory():刷新【创建】beanFactory
      在GenericApplicationContext对象构造时,beanFactory就已经被构造出来了:this.beanFactory=new DefaultListableBeanFactory(),在这一步只是设置了一个序列化的id
    • getBeanFactory();返回刚才GenericApplicationContext创建的BeanFactory【DefaultListableBeanFactory】对象
  3. prepareBeanFactory(beanFactory):BeanFactory的预准备工作(对BeanFactory进行一些设置)

    • 设置BeanFactory的类加载器、支持表达式解析器
    • 添加部分BeanPostProcessor【ApplicationContextAwareProcessor】
    • 设置忽略的自动装配的接口EnvironmentAware、EmbeddedValueResolverAware
    • 注册可以解析的自动装配;我们能直接在任何组件中自动注入:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext
    • 添加BeanPostProcessor【ApplicationListenerDetector】
    • 添加编译时的AspectJ
    • 给BeanFactory中注册一些能用的组件:
      environment【ConfigurableEnvironment】、systemProperties【Map<String,Object>】、systemEnvironment【Map<String,Object>】
  4. postProcessBeanFactory(beanFactory):BeanFactory准备工作完成后进行的后置处理工作

    • 子类通过重写这方法在BeanFactory创建并预准备完成以后做进一步的设置

++++++++++++++++++++++以上是BeanFactory的创建及预准备工作+++++++++++++++++++++++++++++

  1. invokeBeanFactoryPostProcessors(beanFactory):执行BeanFactoryPostProcessor的方法

    BeanFactoryPostProcessor:BeanFactory的后置处理器,在BeanFactory标准初始化之后执行两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor接口

    • 执行BeanFactoryPostProcessor的方法:
      • 获取所有BeanDefinitionRegistryPostProcessor
      • 先执行实现了PriorityOrdered优先级接口的、再执行实现了Ordered的接口的、最后执行其它的
      • 获取所有BeanFactoryPostProcessor
      • 先执行实现了PriorityOrdered优先级接口的、再执行实现了Ordered的接口的、最后执行其它的
  2. registerBeanPostProcessors(beanFactory):注册BeanPostProcessor(Bean的后置处理器)【拦截Bean的创建过程】

    不同类型的BeanPostProcessor,在Bean创建前后的执行时机是不一样的
    有如下几类: BeanPostProcessor、DestructionAwareBeanPostProcessor、InstantiationAwareBeanPostProcessor、SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor【internalPostProcessors】

    • 获取所有的BeanPostProcessor;
      后置处理器都默认可以通过PriorityOrdered、Ordered来指定优先级
    • 先注册PriorityOrdered优先级接口的BeanPostProcessor
      把每一个BeanPostProcessor添加到BeanFactory中,beanFactory.addBeanPostProcessor(postProcessor);
    • 再注册了实现Ordered接口的
    • 最后注册其它的
    • 最终注册MergedBeanDefinitionPostProcessor类型的
    • 注册一个ApplicationListenerDetector;来在Bean创建完成后检查是否是ApplicationListener:addApplicationListener((ApplicationListener<?>) bean);
  3. initMessageSource():初始化MessageSource组件(做国际化功能;消息绑定;消息解析等功能)

    • 获取BeanFactory
    • 看容器中是否有id为messageSource,类型是MessageSource的组件,如果有赋值给messageSource,如果没有自己创建一个DelegatingMessageSource;
      MessageSource:取出国际化配置文件中的某个key的值;能按照区域信息获取;
    • 把创建好的MessageSource注册在容器中,以后获取国际化配置文件的值的时候,可以自动注入MessageSource;调用其方法可以获得相关配置属性:beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
  4. initApplicationEventMulticaster():初始化事件派发器【多播器】

    • 获取BeanFactory
    • 从BeanFactory获取applicationEventMulticaster的组件
    • 如果上一步没有配置;创建一个SimpleApplicationEventMulticaster
    • 将创建的ApplicationEventMulticaster添加到BeanFactory中,以后其他组件直接自动注入
  5. onRefresh()—留给子容器(子类)

    • 子类重写这个方法,在容器刷新的时候可以自定义逻辑——SpringMVC九大组件的初始化;
  6. registerListeners():将所有项目里面的ApplicationListener注册到容器中来

    • 从容器中拿到所有ApplicationListener组件
    • 将每个监听器添加到事件派发器中:getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    • 派发之前步骤产生的事件;
  7. finishBeanFactoryInitialization(beanFactory):初始化所有剩下的单实例bean

    • beanFactory.preInstantiateSingletons() 初始化剩下的单实例bean

      1)、获取容器中的所有BeanDefination,依次进行初始化和创建对象

      List beanNames = new ArrayList<>(this.beanDefinitionNames);
      

      2)、遍历beanNames获取bean 的定义信息;

      RootBanDefinition bd = getMergedLocalBeanDefinition(beanName);
      

      3)、Bean不是抽象的,是单实例的,是懒加载;

      • 判断是否是FactoryBean;是否是实现FactoryBean接口的Bean;
        • 如果是,利用工厂方法创建对象
        • 不是工厂Bean,利用getBean(过程如下)(beanName);创建对象
    • getBean()方法流程

      1. AbstractBeanFactory.doGetBean();

      2. 先获取缓存中保存的单实例bean,如果能获取到,说明这Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来)从singletonObjects=new ConcurrentHashMap<String,Object>中获取到

      3. 缓存中获取不到,开始Bean的创建对象流程;

      4. 标记当前Bean已经被创建,markBeanAsCreated(beanName);

      5. 获取Bean的定义信息

      6. 获取当前Bean依赖的其它Bean;如果有,按照getBean()把依赖的Bean先创建出来

      7. 启动单实例Bean的创建流程

        • createBean(beanName,mbd,args);

          • 先让BeanPostProcessor【InstantiationAwareBeanPostProcessor】先拦截返回代理对象,先触发所有该接口的postProcessBeforeInstantiation()方法,如果有返回对象,调用applyBeanPostProcessorsAfterInitialization(),即会执行所有的BeanPostProcessor的postProcessAfterInitialization()方法,将bean返回

            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            
          • 如果没有返回bean,调用doCreateBean();

            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            
            • 利用工厂方法或者对象的构造器等创建bean实例

              createBeanInstance(beanName, mbd, args);
              
              
            • 调用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition()

              applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
              
            • 为bean的属性赋值

              populateBean(beanName, mbd, instanceWrapper);
              
              • 拿到InstantiationAwareBeanPostProcessor类型的后置处理器,执行postProcessAfterInstantiation();

              • 拿到InstantiationAwareBeanPostProcessor类型的后置处理器,执行postProcessProperties();

              • 应用Bean属性的值,为属性利用setter方法等进行赋值

                applyPropertyValues(beanName, mbd, bw, pvs)
                
            • Bean初始化

              initializeBean(beanName, exposedObject, mbd);
              
              • 执行xxxAware接口的方法

                invokeAwareMethods(beanName, bean);
                BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
                
              • 执行后置处理器在初始化之前

                applyBeanPostProcessorsBeforeInitialization();
                BeanPostProcessor.postProcessBeforeInitialization();
                
              • 执行初始化方法

                invokeInitMethods();
                
                • 判断是否是InitializingBean接口的实现;执行接口规定的初始化
                • 是否自定义初始化方法
              • 执行后置处理器在初始化之后

                applyBeanPostProcessorsAfterInitialization();
                
            • 注册Bean的销毁方法

              registerDisposableBeanIfNecessary(beanName, bean, mbd)
              
          • Bean的实例创建完成,将bean添加到缓存中

            addSingleton(beanName, singletonObject);
            
          • 所有Bean都利用getBean创建完成以后;检查所有的Bean是否是SmartInitializingSingleton接口类型的,如果是就执行 afterSingletonsInstantiated()方法

  8. finishRefresh():完成BeanFactory初始化创建工作;IOC容器就创建完成

    • initLifecycleProcessor();初始化声明周期有关的后置处理器

      允许我们写一个LifecycleProcessor的实现类,可以在BeanFactory进行到特定生命周期时进行调用
      
      默认从容器中找是否有LifeCycleProcessor的组件,如果没有,默认会创建一个
      
      new DefaultLifecycleProcessor();然后加入到容器中
      
    • getLifecycleProcessor().onRefresh() 拿到所有前面定义的生命周期处理器回调onRefresh()

    • publishEvent(new ContextRefreshedEvent(this)) 发布容器刷新完成事

    • LiveBeansView.registerApplicationContext(this);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值