Spring注解开发(Springboot源码必备前置知识)



一、组件注册(IOC)

Spring官方文档

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

概述

  • @Configuration 用于标注配置类
  • @Bean 结合@Configuration使用。可以导入第三方组件
  • 包扫描@ComponentScan (@ComponentScans可以配置多个扫描,@TypeFilter:指定过滤规则,自己实现TypeFilter类)
    组件(@Service、@Controller、@Repository):包扫描+组件注解导入注解。
  • @Scope:设置组件作用域 1.prototype:多例的2.singleton:单例的(默认值)
  • @Lazy 懒加载
  • @Conditional:按照一定的条件进行判断,满足条件给容器中注册Bean,传入Condition数组,,使用时需自己创建类继 承Condition然后重写match方法。
  • @Import[快速给容器中导入一个组件]
    1、Import(类名),容器中就会自动注册这个组件,id默认是组件的全名
    2、ImportSelector:返回需要导入的组件的全类名的数组
    3、ImportBeanDefinitionRegistrar:手动注册bean
  • FactoryBean:工厂Bean,交给spring用来生产Bean到spring容器中.可以通过前缀&来获取工厂Bean本身.

1、@Configuration @Bean

给容器注册组件

1、配置文件注册bean(旧)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
    <bean id="person" class="com.laptoy.bean.Person">
        <property name="age" value="21"></property>
        <property name="name" value="jiali"></property>
    </bean>
</beans>
public class MainTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}

2、注解方式注册bean

@Configuration
public class MainConfig {
	//给容器注册bean,id默认用方法名id
	//也可以通过@Bean注解name属性指定id
    @Bean(name = "person")
    public Person person() {
        return new Person("jiali", 21);
    }
}
public class MainTest {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
        
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}

2、@ComponentScan

自动扫描组件&自定义扫描规则

1、xml文件配置(旧)

<!-- 包扫描:只要标注了@Controller、@Service、@Reposity、@Component -->
<context:component-scan base-package="com.laptoy" />

2、注解配置

1.配置类配置

@Configuration
@ComponentScan(value = "com.laptoy")
//value 指定要扫描的包
public class MainConfig {
    @Bean
    public Person person() {
        return new Person("jiali", 21);
    }
}

2.添加controller和service

@Controller
public class BookController {
}
@Service
public class BookService {
}
@Test
public void test01(){
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
    for (String name : context.getBeanDefinitionNames()) {
        System.out.println(name);
    } 
}

在这里插入图片描述

3、测试属性excludeFilters(指定扫描的时候排除那些)

@Configuration
@ComponentScan(value = "com.laptoy", excludeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MainConfig {
    @Bean
    public Person person() {
        return new Person("jiali", 21);
    }
}

在这里插入图片描述

4、测试属性 includeFilters(指定扫描的时候只扫描哪些),需要关闭默认规则useDefaultFilters = false

@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)
public class MainConfig {
    @Bean
    public Person person() {
        return new Person("jiali", 21);
    }
}

在这里插入图片描述
5、FilterType过滤规则

@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
        @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
}, useDefaultFilters = false)
//FilterType.ANNOTATION 		按照注解
//FilterType.ASSIGNABLE_TYPE	按照指定的类型
//FilterType.ASOECTJ			使用ASPECTJ表达式
//FilterType.REGEX				使用正则
//FilterType.CUSTOM				使用自定义规则
public class MainConfig {
    @Bean
    public Person person() {
        return new Person("jiali", 21);
    }
}

在这里插入图片描述
6、自定义TypeFilter过滤规则

MyTypeFilter

