Spring:注解源码解析

Spring IOC注解解析

一、组件注册的几种方式

1、@Configuration注解

下面是@Configuration注解的源码翻译部分:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
	/**
	 * 1、显式指定与此配置类关联的Spring bean定义的名称。如果未指定(常见情况),将自动生成bean名称。
	 * 2、只有通过组件扫描获取配置类或直接提供给{@link AnnotationConfigApplicationContext}时,才应用自定义名称。
	 * 3、如果配置类注册为传统的XML bean定义,那么bean元素的名称/id将优先。
	 * @return the specified bean name, if any
	 * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
	 */
	String value() default "";
}

具体看@Configuration的作用是什么?

//配置类:代替bean.xml配置文件
@Configuration
public class MainConfig {
    
    //给spring容器注册组件,类型就是返回值的类型Person,id为方法名person
    @Bean
    public Person person(){
        System.out.println("person方法执行了");
        return new Person();
    }

    //给spring容器注册组件,类型就是返回值的类型Dog,id为方法名dog
    @Bean
    public Dog dog(){
        person();
        System.out.println("dog方法执行了");
        return new Dog();
    }
}

public class MainTest {
    public static void main(String[] args) {
        //初始化容器
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MainConfig.class);
		//获取容器中含有的组件名称
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for(String name:beanDefinitionNames){
            System.out.println(name);
        }
    }
}

MainTest类输出的结果:

//当初始化容器时,就会执行MainConfig类中的方法,并且发现person()方法只执行了一次,按照常理应该执行2次
person方法执行了
dog方法执行了
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
//容器中只有下面的三个组件
mainConfig
person
dog

问题:person()方法明明调用了两次,但是却只执行了一次,保证了@Bean的对象时单例,它是如何实现的?

原因:@Configuration这个注解的作用就是为MainConfig类加上Cglib动态代理,保证容器中的对象都是单例的

2、@ComponentScan注解

1、自动扫描组件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IACa4gwQ-1588066643357)(C:\Users\hh\Desktop\秋招面试准备\assets\image-20200420133925393.png)]

//将这个组件加入到IOC容器中
@Repository
public class BookDao {
}

public class BookService {
}

//将这个组件加入到IOC容器中
@Controller
public class BookController {
}

//配置类代替bean.xml配置文件
@Configuration
//将这个包下面的加了@Repository、@Service、@Controller、@Component注解的组件都加入容器中
@ComponentScan(value = "com.hh")
public class MainConfig {

    //将对象person放入spring容器中
    @Bean
    public Person person(){
        return new Person();
    }

    //将对象dog放入spring容器中
    @Bean
    public Dog dog(){
        return new Dog();
    }
}


public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MainConfig.class);

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for(String name:beanDefinitionNames){
            System.out.println(name);
        }
    }
}

运行结果:

可以看出:这些组件加到了IOC容器中:而Service层的类BoorService由于没有使用@Service注解,没有被加入容器中
mainConfig
bookController
bookDao
person
dog
2、指定扫描规则

下面是@ComponentScan注解的部分源码,翻译了一下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
	/**
	 *1、指示是否自动检测用{@code@Component}注释的类
	 */
	boolean useDefaultFilters() default true;

	/**
    1、指定哪些类型适合进行组件扫描。
    2、进一步将候选组件集从{@link#base packages}中的所有内容,缩小到与给定过滤器匹配的基本包中的所有内容。
    3、 注意,如果指定,这些过滤器将应用于默认过滤器之外。
    4、 指定的基包下与给定过滤器匹配的任何类型都将包括在内,即使它与默认过滤器不匹配(即没有用{@code@Component}注释)。
	 */
	Filter[] includeFilters() default {};

	/**
	 * 指定哪些类型不适合进行组件扫描。
	 */
	Filter[] excludeFilters() default {};

	/**
	 * 指定是否应为延迟初始化注册扫描的bean。
	 * <p>Default is {@code false}; switch this to {@code true} when desired.
	 */
	boolean lazyInit() default false;
    
    
	/**
	  1、 声明要用作{@linkplain ComponentScan#includeFilters include filter} or 		{@linkplain ComponentScan#excludeFilters exclude filter}的类型过滤器。
	 */
	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		/**
		 * 1、要使用的过滤器类型。
		 * <p>Default is {@link FilterType#ANNOTATION}.
		 *	默认FilterType.ANNOTATION
		 */
		FilterType type() default FilterType.ANNOTATION;

		@AliasFor("classes")
		Class<?>[] value() default {};
		
		@AliasFor("value")
		Class<?>[] classes() default {};

		String[] pattern() default {};
	}

