博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring的注解驱动开发
阅读量:4224 次
发布时间:2019-05-26

本文共 26727 字,大约阅读时间需要 89 分钟。

文章目录

Java的注解

注解的概念

注释:用文字描述程序,给程序员看的;

注解:说明程序的,给计算机看的。

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

概念描述:

  • JDK1.5之后的新特性

  • 说明程序的

  • 使用注解:@注解名称

作用分类:

  1. 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
/** * 注解javadoc演示 * * @version 1.0 * @since 1.5 */public class AnnoDemo1 {
/** * 计算两数的和 * @param a 整数 * @param b 整数 * @return 两数的和 */ public int add(int a, int b ){
return a + b; }}
  1. 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
  2. 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【@override】

JDK中预定义的一些注解:

  • @Override:检测被该注解标注的方法是否是继承自父类(接口)的
  • @Deprecated:该注解标注的内容,表示已过时
  • @SuppressWarnings:压制警告。一般传递参数all,@SuppressWarnings("all")

自定义注解

格式:

public @interface 注解名称{
属性列表;}

本质:注解本质上就是一个接口,该接口默认继承Annotation接口

public interface MyAnno extends java.lang.annotation.Annotation {}

属性:接口中的抽象方法

属性的返回值类型有下列取值:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上类型的数组
public @interface MyAnno {
int value(); Person per(); MyAnno2 anno2(); String[] strs(); String name() default "张三";}

定义了属性,在使用时需要给属性赋值:

  1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值;
  2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可;
  3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略。
@MyAnno(value=12, per = Person.P1, anno2 = @MyAnno2, strs="bbb")public class Worker {
}

元注解

元注解:用于描述注解的注解

  • @Target:描述注解能够作用的位置。ElementType取值:
    • TYPE:可以作用于类上
    • METHOD:可以作用于方法上
    • FIELD:可以作用于成员变量上
  • @Retention:描述注解被保留的阶段

@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。

  • @Documented:描述注解是否被抽取到API文档中
  • @Inherited:描述注解是否被子类继承
@Target({
ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface MyAnno3 {
}@MyAnno3public class Worker {
@MyAnno3 public String name = "aaa"; @MyAnno3 public void show(){
}}

注解解析

在程序使用(解析)注解:获取注解中定义的属性值

@Pro(className = "top.tjtulong.annotation.Demo1",methodName = "show")public class ReflectTest {
public static void main(String[] args) throws Exception {
Pro an = reflectTestClass.getAnnotation(Pro.class); // 调用注解对象中定义的抽象方法,获取返回值 String className = an.className(); String methodName = an.methodName(); System.out.println(className); System.out.println(methodName); }}
  1. 获取注解定义的位置的对象(Class,Method,Field);
    2. 获取指定的注解:getAnnotation(Class),其实就是在内存中生成了一个该注解接口的子类实现对象。
public class ProImpl implements Pro{
public String className(){
return "top.tjtulong.annotation.Demo1"; } public String methodName(){
return "show"; }}
  1. 调用注解中的抽象方法获取配置的属性值

Spring中的注解

组件注册

@Configuration:指定配置类;

@Component:用于把当前类对象存入spring容器中;

以下三个注解的作用与@Component完全一样,它们是spring提供的更明确的划分,使三层对象更加清晰:

@Controller:用于表现层;

@Service:用于业务层;

@Repository:用于持久层;

@Bean:给容器中注册一个Bean对象,类型为返回值的类型,id默认是用方法名作为id;

@ComponentScans:指明多个注解扫描的包;

@ComponentScan:指明注解扫描的包,属性包括:

  • excludeFilters = Filter[] :指定扫描的时候按照什么规则排除哪些组件
  • includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
    • FilterType.ANNOTATION:按照注解类型过滤
    • FilterType.ASSIGNABLE_TYPE:按照给定的类型过滤
    • FilterType.ASPECTJ:使用ASPECTJ表达式过滤
    • FilterType.REGEX:使用正则指定
    • FilterType.CUSTOM:使用自定义规则过滤

@Scope:调整Bean实例的作用域,共有4种:

  • prototype:多实例的,ioc容器启动并不会去调用方法创建对象放在容器中,而是每次获取的时候才会调用方法创建对象;
  • singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就是直接从容器中拿;
  • request:同一次请求创建一个实例;
  • session:同一个session创建一个实例。

@Lazy:懒加载,容器启动不创建对象,第一次获取Bean时再创建对象并初始化;

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.context.annotation.ComponentScan.Filter;import org.springframework.context.annotation.ComponentScans;import top.tjtulong.bean.Person;// 配置类==配置文件@Configuration  // 告诉Spring这是一个配置类@ComponentScans(    value = {
@ComponentScan(value="top.tjtulong",includeFilters = {
@Filter(type=FilterType.ANNOTATION, classes={
Controller.class}), @Filter(type=FilterType.ASSIGNABLE_TYPE, classes={
BookService.class}), @Filter(type=FilterType.CUSTOM, classes={
MyTypeFilter.class}) },useDefaultFilters = false) })public class MainConfig {
//给容器中注册一个Bean,类型为返回值的类型,id默认是用方法名作为id @Scope("prototype") @Bean("person") public Person person01(){
return new Person("lisi", 20); }}public class Person {
private String name; private Integer age; //get() set() toString()... }

自定义的包扫描过滤规则:

public class MyTypeFilter implements TypeFilter {
/** * metadataReader:读取到的当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// TODO Auto-generated method stub //获取当前类注解的信息 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; }}

@Condition:按照一定的条件进行判断,满足条件给容器中注册bean,可以放在类上,也可以放在方法上;

@Import:导入组件,id默认是组件的全类名;

//类中组件统一设置,满足当前条件,这个类中配置的所有bean注册才能生效;@Conditional({
WindowsCondition.class})@Configuration@Import({
Color.class, Red.class, MyImportBeanDefinitionRegistrar.class})public class MainConfig2 {
/** * 如果系统是windows,给容器中注册("bill") * 如果是linux系统,给容器中注册("linus") */ @Bean("bill") public Person person01(){
return new Person("Bill Gates",62); } @Conditional(LinuxCondition.class) @Bean("linus") public Person person02(){
return new Person("linus", 48); }}

Condition类:

//判断是否linux系统public class LinuxCondition implements Condition {
/** * ConditionContext:判断条件能使用的上下文(环境) * AnnotatedTypeMetadata:注释信息 */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系统 //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("linux")){
return true; } return false; }}