public class MyTypeFilter implements TypeFilter {
    /**
     * @param metadataReader:读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory:可以获取到其他任何信息的
     * return true表示组件加入容器
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当正在扫描的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        System.out.println("--->" + className);
		
		//如果类名包含er则加入容器
        if (className.contains("er")) {
            return true;
        }

        return false;
    }
}

MainConfig

@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
    @Bean
    public Person person() {
        return new Person("jiali", 21);
    }
}

在这里插入图片描述


3、@Scope

设置组件作用域
1、prototype:多实例的,IOC容器启动不会创建对象,每次获取才会调用方法创建对象
2、singleton:单实例的(默认),IOC容器启动会调用方法创建对象并放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿,只会创建一次

不常用
3、request:同一个请求创建一个实例
4、session:同一个session创建一个实例

验证singleton模式IOC容器启动会调用方法创建对象并放到ioc容器中

@Scope("singleton")
@Bean
public Person person() {
    System.out.println("给容器中注册Bean。。。");
    return new Person("jiali", 22);
}
@Test
public void test02() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("==容器加载完成==");
    //默认单实例
    Object bean1 = context.getBean("person");
    Object bean2 = context.getBean("person");
    System.out.println(bean1 == bean2);
}

在这里插入图片描述
验证prototype(多实例的,IOC容器启动不会创建对象,每次获取才会调用方法创建对象)

@Scope("prototype")
@Bean
public Person person() {
    System.out.println("给容器中注册Bean。。。");
    return new Person("jiali", 22);
}
@Test
public void test02() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("==容器加载完成==");
    //默认单实例
    Object bean1 = context.getBean("person");
    Object bean2 = context.getBean("person");
    System.out.println(bean1 == bean2);
}

在这里插入图片描述


4、@Lazy

单实例bean,默认容器创建就创建对象,只会创建一次
懒加载,容器启动不创建对象,第一次使用(获取)Bean创建对象并初始化

@Lazy
@Bean
public Person person() {
    System.out.println("给IOC添加Person。。。。");
    return new Person("jiali", 22);
}
@Test
public void test02() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("==容器加载完成==");
    //默认单实例
    Object bean1 = context.getBean("person");
    Object bean2 = context.getBean("person");
    System.out.println(bean1 == bean2);
}

在这里插入图片描述


5、@Conditional

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

@Configuration
public class MainConfig3 {

    @Conditional({WindowCondition.class})
    @Bean("bill")
    public Person person1() {
        return new Person("Bill Gates", 62);
    }

    @Conditional({LinuxCondition.class})
    @Bean("linus")
    public Person person2() {
        return new Person("linus", 48);
    }

}
//判断是否为linux系统
public class LinuxCondition implements Condition {

    /**
     * @param context:  判断条件能使用的上下文(环境)
     * @param metadata:注释信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1、获取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //2、获取类加载器
        ClassLoader beanClassLoader = 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;
    }
}
//判断是否为win系统
public class WindowCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String property = environment.getProperty("os.name");
        if (property.contains("Windows")) {
            return true;
        }

        return false;
    }
}

@Test
public void test03() {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
    for (String name : context.getBeanNamesForType(Person.class)) {
        System.out.println(name);
    }
    //获取环境变量
    ConfigurableEnvironment environment = context.getEnvironment();
    String property = environment.getProperty("os.name");
    System.out.println(property);
    
    Map<String, Person> persons = context.getBeansOfType(Person.class);
    System.out.println(persons);
}

在这里插入图片描述


6、@Import

6.1、@Import

快速给容器导入组件,id默认是组件的全类名

@Configuration
@Import({Color.class})
public class MainConfig4 {
}
private void printBeans(AnnotationConfigApplicationContext applicationContext) {
    for (String name : applicationContext.getBeanDefinitionNames()) {
        System.out.println(name);
    }
}

@Test
public void test04() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    printBeans(applicationContext);
}

在这里插入图片描述


6.2、ImportSelector

与@Import一起使用,该选择器能够读取标注@Import注解的类的其他注解信息

返回需要导入的组件的全类名数组
下面例子从MyImportSelector将我们的Pink,Red全类名数组返回给@Import实现组件导入

@Configuration
@Import({Color.class, MyImportSelector.class})
public class MainConfig4 {
}
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata:当前标注@Import注解的类的所有注解信息
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {

        return new String[]{"com.laptoy.bean.Pink","com.laptoy.bean.Red"};
    }
}
@Test
public void test04() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    printBeans(applicationContext);
}

在这里插入图片描述


6.3、ImportBeanDefinitionRegistrar

可以把需要的组件手工注册进容器

@Configuration
@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig4 {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * @param importingClassMetadata:当前类的注解信息
     * @param registry:BeanDefinitionRegistry注册类: registerBeanDefinition()方法可以把需要的组件手工注册进容器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //注册Bean并指定bean名
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Yellow.class);
        registry.registerBeanDefinition("yellow", rootBeanDefinition);
    }
}
@Test
public void test04() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
    printBeans(applicationContext);
}

在这里插入图片描述


7、FactoryBean工厂注册

验证FactoryBean默认创建的对象是调用getObject创建的对象(单多实例有上面的基础可以自行验证)

public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一个Color对象,对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean。。。getObject。。。");
        return new Color();
    }
	
	//指定为Color类
    @Override
    public Class<?> getObjectType() {
        return Color.class;
    }

    //true:单实例,在容器中保存一分
    //false:多实例,每次获取都会创建一个新的bean
    @Override
    public boolean isSingleton() {
        return true;
    }
}
@Configuration
public class MainConfig5 {
    @Bean
    public ColorFactoryBean colorFactoryBean() {
        return new ColorFactoryBean();
    }
}
@Test
public void test05() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);

    System.out.println("==验证colorFactoryBean创建的对象是指定的对象==");
    Object color = applicationContext.getBean("colorFactoryBean");
    System.out.println(color.getClass());

    System.out.println("==验证获取到ColorFactoryBean本身==");
    Object colorFactoryBean = applicationContext.getBean("&colorFactoryBean");
    System.out.println(colorFactoryBean.getClass());
}

要获取到工厂Bean本身,在id前加&
在这里插入图片描述

在这里插入图片描述


二、Bean生命周期

概述

创建—初始化----销毁
可以自定义bean的初始化和销毁方法

1、构造方法(对象创建)

  1. 单实例:在容器启动的时候创建
  2. 多实例:在每次获取的时候创建

2、初始化(init):对象创建构造完成,并赋值好,开始调用初始化方法

3、销毁(destroy)

  1. 单实例:容器关闭自动销毁
  2. 多实例:需要手动销毁

实现方式:

  1. 通过@Bean 指定init-method和destroy-method
  2. 实现InitializingBean定义初始化逻辑,实现DisposableBean定义销毁方法
  3. JSP250:
    @PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法
    @PreDestory:在容器销毁bean之前通知我们进行清理
  4. 实现BeanPostProcessor接口的后置拦截器放入容器中,可以拦截bean初始化,并可以在被拦截的Bean的初始化前后进行一些处理工作。

1、@Bean指定init-method和destroy-method

xml配置(旧)

<bean id="person" class="com.laptoy.bean.Person" init-method="" destroy-method="">
    <property name="age" value="21"></property>
    <property name="name" value="jiali"></property>
</bean>

注解配置
1、验证单实例

@Configuration
public class MainConfigOfLifeCycle {
    @Bean(initMethod = "init",destroyMethod = "destroy")
    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 destroy(){
        System.out.println("car...destroy..");
    }
}
@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");
    System.out.println("关闭容器");
    applicationContext.close();
}

在这里插入图片描述
2、验证多实例

@Configuration
public class MainConfigOfLifeCycle {
    @Scope("prototype")
    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }
}
@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");

    Object car = applicationContext.getBean("car");

    System.out.println("关闭容器");
    applicationContext.close();
}

在这里插入图片描述


2、InitializingBean和DisposableBean

InitializingBean:定义初始化逻辑()
在这里插入图片描述
DisposableBean:定义销毁逻辑
在这里插入图片描述

@Component
public class Cat implements InitializingBean, DisposableBean {
    public Cat(){
        System.out.println("cat...constructor");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("cat...afterPropertiesSet(init)");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("cat...destroy");
    }
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");
    System.out.println("关闭容器");
    applicationContext.close();
}

在这里插入图片描述


3、JSR250

1、@PostConstruct

在bean创建完成并且属性赋值完成,来执行初始化方法

/**
 * The PostConstruct annotation is used on a method that needs to be executed
 * after dependency injection is done to perform any initialization. This
 * method MUST be invoked before the class is put into service. This
 * annotation MUST be supported on all classes that support dependency
 * injection. The method annotated with PostConstruct MUST be invoked even
 * if the class does not request any resources to be injected. Only one
 * method can be annotated with this annotation. The method on which the
 * PostConstruct annotation is applied MUST fulfill all of the following
 */
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}