具体使用规则:

//配置类代替bean.xml配置文件
@Configuration
/**
 * 1、将这个包下面的添加了@Repository、@Service、@Controller、@Component注解的组件添加到容器中
 * 2、自定义扫描规则---使用过滤器排除一些组件:按照type = FilterType.ANNOTATION, classes = {Controller.class}来排除,
 * 3、自定义扫描规则---只扫描哪些组件:这个需要禁用掉默认的扫描规则才能生效
 */
@ComponentScan(value = "com.hh",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class})},useDefaultFilters = false)
public class MainConfig {

    //将对象person放入spring容器中
    @Bean
    public Person person(){
        return new Person();

    }

    //将对象dog放入spring容器中
    @Bean
    public Dog dog(){
        return new Dog();
    }
}

结果:

mainConfig
//只包含了@Service注解的组件
bookService
person
dog
3、自定义FilterType指定过滤规则

下面是 FilterType的源码:

public enum FilterType {

	/**
	 * 筛选用给定注释标记的候选项。
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * 筛选可分配给给定类型的候选项。
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * 筛选与给定AspectJ类型模式表达式匹配的候选项。
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * 筛选与给定正则表达式模式匹配的候选.
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** 使用给定的自定义筛选候选
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM
}

具体使用规则:

//配置类代替bean.xml配置文件
@Configuration

//按照给定的类型来过滤 type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}
@ComponentScan(value = "com.hh",
               includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})},useDefaultFilters = false)
public class MainConfig {

    //将对象person放入spring容器中
    @Bean
    public Person person(){
        return new Person();
    }

    //将对象dog放入spring容器中
    @Bean
    public Dog dog(){
        return new Dog();
    }
}

自定义FilterType来过滤:

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

3、@Scope注解

下面是翻译后的@Scope注解的源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
	/**
	 * Alias for {@link #scopeName}.
	 * @see #scopeName
	 */
	@AliasFor("scopeName")
	String value() default "";

	/**
	 1、指定用了Scope注解的组件/bean的作用域的名称。
	 2、 默认为空字符串,代表{@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
	 * @see #value
	 */
	@AliasFor("value")
	String scopeName() default "";

	/**
	 * 1、指定组件是否应配置为作用域代理,如果应配置,则代理应基于接口还是基于子类。
	 * 2、默认为{@link ScopedProxyMode#DEFAULT},这通常表示除非在组件扫描指令级别配置了不同的默认值,否则不应创建作用域代理。
	 * 3、类似于Spring XML中的{@code<aop:scoped proxy/>}支持。
	 * @see ScopedProxyMode
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

具体使用规则:

@Configuration
@ComponentScan("com.hh")
public class MainConfig2 {

    /**
     * prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
     * 					每次获取的时候才会调用方法创建对象;
     * singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
     * 			以后每次获取就是直接从容器(map.get())中拿,
     * request:同一次请求创建一个实例
     * session:同一个session创建一个实例
     */
    @Scope("prototype")
    @Bean("person")
    public Person person(){
        System.out.println("person()方法执行了。。。。");
        return new Person();
    }
}

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MainConfig2.class);

        System.out.println(applicationContext.getBean("person"));
        System.out.println(applicationContext.getBean("person"));
    }
}