实现ImportBeanDefinitionRegistrar手动注册bean到容器中:

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/** * AnnotationMetadata:当前类的注解信息 * BeanDefinitionRegistry:BeanDefinition注册类; * 把所有需要添加到容器中的bean;调用 * BeanDefinitionRegistry.registerBeanDefinition手工注册进来 */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("top.tjtulong.bean.Red"); boolean definition2 = registry.containsBeanDefinition("top.tjtulong.bean.Blue"); if(definition && definition2){
//指定Bean定义信息 RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); //注册一个Bean,指定bean名 registry.registerBeanDefinition("rainBow", beanDefinition); } }}

给容器中注册组件方法汇总:

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
  2. @Bean[导入的第三方包里面的组件]
  3. @Import[快速给容器中导入一个组件]
    • @Import(要导入到容器中的类);容器中就会自动注册这个组件,id默认是全类名;
    • ImportSelector:返回需要导入的组件的全类名数组;
    • ImportBeanDefinitionRegistrar:手动注册bean到容器中;
  4. 使用Spring提供的 FactoryBean(工厂Bean);
    • 默认获取到的是工厂bean调用getObject创建的对象;
    • 要获取工厂Bean本身,需要给id前面加一个&(&colorFactoryBean);
@Beanpublic ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();}//创建一个Spring定义的FactoryBeanpublic class ColorFactoryBean implements FactoryBean
{
//返回一个Color对象,这个对象会添加到容器中 @Override public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject..."); return new Color(); } @Override public Class
getObjectType() {
return Color.class; } //是否为单例 @Override public boolean isSingleton() {
return false; }}

生命周期

bean的生命周期是指:bean创建---->初始化---->销毁的过程

方法一:通过@Bean指定初始化和销毁方法

initMethod:初始化方法,destroyMethod:销毁方法。

@Componentpublic class Car {
public Car(){
System.out.println("car constructor..."); } public void init(){
System.out.println("car ... init..."); } public void detory(){
System.out.println("car ... detory..."); }}
@ComponentScan("top.tjtulong.bean")@Configurationpublic class MainConfigOfLifeCycle {
//@Scope("prototype") @Bean(initMethod="init", destroyMethod="detory") public Car car(){
return new Car(); }}

方法二:通过让Bean实现InitializingBean接口(定义初始化逻辑),DisposableBean接口(定义销毁逻辑)