在这里插入图片描述
2、@PreDestory

在容器销毁bean之前通知我们进行清理

/**
 * The PreDestroy annotation is used on methods as a callback notification to
 * signal that the instance is in the process of being removed by the
 * container. The method annotated with PreDestroy is typically used to
 * release resources that it has been holding. This annotation MUST be
 * supported by all container managed objects that support PostConstruct
 * except the application client container in Java EE 5. The method on which
 * the PreDestroy annotation is applied MUST fulfill all of the following
 */
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}

在这里插入图片描述

测试

@Component
public class Dog {
    public Dog(){
        System.out.println("dog constructor");
    }
    @PostConstruct
    public void init(){
        System.out.println("dog...@PostConstruct(init)");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("dog...@PreDestory(destroy)");
    }
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");
    System.out.println("关闭容器");
    applicationContext.close();
}

在这里插入图片描述


4、BeanPostProcessor(后处理器)

Spring底层对BeanPostProcess的使用
bean赋值,注入其他组件@Autowired,生命周期注解,异步@Async

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

接口源码

public interface BeanPostProcessor {
	//bean初始化之前进行处理
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	//bean初始化后进行处理
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

自定义BeanPostProcessor

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    //返回要使用的bean实例,无论是原始的还是包装好的;如果为null,则不会调用后续的BeanPostProcessors
    @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;
    }
}
public class Car {
    public Car(){
        System.out.println("car...constructor");
    }
    public void init(){
        System.out.println("car...init..");
    }
    public void destroy(){
        System.out.println("car...destroy..");
    }
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
    @Bean(initMethod = "init",destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }
}
@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
    System.out.println("容器创建完成");
    System.out.println("关闭容器");
    applicationContext.close();
}