结果:ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象;

person()方法执行了。。。。
Person(name=null, age=null)
person()方法执行了。。。。
Person(name=null, age=null)

4、@Lazy注解

@Configuration
@ComponentScan("com.hh")
public class MainConfig2 {
    /**
     * 单实例bean:默认在容器启动的时候创建对象;
     * 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
     */
    @Lazy
    @Bean("person")
    public Person person(){
        System.out.println("person()方法执行了。。。。");
        return new Person();
    }
}

public class MainTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MainConfig2.class);
        System.out.println("IOC容器创建完成.....");
    }
}

结果:

IOC容器创建完成.....

5、@Import注解

@Import注解注解的源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
	/**
	 * {@link Configuration}, 
	 * {@link ImportSelector}, 
	 * {@link ImportBeanDefinitionRegistrar}
	 * or regular component classes to import.
	 */
	Class<?>[] value();//这是一个数组形式
}
1、使用类名导入组件
@Data
public class Color {
	private String color;
}

@Data
public class Red{
}

@Configuration
@Import({Color.class, Red.class})
public class MainConfig2 {
    @Bean
    public Person person(){
        return new Person();
    }
}

容器中的组件为:

mainConfig2
com.hh.bean.Color
com.hh.bean.Red
person
2、ImportSelector
@Configuration
@Import({Color.class, Red.class,MyImportSelector.class})
public class MainConfig2 {
    @Bean
    public Person person(){
        return new Person();
    }
}

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //需要导入类的全类名
        return new String[]{"com.hh.bean.Yellow","com.hh.bean.Blue"};
    }
}

结果:返回需要导入的组件的全类名数组;

mainConfig2
com.hh.bean.Color
com.hh.bean.Red
com.hh.bean.Yellow
com.hh.bean.Blue
person
3.ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar:手动注册bean到容器中

public class RainBow {
}

@Configuration
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
    @Bean
    public Person person(){
        return new Person();
    }
}

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //判断容器中是否包含这个组件
        boolean definition1 = registry.containsBeanDefinition("com.hh.bean.Red");
        boolean definition2 = registry.containsBeanDefinition("com.hh.bean.Blue");
        if(definition1 && definition2){
            //指定Bean名
            RootBeanDefinition definition = new RootBeanDefinition(RainBow.class);
            //给容器中注册Bean
            registry.registerBeanDefinition("rainbow",definition);
        }
    }
}

6、FactoryBean

给容器中加入组件的方法:

  • @ComponentScan注解+@Controller、@Service、@Repository、@Component(局限,是自己写的类)
  • @Bean(导入第三方组件)
  • @Import 快速的给容器中导入一个组件
  • FactoryBean

二、Spring bean的生命周期

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

@Component
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...");
	}
}

/**
 *  1、bean的生命周期由容器管理:创建---初始化---销毁
 *  2、初始化和销毁方法可以自定义
 *  3、
 *     初始化时机:对象创建完成,调用初始化方法
 *     对于单实例Bean的销毁时机:容器的关闭的时候
 *     对于多实例Bean:不会管理销毁方法
 */
@ComponentScan("com.hh.bean")
@Configuration
public class MainConfigOfLifeCycle {
    
    @Bean(initMethod ="init",destroyMethod = "destory")
    public Car car(){
        return new Car();
    }
}

//测试类
public class IOCTest_LifeCycle {
    @Test
    public void test01(){
        //创建ioc容器
        AnnotationConfigApplicationContext applicationContext
                = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器创建完成...");

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

2、InitializingBean 和 DisposableBean接口

通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)

public interface InitializingBean {
   void afterPropertiesSet() throws Exception;
}

public interface DisposableBean {
	void destroy() throws Exception;
}
@Component
public class Cat implements InitializingBean,DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化。。。。。");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("销毁。。。。。");
    }
}