@Componentpublic class Cat implements InitializingBean,DisposableBean {
public Cat(){
System.out.println("cat constructor..."); } @Override public void destroy() throws Exception {
System.out.println("cat...destroy..."); } @Override public void afterPropertiesSet() throws Exception {
System.out.println("cat...afterPropertiesSet..."); }}

方法三:可以使用JSR250

  • @PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法;
  • @PreDestroy:在容器销毁bean之前通知我们进行清理工作;
@Componentpublic class Dog implements ApplicationContextAware {
public Dog(){
System.out.println("dog constructor..."); } //对象创建并赋值之后调用 @PostConstruct public void init(){
System.out.println("Dog....@PostConstruct..."); } //容器移除对象之前 @PreDestroy public void detory(){
System.out.println("Dog....@PreDestroy..."); }}

以上三种初始化方法的执行顺序为:Constructor > @PostConstruct >InitializingBean > init-method

三种销毁方法的执行顺序为:@preDestroy >DisposableBean接口 > @Bean(destroyMethod)

方法四:BeanPostProcessor接口(bean的后置处理器)

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

  • postProcessBeforeInitialization:在初始化之前工作,在上面三个方法执行前执行
  • postProcessAfterInitialization:在初始化之后工作,在上面三个方法执行后执行
/** * 后置处理器:初始化前后进行处理工作 * 将后置处理器加入到容器中 */@Componentpublic 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; }}

BeanPostProcessor的原理

refresh()方法中的finishBeanFactoryInitialization(beanFactory);方法执行时bean进行初始化。

step1:populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值

step2:initializeBean();初始化bean对象

applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);invokeInitMethods(beanName, wrappedBean, mbd); //执行自定义初始化applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

遍历得到容器中所有的BeanPostProcessor,逐个执行beforeInitialization,一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization()

p.s. bean的赋值、注入其它组件、@Autowired、生命周期注解功能、@Async等功能都是通过BeanPostProcessor实现。

参考:https://blog.csdn.net/weixin_34054931/article/details/91397723

属性赋值

使用@Value赋值:

  1. 基本数值
  2. 可以写SpEL:#{}
  3. 可以写${},取出配置文件【properties】中的值(在运行环境变量里面的值)
public class Person {
@Value("张三") private String name; @Value("#{20-2}") private Integer age; @Value("${person.nickName}") private String nickName;}

配置类:

// 使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;// 加载完外部的配置文件以后使用${}取出配置文件的值@PropertySource(value={
"classpath:/person.properties"})@Configurationpublic class MainConfigOfPropertyValues {
@Bean public Person person(){
return new Person(); }}

自动装配

自动装配:Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值。

@Autowired:自动注入,可以放在构造器,参数,方法,属性

BookService {
@Autowired BookDao bookDao;}

默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class),找到就赋值;

如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。

@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名;

自动装配默认一定要将属性赋值好,没有就会报错;可以使用@Autowired(required=false)

@Primary:放在注解@Bean上,让Spring进行自动装配的时候,默认使用首选的bean;

Spring还支持使用@Resource(JSR250)和@Inject(JSR330) [java规范的注解]:

@Resource:可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配的;但没有能支持@Primary功能,也没有支持@Autowired(reqiured=false);

@Inject:需要导入javax.inject的包,和@Autowired的功能一样,但没有required=false的功能;

p.s. @Autowired是Spring定义的;而@Resource、@Inject都是Java的规范。

都是通过AutowiredAnnotationBeanPostProcessor解析完成自动装配功能。

xxxAware接口

在Spring中有很多以Aware结尾的接口,如果一个Bean实现了该接口,那么当该Bean被Spring初始化时,Spring会向该Bean注入相关资源(就是会回调接口中的方法)。作用是把Spring底层一些组件注入到自定义的Bean中

示例:下面的TestService实现了两个接口BeanNameAwareApplicationContextAware接口。当Spring对bean进行初始化时,Spring会调用接口对应的方法。这样就可以获取到spring中的资源。

@Componentpublic class TestService implements BeanNameAware,ApplicationContextAware {
private String beanName; private ApplicationContext context; @Override // 获取到bean的名称 public void setBeanName(String name) {
System.out.println("name = " + name); beanName = name; } @Override // 获取上下文环境ApplicationContext public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("applicationContext = " + applicationContext); context = applicationContext; }}

常用的Aware相关接口作用:

接口名称 作用
ApplicationContextAware 获取spring 上下文环境的对象
BeanNameAware 获取该bean在BeanFactory配置中的名字
BeanFactoryAware 创建它的BeanFactory实例
ServletContextAware 获取servletContext容器
ResourceLoaderAware 获取ResourceLoader对象,通过它获得各种资源

通过ApplicationContextAwareProcessor实现。

具体实现参考:https://blog.csdn.net/baidu_19473529/article/details/81072524

环境搭建

@Profile:指定组件在哪个环境的情况下才能被注册到容器中,若不指定,任何环境下都能注册这个组件。

便于开发环境、测试环境、生产环境之间的切换

  1. 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境;
  2. 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效;
  3. 没有标注环境标识的bean,在任何环境下都是加载的;
@PropertySource("classpath:/dbconfig.properties")@Configurationpublic class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}") private String user; private StringValueResolver valueResolver; private String driverClass; @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) {
this.valueResolver = resolver; driverClass = valueResolver.resolveStringValue("${db.driverClass}"); }}

设置运行时设置profile的方式:

  1. 使用命令行动态参数: 在虚拟机参数位置加上-Dspring.profiles.active=test
  2. 代码的方式激活某种环境:
//1、使用无参构造创建一个applicationContextAnnotationConfigApplicationContext applicationContext = 				new AnnotationConfigApplicationContext();		//2、设置需要激活的环境applicationContext.getEnvironment().setActiveProfiles("dev");//3、注册主配置类applicationContext.register(MainConfigOfProfile.class);//4、启动刷新容器applicationContext.refresh();

AOP的原理

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

AOP的使用

step:

  1. 导入Spring-AOP模块;
  2. 定义一个业务逻辑类,在业务逻辑运行的时候将日志进行打印(方法执行之前、方法运行结束、方法出现异常,方法返回前);
  3. 定义一个日志切面类,切面类里面的方法需要动态感知业务逻辑类运行到哪里然后执行;
  4. 编写通知方法:
    • 前置通知(@Before):在目标方法运行之前运行;
    • 后置通知(@After):在目标方法运行结束之后运行(无论方法正常结束还是异常结束);
    • 返回通知(@AfterReturning):在目标方法正常返回之后运行;
    • 异常通知(@AfterThrowing):在目标方法出现异常以后运行;
    • 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced());
  5. 给切面类的目标方法标注何时何地运行(通知注解);
  6. 将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
  7. 必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect);
  8. 给配置类中加**@EnableAspectJAutoProxy**,开启基于注解的aop模式。
@Component("logger")@Aspect//表示当前类是一个切面类public class Logger {
// 配置切入点 @Pointcut("execution(* io.github.tjtulong.service.impl.*.*(..))") private void pt1(){
} /** * 前置通知 */ @Before("pt1()") public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。"); } /** * 后置通知 */ @AfterReturning("pt1()") public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。"); } /** * 异常通知 */ @AfterThrowing("pt1()") public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。"); } /** * 最终通知 */ @After("pt1()") public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。"); }}

阅读Sring源码的核心是:看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么。

AOP的源码

@EnableAspectJAutoProxy注解

@EnableAspectJAutoProxy的作用:

  • @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
  • 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;
  • 给容器中注册的Bean为**AnnotationAwareAspectJAutoProxyCreator**,id为internalAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator类的层级结构图:

注意:实现了SmartInstantiationAwareBeanPostProcessorBeanFactoryAware接口,说明该类为后置处理器(在bean初始化完成前后做事情)并自动装配了BeanFactory。

创建和注册AnnotationAwareAspectJAutoProxyCreator的过程

step1:传入配置类,创建ioc容器;

step2:注册配置类,调用refresh()刷新容器;

step3:registerBeanPostProcessors(beanFactory),注册bean的后置处理器来方便拦截bean的创建;

  • 先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
  • 给容器中加别的BeanPostProcessor
  • 优先注册实现了PriorityOrdered接口的BeanPostProcessor;
  • 再给容器中注册实现了Ordered接口的BeanPostProcessor;
  • 注册没实现优先级接口的BeanPostProcessor;
  • 注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;对于**AnnotationAwareAspectJAutoProxyCreator**,创建一个id为internalAutoProxyCreator的BeanPostProcessor,流程为:
1. 创建Bean的实例;2. populateBean():给bean的各种属性赋值;3. initializeBean():初始化bean;    (1) invokeAwareMethods():处理Aware接口的方法回调    (2) applyBeanPostProcessorsBeforeInitialization():执行所有后置处理器的postProcessBeforeInitialization()    (3) invokeInitMethods();执行自定义的初始化方法    (4) applyBeanPostProcessorsAfterInitialization():执行所有后置处理器的postProcessAfterInitialization()4. BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;
  • 把BeanPostProcessor注册到BeanFactory中:beanFactory.addBeanPostProcessor(postProcessor);

step4:finishBeanFactoryInitialization(beanFactory):完成BeanFactory初始化工作,创建剩下的单实例bean。

  • 遍历获取容器中所有的Bean,依次创建对象getBean(beanName);

  • 创建bean。AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()方法,其流程如下:

1. 先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来2. createBean(),创建bean;    (1)AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean的实例    【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】	【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】  		    (a) resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation  			希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续(b)  				bean = applyBeanPostProcessorsBeforeInstantiation():  			    拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor,就执行postProcessBeforeInstantiation  				if (bean != null) {					bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);				}  			(b) doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例,与step3中一致

AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】的作用:

  1. 每一个bean创建之前,调用postProcessBeforeInstantiation()

判断当前bean是否在advisedBeans中(保存了所有需要增强的bean)

判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)

  1. 创建对象---->postProcessAfterInitialization
return wrapIfNecessary(bean, beanName, cacheKey); //包装如果需要的情况下1. 获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors	(1) 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的);    (2) 获取到能在bean使用的增强器;    (3) 给增强器排序.2. 保存当前bean在advisedBeans中;3. 如果当前bean需要增强,创建当前bean的代理对象;    (1) 获取所有增强器(通知方法)    (2) 保存到proxyFactory    (3) 创建代理对象:Spring自动决定 			JdkDynamicAopProxy(config);jdk动态代理;			ObjenesisCglibAopProxy(config);cglib的动态代理;4. 给容器中返回当前组件使用jdk/cglib增强了的代理对象;5. 以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

目标方法的执行:

CglibAopProxy.intercept():拦截目标方法的执行