在这里插入图片描述


5、容器创建原理基础

1、从Annotation注解开始
在这里插入图片描述
2、刷新IOC容器
在这里插入图片描述
3、完成Bean工厂的初始化
在这里插入图片描述
4、预实例化单例
在这里插入图片描述
5、获取bean
在这里插入图片描述
在这里插入图片描述
6、获取单实例
在这里插入图片描述
在这里插入图片描述
7、获取不到就自行创建
在这里插入图片描述
在这里插入图片描述
8、先执行populateBean为bean赋值后才初始化bean
在这里插入图片描述

9、关键就在这
执行调用初始化方法invokeInitMethods()
之前执行了 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
之后执行了applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
在这里插入图片描述
遍历得到容器中所有BeanProcessor,依次执行postProcessAfterInitialization(),如果返回null,跳出for循环,不会执行后面的postProcessAfterInitialization()
在这里插入图片描述
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
在这里插入图片描述
总结:
执行initializeBean()之前执行populateBean()对bean赋值,之后就是
执行前置applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
执行调用初始化方法invokeInitMethods()
执行后置applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)


三、属性赋值

1、@Value&@PropertySource

@Value:对属性进行赋值
@PropertySource:导入配置文件

application.properties

server.port=80
public class User {
    @Value("1")
    private int id;
    @Value("jiali")
    private String name;
    @Value("${server.port}")
    private String port;
    
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", port='" + port + '\'' +
                '}';
    }
}
@PropertySource(value = {"classpath:/application.properties"})
@Configuration
public class MainConfigOfPropertyValues {

    @Bean
    public User user() {
        return new User();
    }

}
@Test
public void test01(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.clas
    for (String name : applicationContext.getBeanDefinitionNames()) {
        System.out.println(name);
    }
    Object person = applicationContext.getBean("user");
    System.out.println(person);
}

在这里插入图片描述



四、自动装配

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

1、@Autowired&@Qualifier&@Primary[Spring规范的注解]

@Autowired
private BookDao bookDao;

1、默认优先按照类型去容器中找对应的组件

applicationContext.getBean(bookDao.class)

2、如果找到多个相同类型的组件,再将属性的id去容器中寻找

applicationContext.getBean("bookDao")

3、当容器有多个相同类型的bean时,可以使用 @Qualifier 指定需要装配的组件的id

@Qualifier("bookDao")
@Autowired
private BookDao bookDao;