3、使用 @PostConstruct与@PreDestroy

@PostConstruct注解:对象创建后调用标注了这个注解的方法

@PreDestroy注解:容器移除之前调用标注了这个注解的方法

//对象创建后调用标注了这个注解的方法
public @interface PostConstruct {
}
//容器移除之前调用标注了这个注解的方法
public @interface PreDestroy {
}
@Component
public class Dog  {
   //对象创建并赋值之后调用
   @PostConstruct
   public void init(){
      System.out.println("Dog....@PostConstruct...");
   }
   
   //容器移除对象之前
   @PreDestroy
   public void detory(){
      System.out.println("Dog....@PreDestroy...");
   }
}

4、BeanPostProcessor

BeanPostProcessor:bean的后置处理器,在初始化前后进行一些处理工作

public interface BeanPostProcessor {
   /**
    * 在初始化之前进行一些处理工作
    */
   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

   /**
    * 在初始化之后进行一些处理工作
    */
   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

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

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

容器中的组件在初始化之前和初始化之后都执行一些处理:

postProcessBeforeInitialization...cat=>com.hh.bean.Cat@105fece7
cat...init..
postProcessAfterInitialization...cat=>com.hh.bean.Cat@105fece7
car constructor...
postProcessBeforeInitialization...car=>com.hh.bean.Car@6b419da
car ... init...
postProcessAfterInitialization...car=>com.hh.bean.Car@6b419da

三、自动装配

1、@Autowired、@Qualifier、@Primary

@Data
@Repository //默认使用类名首字母小写作为组件id,bookDao
public class BookDao {
    private String lable = "1";
}

@Configuration
@ComponentScan({"com.hh.service","com.hh.dao","com.hh.controller","com.hh.bean"})
public class MainConifgOfAutowired {
    //向容器中再加入一个组件bookDao2
	@Bean("bookDao2")
	public BookDao bookDao(){
		BookDao bookDao = new BookDao();
		bookDao.setLable("2");
		return bookDao;
	}
}


@Service
@Data
public class BookService {
    /**
     * 1、默认按照类型(ByType)去容器中找对应的组件
     * 2、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 (bookDao)
     * 3、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id
     * 4、@Primary:让Spring进行自动装配的时候,默认使用首选的bean
     * 5、自动装配默认一定要将属性赋值好,没有就会报错;可以使用 @Autowired(required=false);
     */
    @Qualifier("bookDao2")
    @Autowired
    private BookDao bookDao;

    public void print(){
        System.out.println(bookDao);
    }
}

2、@Resource(JSR250)、@Inject(JSR330)

java规范的注解

@Service
public class BookService {
    /**
     * 1、默认按照属性名称到容器中找对应的组件进行注入(byName:bookDao)
     * 2、可以指定组件的名称 @Resource(name="bookDao2")
     * 3、没有required=false的功能,@Primary功能
     */
    @Resource(name="bookDao2")
    private BookDao bookDao;
}
@Service
public class BookService {
    /**
     * 1、@Inject:需要导入javax.inject的包,和Autowired的功能一样
     * 2、没有required=false的功能
     */
    private BookDao bookDao;
}

3、@Autowired标在方法、构造器、参数上

1、默认情况下,加在容器中的组件,在容器启动时就会调用无参构造函数来创建对象,然后初始化,把@Autowired标注在方法上,该方法如果有参数(组件),会使用autowired的方式(byType)在容器中查找是否有该组件

@Component
public class Boss {
   private Car car;
   //标注在方法上,该方法如果有参数(组件),会使用autowired的方式(byType)在容器中查找是否有该组件
   @Autowired
   public void setCar(Car car) {
      this.car = car;
   }
}

2、标注在带参构造函数上,如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件可以自动从容器中获取。

@Component
public class Boss {
   private Car car;
   