核心:拦截器链

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try {
... target = targetSource.getTarget(); Class
targetClass = (target != null ? target.getClass() : null); // 重点:将所有的MethodInterceptor串成一个链 List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; ... // proxy为动态代理后的bean // target为原bean // proceed方法为处理之前提到的chain上所有methodInterceptor // 最后一个methodinterceptor调用methodProxy的invoke方法 // methodProxy.invoke(this.target, this.arguments); retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target); } if (setProxyContext) {
// Restore old proxy. AopContext.setCurrentProxy(oldProxy); } }}

拦截器链的触发过程:

  • 如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
  • 链式获取每一个拦截器,拦截器执行invoke方法**,每一个拦截器等待下一个拦截器执行完成返回以后再来执行**;拦截器链的机制,保证通知方法与目标方法的执行顺序。

参考:https://www.jianshu.com/p/1b557d22fad3

Spring的事务控制

声明式事务

@EnableTransactionManagement@ComponentScan("top.tjtulong.tx")@Configurationpublic class TxConfig {
//数据源 @Bean public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource(); dataSource.setUser("root"); dataSource.setPassword("123456"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test"); return dataSource; } // 创建JdbcTemplate组件 @Bean public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); return jdbcTemplate; } //注册事务管理器在容器中 @Bean public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource()); }}

源码分析

@EnableTransactionManagement

功能与@EnableAspectJAutoProxy类似,利用TransactionManagementConfigurationSelector给容器中会导入两个组件:AutoProxyRegistrarProxyTransactionManagementConfiguration

  1. AutoProxyRegistrar
  • 给容器中注册一个InfrastructureAdvisorAutoProxyCreator组件(也是一个后置处理器)
  • 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用。
  1. ProxyTransactionManagementConfiguration
  • 给容器中注册事务增强器

    • 事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
    • 事务拦截器:TransactionInterceptor;保存了事务属性信息,事务管理器;
  • 在目标方法执行的时候,执行拦截器链

    • 如果异常,获取到事务管理器,利用事务管理回滚操作;
    • 如果正常,利用事务管理器,提交事务。

拓展原理

BeanFactoryPostProcessor

BeanFactory的后置处理器。在BeanFactory标准初始化之后调用,用来定制和修改BeanFactory的内容。

执行时机:所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建。

@Componentpublic class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory..."); // 获取容器中组件的数量 int count = beanFactory.getBeanDefinitionCount(); // 获取容器中全部组件的名称 String[] names = beanFactory.getBeanDefinitionNames(); System.out.println("当前BeanFactory中有"+ count +" 个Bean"); System.out.println(Arrays.asList(names)); }}

原理:ioc容器创建对象refresh() ——> invokeBeanFactoryPostProcessors(beanFactory);

直接在BeanFactory中找到所有类型是BeanFactoryPostProcessor的组件,并执行他们的方法(在初始化创建其他组件前面执行)。

BeanDefinitionRegistryPostProcessor

继承于BeanFactoryPostProcessor,实现的方法为postProcessBeanDefinitionRegistry();

执行时机:在所有bean定义信息将要被加载,bean实例还未创建的;优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件;

@Componentpublic class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor{
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanDefinitionRegistryPostProcessor...bean的数量:"+beanFactory.getBeanDefinitionCount()); } //BeanDefinitionRegistry为Bean定义信息的保存中心,以后BeanFactory就是按照BeanDefinitionRegistry里面保存的每一个bean定义信息创建bean实例; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("postProcessBeanDefinitionRegistry...bean的数量:"+registry.getBeanDefinitionCount()); //RootBeanDefinition beanDefinition = new RootBeanDefinition(Blue.class); // 手动添加一个BeanDefinition AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Blue.class).getBeanDefinition(); registry.registerBeanDefinition("hello", beanDefinition); }}

**原理:**ioc创建对象refresh() ——> invokeBeanFactoryPostProcessors(beanFactory);

  1. 从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件;
  2. 依次触发所有的postProcessBeanDefinitionRegistry()方法;
  3. 再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor;
  4. 再来从容器中找到BeanFactoryPostProcessor组件;然后依次触发postProcessBeanFactory()方法。

ApplicationListener监听器

监听容器中发布的事件,事件驱动模型开发。

@Componentpublic class MyApplicationListener implements ApplicationListener
{
//当容器中发布此事件以后,方法触发 @Override public void onApplicationEvent(ApplicationEvent event) {
System.out.println("收到事件:"+event); }}

只要容器中有相关事件的发布,我们就能监听到这个事件:

  • ContextRefreshedEvent:容器刷新完成(所有bean都完全创建)会发布这个事件;
  • ContextClosedEvent:关闭容器会发布这个事件;
  • 也可以自己发布事件:applicationContext.publishEvent();

监听器的创建过程:refresh() ——>registerListeners();

从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中(注意此时并不初始化):

String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

事件发布流程

publishEvent(new ContextRefreshedEvent(this));

  1. 获取事件的多播器(派发器):getApplicationEventMulticaster()
  2. multicastEvent派发事件;
  3. 获取到所有的ApplicationListener
for (final ApplicationListener listener : getApplicationListeners(event, type)) {
// 1.如果有Executor,可以支持使用Executor进行异步派发; Executor executor = getTaskExecutor(); // 2.否则,同步的方式直接执行listener方法; invokeListener(listener, event); // 3.拿到listener回调onApplicationEvent方法;}

事件多播器(派发器)

刷新容器refresh() ——>initApplicationEventMulticaster();初始化ApplicationEventMulticaster

  1. 先去容器中找有没有id="applicationEventMulticaster"的组件;
  2. 如果没有,可以自动创建一个多播器this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster。
你可能感兴趣的文章
makefile学习网站
查看>>
Lua教程:Lua调用C/C++函数(4)
查看>>
win下创建win32控制台工程,执行lua脚本
查看>>
cocos2dx android启动错误
查看>>
eclipse: android rename package name
查看>>
cocos2dx c++调用java思想
查看>>
lua math.ceil math.ceil
查看>>
cocos2dx CCNode计算node的大小
查看>>
cocos2dx 布局记录(1)
查看>>
lua 多行注释和取消多行注释
查看>>
缩放系数计算
查看>>
cocos2dx --- 按钮点击居中放大
查看>>
cocos2dx menu位置计算
查看>>
cocos2dx资源加载机制(同步/异步)
查看>>
cocos2dx C++调用java -- 字符串传递
查看>>
git学习网站
查看>>
JavaScript 学习网站
查看>>
cocos2dx java调用c++ -- 字符串传递
查看>>
CCScaleTo与CCScaleBy比较
查看>>
cocos2dx CCObject引用计数,内存释放分析(1)
查看>>