4、自动装配默认一定要将属性赋值好,没有就会报错,可以使用@Autowired(required=false)解决,需要装配的属性不存在则返回null

@Autowired(required = false)
private BookDao bookDao;

5、@Primary:让Spring进行自动装配的时候,默认使用该bean
   也可以继续使用@Qualifier进行id指定

@Primary
@Bean
public BookDao bookDao(){
    return new BookDao();
}

2、@Resource(JSR250)@Inject(JSR330)[java规范的注解]

1、@Resource:可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配
   没有支持@Primary和@Autowired(requird=false)
2、@Inject:需要导入javax.inject的包,和@Autowired功能一样,也没有@Autowired(requird=false)


3、@Autowired

1、标注在方法位置上:@Bean+方法参数,参数从容器中获取。默认不写@Autowired效果是一样的
2、标在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略
3、放在参数位置


4、Aware

自定义组件想要使用Spring底层的一些组件(ApplicationContext,BeanFactory。。。)
实现xxxAware接口:创建对象的时候,会调用规定的方法注入相关组件,把Spring底层的一些组件注入到自定义bean中

@Component
public class Red implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("传入的IOC" + applicationContext);
        this.applicationContext = applicationContext;
    }
}

如下源码:依旧是调用BeanPostProcessor进行处理
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


5、@Profile

可以根据当前环境:动态地激活和切换一系列组件的功能
1、使用命令行动态参数:在虚拟机位置加载-Dspring.profiles.active=dev
2、使用代码激活

@Configuration
public class MainConfigOfProfile {
    @Profile("test")
    @Bean
    public MyDataSource testSource() {
        return new MyDataSource();

    }
    @Profile("dev")
    @Bean
    public MyDataSource devSource() {
        return new MyDataSource();
    }
    //未标注@Profile的默认生效
    @Bean
	public MyDataSource source() {
    	return new MyDataSource();
	}
}
@Test
public void test() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    applicationContext.getEnvironment().setActiveProfiles("dev");
    applicationContext.register(MainConfigOfProfile.class);
    applicationContext.refresh();
    
    for (String s : applicationContext.getBeanNamesForType(MyDataSource.class)) {
        System.out.println(s);
    }
}

在这里插入图片描述



二、AOP

1、AOP注解功能测试

  1. 业务类和切面类@Aspect加入容器
  2. 切面类方法标志通知注解@Before。。。
  3. 配置类开启基于注解的模式@EnableAspectJAutoProxy
类型运行时机
前置通知(@Before)在目标方法运行前运行
后置通知(@After)在目标方法运行后运行(无论方法正常结束还是异常)
返回通知(@AfterReturning)在目标方法正常返回后执行
异常通知(@AfterThrowing)在目标方法出现异常后执行
环绕通知(@Around)动态代理,手动推进目标方法运行(joinPoint.procced())

测试
1、导入aop模块

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.12.RELEASE</version>
</dependency>

2、定义一个业务逻辑类(MathCalculator),在业务逻辑运行的时候将日志进行打印(方法之前,运行,结束,异常)

public class MathCalculator {
    public int divisor(int i, int j) {
	    System.out.println("MathCalculator...divisor...");
        return i / j;
    }
}

3、定义一个日志切面类(LogAspects)动态感知业务逻辑类并@Aspect

  1. @PonitCut:抽取公共切入点表达式
    在这里插入图片描述
  2. JoinPoint属性:可以获取方法的信息(一定要出现在方法参数列表的第一个位置,否则spring无法识别
    在这里插入图片描述
  3. @AfterReturing:returing属性可以用来获取方法的返回值
    在这里插入图片描述
  4. @AfterThrowing:throwing属性可以用来获取方法的异常信息
    在这里插入图片描述

切面类

@Aspect
public class LogAspects {
    
    @Pointcut("execution(public int com.laptoy.aop.MathCalculator.*(..))")
    public void pointCut() {
    }

    @Before("execution(public int com.laptoy.aop.MathCalculator.divisor(int,int))")
    public void logStart(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        System.out.println("" + joinPoint.getSignature().getName() + "运行。。。@Before。。。参数列表是:{" + Arrays.asList(args) + "}");
    }

    @After("pointCut()")
    public void logEnd(JoinPoint joinPoint) {
        System.out.println("" + joinPoint.getSignature().getName() + "结束。。。@After。。。");
    }

    //returning属性可以用来获取方法返回值
    @AfterReturning(value = "pointCut()", returning = "result")
    public void logReturn(JoinPoint joinPoint, Object result) {
        System.out.println("" + joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning。。。运行结果是:{" + result + "}");
    }

    //throwing属性可以用来获取异常信息
    @AfterThrowing(value = "pointCut()", throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception) {
        System.out.println("" + joinPoint.getSignature().getName() + "异常@AfterThrowing。。。异常信息{" + exception + "}");
    }
}

4、将切面类和业务逻辑类加入容器并开启切面类注解配置@EnableAspectJAutoProxy

xml开启切面功能(旧)等同于@EnableAspectJAutoProxy

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }
    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}