   //构造器要用的组件,从容器中获取
   public Boss(Car car){
      this.car = car;
      System.out.println("Boss...有参构造器");
   }
}

3、标注在参数上

@Component
public class Boss {
   private Car car;
   public void setCar(@Autowired Car car) {
      this.car = car;
   }
}

4、标注在方法上:@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配

@Configuration
@ComponentScan({"com.hh.service","com.hh.dao","com.hh.controller","com.hh.bean"})
public class MainConifgOfAutowired {
   //@Bean标注的方法创建对象的时候,方法参数的值从容器中获取
   @Bean
   public Color color(Car car){
      Color color = new Color();
      return color;
   }
}

4、自定义组件想使用Spring容器底层的组件

自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware;

把Spring底层一些组件注入到自定义的Bean中;

@Data
@Component
public class Red implements ApplicationContextAware, BeanNameAware {
    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("当前IOC容器:"+applicationContext);
        this.applicationContext = applicationContext;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("当前Bean的名称:"+name);
    }
}

Spring AOP 注解源码解析

1、AOP的使用

img

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

 步骤:
 1、导入aop模块;Spring AOP:(spring-aspects)
 2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方		法出现异常,方法正常返回)
 3、定义一个日志切面类(LogAspects):切面类里的方法要动态感知MathCalculator.div运行到哪里然后执行;
 		通知方法:
 			前置通知(@Before):logStart:在目标方法(div)运行之前运行
 			后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
 			返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
			异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
			环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
 4、给切面类的目标方法标注何时何地运行(通知注解);
 5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
 6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
 7、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
 		在Spring中很多的 @EnableXXX;

具体使用步骤:

1、业务逻辑类:

/**
 * 1、业务逻辑类
 */
public class MathCalculator { 
   public int div(int i,int j){
      System.out.println("MathCalculator...div...");
      return i/j;    
   }
}

2、切面类:

/**
 * 切面类
 * @Aspect: 告诉Spring当前类是一个切面类
 */
@Aspect
public class LogAspects {
	/**
	 * 抽取公共的切入点表达式,切入点表达式(指定在哪个方法被切入)
	 * 	1、本类引用:@Before("pointCut()"):随便写一个方法的方法名
	 * 	2、其他的切面引用:@After("com.hh.aop.LogAspects.pointCut()"):方法的全类名
	 */
	@Pointcut("execution(public int com.hh.aop.MathCalculator.*(..))")
	public void pointCut(){};

	/**
	 * 在目标方法执行之前切入
	 * @param joinPoint 接收切入方法
	 */
	@Before("pointCut()")
	public void logStart(JoinPoint joinPoint){
		Object[] args = joinPoint.getArgs();
		System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
	}

	/**
	 * 在目标方法执行结束后切入
	 * @param joinPoint 接收切入方法
	 */
	@After("pointCut()")
	public void logEnd(JoinPoint joinPoint){
		System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
	}


	/**
	 * 在目标方法正常返回时切入
	 * @param joinPoint 接收切入方法
	 * @param result 接收返回结果
	 */
	@AfterReturning(value="pointCut()",returning="result")
	public void logReturn(JoinPoint joinPoint,Object result){
		System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
	}

	/**
	 * 在目标方法出现异常时切入
	 * @param joinPoint 接收切入方法
	 * @param exception 接收异常信息
	 */
	@AfterThrowing(value="pointCut()",throwing="exception")
	public void logException(JoinPoint joinPoint,Exception exception){
		System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
	}
}

3、将切面类和业务逻辑类(目标方法所在类)都加入到容器中 :

/**
 * 3、将切面类和业务逻辑类(目标方法所在类)都加入到容器中 
 * @EnableAspectJAutoProxy 【开启基于注解的aop模式】
 */
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
	 
	//业务逻辑类加入容器中
	@Bean
	public MathCalculator calculator(){
		return new MathCalculator();
	}

	//切面类加入到容器中
	@Bean
	public LogAspects logAspects(){
		return new LogAspects();
	}
}