5、将获取类交给spring管理

@Test
public void test01() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
    //不要自己创建对象(这样spring无法接管对象导致aspect无法执行)
    //MathCalculator mathCalculator = new MathCalculator();
    //mathCalculator.divisor(4,2);
    
    MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
    mathCalculator.divisor(4, 2);
    //mathCalculator.divisor(4, 0);
}

在这里插入图片描述

mathCalculator.divisor(4, 0);

在这里插入图片描述

2、AOP原理

1、@EnableAspectJAutoProxy调用@Import给容器导入了AspectJAutoProxyRegistrar.class
在这里插入图片描述
2、利用registerBeanDefinitions()自定义给容器导入组件
此处可以参考上文的一、组件注册 6、@import 3、ImportBeanDefinitionRegistrar
在这里插入图片描述
3、debug开始测试
在这里插入图片描述
进入该方法,发现是給容器注册了AnnotationAwareAspectJAutoProxyCreator
在这里插入图片描述
重点分析AnnotationAwareAspectJAutoProxyCreator

流程:
1、传入配置类,创建ioc容器
在这里插入图片描述
2、注册配置类,调用refresh()刷新
在这里插入图片描述
3、registerBeanPostProcessors(beanFactory)注册bean的后置处理器来方便拦截bean的创建
在这里插入图片描述
registerBeanPostProcessors(beanFactory)方法流程:
3.1、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
在这里插入图片描述
在这里插入图片描述
3.2、给容器中加别的BeanPostProcessor
在这里插入图片描述
3.3、优先注册实现了PriorityOrdered接口的BeanPostProcessor
再给容器注册实现了Ordered接口的BeanPostProcessor
在这里插入图片描述
3.4、接下来注册没实现优先级接口的BeanPostProcessor
在这里插入图片描述
当前的AnnotationAwareAspectJAutoProxyCreator父类实现了Ordered接口(所以是走图3.3的Next流程)

注册BeanPostProcessor实际上就是创建BeanPostProcessor对象,保存在容器中(容器本身是没有这个bean的,但还是会尝试获取,获取不到就创建):下图快速展示下获取及获取不到流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
获取不到就创建bean
在这里插入图片描述
4、创建bean
在这里插入图片描述
5、初始化bean(初始化bean之前调用了populateBean给bean的属性赋值)
在这里插入图片描述
6、调用感知方法invokeAwareMethods
在这里插入图片描述
题外话这里不是该流程:仅仅作为加强bean初始化BeanPostProcessor概念
调用初始化方法invokeInitMethods()(之前和之后都会调用bean的后置处理器)
applyBeanPostProcessorsBeforeInitialization()初始化之前调用后置处理器
applyBeanPostProcessorsAfterInitialization()初始化之后调用后置处理器
在这里插入图片描述
这里以applyBeanPostProcessorsBeforeInitialization()为例子,其实两个任意一个为例都可以:获取所有的BeanPostProcessor
在这里插入图片描述
回到流程
7、判断是否实现了BeanFactoryAware接口
在这里插入图片描述
8、如果是,就来调用setBeanFactory方法
在这里插入图片描述
9、AnnotationAwareAspectJAutoProxyCreator创建成功
调用initBeanFactory
在这里插入图片描述
在这里插入图片描述
快速执行下一步,最后
10、把BeanPostProcessor注册到BeanFactory中
在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laptoy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值