4、测试类:

public class IOCTest_AOP {
   @Test
   public void test01(){
      AnnotationConfigApplicationContext applicationContext 
          = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
      MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
      mathCalculator.div(1, 0);
   }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0vL0VLjr-1588066643372)(C:\Users\hh\Desktop\秋招面试准备\assets\image-20200428160848352.png)]

主要有三步:

1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
3)、开启基于注解的aop模式;@EnableAspectJAutoProxy

2、AOP的底层原理

*   总结:
*     1)、  @EnableAspectJAutoProxy 开启AOP功能
*     2)、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator
*     3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
*     4)、容器的创建流程:
*        1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象
*        2)、finishBeanFactoryInitialization()初始化剩下的单实例bean
*           1)、创建业务逻辑组件和切面组件
*           2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
*           3)、组件创建完之后,判断组件是否需要增强
*              是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib);
*     5)、执行目标方法:
*        1)、代理对象执行目标方法
*        2)、CglibAopProxy.intercept();
*           1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
*           2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行;
*           3)、效果:
*              正常执行:前置通知-》目标方法-》后置通知-》返回通知
*              出现异常:前置通知-》目标方法-》后置通知-》异常通知

Spring 声明式事务

1、声明式事务的使用

/**
 * 环境搭建:
 * 1、导入相关依赖:数据源、数据库驱动、Spring-jdbc模块		
 * 2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
 * 3、给方法上标注 @Transactional 表示当前方法是一个事务方法;
 * 4、@EnableTransactionManagement 开启基于注解的事务管理功能;	@EnableXXX
 * 5、配置事务管理器来控制事务; public PlatformTransactionManager transactionManager(){}	
*/

1、配置类:向容器中配置数据源、JDBCTemplate、事务管理器

@EnableTransactionManagement
@ComponentScan("com.hh.tx")
@Configuration
public class TxConfig {
	//数据源
	@Bean
	public DataSource dataSource() throws Exception{
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setUser("root");
		dataSource.setPassword("root");
		dataSource.setDriverClass("com.mysql.jdbc.Driver");
		dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
		return dataSource;
	}

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

2、UserDao接口:

@Repository
public class UserDao {
   @Autowired
   private JdbcTemplate jdbcTemplate;
   public void insert(){
      String sql = "INSERT INTO `tbl_user`(username,age) VALUES(?,?)";
      String username = UUID.randomUUID().toString().substring(0, 5);
      jdbcTemplate.update(sql, username,19);
      
   }
}

3、UserService类:

@Service
public class UserService {
   @Autowired
   private UserDao userDao;
   
   @Transactional
   public void insertUser(){
      userDao.insert();
      System.out.println("插入完成...");
      int i = 10/0;
   }
}

2、声明式事务底层原理

/**
* 原理:
* 1)、@EnableTransactionManagement
*        利用TransactionManagementConfigurationSelector给容器中会导入组件
*        导入两个组件
*        AutoProxyRegistrar
*        ProxyTransactionManagementConfiguration
* 2)、AutoProxyRegistrar:
*        给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;
*        InfrastructureAdvisorAutoProxyCreator:?
*        利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
* 
* 3)、ProxyTransactionManagementConfiguration 做了什么?
*        1、给容器中注册事务增强器;
*           1)、事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
*           2)、事务拦截器:
*              TransactionInterceptor;保存了事务属性信息,事务管理器;
*              他是一个 MethodInterceptor;
*              在目标方法执行的时候;
*                 执行拦截器链;
*                 事务拦截器:
*                    1)、先获取事务相关的属性
*                    2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
*                       最终会从容器中按照类型获取一个PlatformTransactionManager;
*                    3)、执行目标方法
*                       如果异常,获取到事务管理器,利用事务管理回滚操作;
*                       如果正常,利用事务管理器,提交事务
*/
原创文章 723 获赞 153 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_42764468/article/details/105813815