spring2

Spring原理java

组件注册@Configuration和@Bean

配置类mysql

package com.great.config;

import com.great.service.BookService;
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 com.great.bean.Person;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;

//配置类==配置文件
@Configuration  //告诉Spring这是一个配置类
//@ComponentScans(
//        value = {
//                @ComponentScan(value="com.great",includeFilters = {
//                            //按照注解
////                            @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
//                            @Filter(type=FilterType.ANNOTATION,classes={Repository.class}),
////                            //按照给定的类型
////                            @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
//                            //使用自定义规则。由于上面的value="com.great"全部该包下的全部的类都会进入这个自定义的规则里面进行匹配。
//                            @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
//                },useDefaultFilters = false)
//        }
//)
//@ComponentScan  value:指定要扫描的包
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只须要包含哪些组件
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式  (这个基本不用)
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则

//@ComponentScan(value="com.great")
public class MainConfig {

    //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名做为id,这里咱们指定的id是person
    @Bean("person")
    public Person person01(){
        return new Person("lisi", 20);
    }

}

测试一下linux

package com.great;

import com.great.config.MainConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.bean.Person;

public class MainTest {
    
    public static void main(String[] args) {
                                                   //AnnotationConfigApplicationContext注解式的ApplicationContext
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);

        //获取ioc容器里面的Person类型的全部bean的名字
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : namesForType) {
            System.out.println(name);
        }
    
    }

}

运行结果spring

Person [name=张三, age=18, nickName=${person.nickName}]
person

分割线
---

---sql

组件注册@Configuration和@ComponentScans自动扫描组件和指定扫描啊规则

在这里插入图片描述
咱们在看MyTypeFilter.class编程

package com.great.config;

import java.io.IOException;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.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();

        String className = classMetadata.getClassName();
        System.out.println("--->"+className);
        //若是className包含er就匹配成功,也就扫描进IOC容器里面了。
        if(className.contains("er")){
            return true;
        }
        return false;
    }

}

做用域

懒加载只针对单实例bean
在这里插入图片描述
在这里插入图片描述windows

分割线


注解@Conditional

在配置类中进行配置,以下图app

在这里插入图片描述

package com.great.condition;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

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

    /**
     * ConditionContext:判断条件能使用的上下文(环境)
     * AnnotatedTypeMetadata:注释信息
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 是否为linux系统
        //一、能获取到ioc使用的beanfactory
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        //二、获取类加载器
        ClassLoader classLoader = context.getClassLoader();
        //三、获取当前环境信息
        Environment environment = context.getEnvironment();
        //四、获取到bean定义的注册类
        BeanDefinitionRegistry registry = context.getRegistry();

        String property = environment.getProperty("os.name");
        //能够判断容器中的bean注册状况,也能够给容器中注册bean。
        boolean definition = registry.containsBeanDefinition("person");
        if(property.contains("linux")){
            return true;
        }
        return false;
    }

}
package com.great.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

//判断是否为windows系统
public class WindowsCondition 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;
    }

}

测试一下
在这里插入图片描述
运行结果
在这里插入图片描述
注意注解Conditional也能够放在类上面的ide

在这里插入图片描述

@Import注解

该注解是给容器中注册组件
在这里插入图片描述post

第一种import

能够直接用import导

在这里插入图片描述

第二种import

@Configuration
//@Import(Color.class)
//@Import导入组件,id默认是组件的全类名
@Import({Color.class,MyImportSelector.class})
public class MainConfig2 {
package com.great.condition;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

//自定义逻辑返回须要导入的组件
public class MyImportSelector implements ImportSelector {

    //返回值,就是到导入到容器中的组件全类名
    //AnnotationMetadata:当前标注@Import注解的类的全部注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //importingClassMetadata
        //方法不要返回null值,要否则报空指针异常
        //这样就把实体类Blue,Yellow扫描进容器里面了。
        return new String[]{"com.great.bean.Blue","com.great.bean.Black"};
    }

}

这样就把Bule和Black加载到容器里面了

第三种import

在这里插入图片描述

package com.great.condition;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import com.great.bean.RainBow;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * AnnotationMetadata:当前类的注解信息
     * BeanDefinitionRegistry:BeanDefinition注册类;
     *      把全部须要添加到容器中的bean;调用
     *      BeanDefinitionRegistry.registerBeanDefinition手工注册进来
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //判断是否有Black和Blue的bean
        boolean definition = registry.containsBeanDefinition("com.great.bean.Black");
        boolean definition2 = registry.containsBeanDefinition("com.great.bean.Blue");
        if(definition && definition2){
            //指定Bean定义信息;(Bean的类型,Bean。。。)
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //注册一个Bean,指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }

}

运行结果就是把RainBow注册进容器里了,bean的名字是rainBow

给容器中注册组件的方式

在这里插入图片描述
如上图所示有四种方式,上面咱们介绍完了import的方式,下面咱们看FactoryBean的方式
在配置类里面加下面的代码

在这里插入图片描述

package com.great.bean;

import org.springframework.beans.factory.FactoryBean;

//建立一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    //返回一个Color对象,这个对象会添加到容器中
    @Override
    public Color getObject() throws Exception {
        System.out.println("ColorFactoryBean...getObject...");
        return new Color();
    }

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

    //是单例?
    //true:这个bean是单实例,在容器中保存一份
    //false:多实例,每次获取都会建立一个新的bean;
    @Override
    public boolean isSingleton() {
        return false;
    }

}

测试一下
在这里插入图片描述

bean的生命周期

> 这里是引用
在这里插入图片描述

指定指定初始化和销毁方法的几种方式

在这里插入图片描述
咱们先看第一种@Bean的方式

@Configuration
public class MainConfigOfLifeCycle {

//    @Scope("prototype") //多实例:容器不会管理这个bean;容器不会调用销毁方法。
    @Bean(initMethod="init",destroyMethod="detory")
    public Car car(){
        return new Car();
    }

}
package com.great.bean;

import org.springframework.stereotype.Component;

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

}

测试

package com.great.test;

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.config.MainConfigOfLifeCycle;

public class IOCTest_LifeCycle {

    @Test
    public void test01(){
        //一、建立ioc容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        System.out.println("容器建立完成...");

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

}

构造(对象建立):
单实例:在容器启动的时候建立对象
多实例:在每次获取的时候建立对象
在这里插入图片描述
在这里插入图片描述
第二种方式
在这里插入图片描述
在这里插入图片描述
第三种方式
加两个注解就能够了
在这里插入图片描述
在这里插入图片描述
第四种方式
在这里插入图片描述
这里使用的BBP的原理也就是BeanPostProcessor bean的后置处理器

在这里插入图片描述
容器里面的全部的bean都是要走这个咱们自定义的MyBeanPostProcessor
在这里插入图片描述

BeanPostProcessor的原理

仍是上面咱们自定义的
在这里插入图片描述
断点进来

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

在这里插入图片描述

BeanPostProcessor原理深挖

在这里插入图片描述
这里咱们看一下这个ApplicationContextAwareProcessor
在这里插入图片描述
在这里插入图片描述

这样咱们自定义一个类来试一下
在这里插入图片描述

属性赋值 @Value注解

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

在这里插入图片描述
运行结果
在这里插入图片描述

自动装配

@Autowired&@Qualifier&@Primary

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

自动装配-@Resource&@Inject

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

@Autowired不只能标注在属性位置

1 . 标注在方法参数上面 @Bean+方法参数
在这里插入图片描述
2.放在构造器上面和放在方法上面
在这里插入图片描述
总结:

在这里插入图片描述

分割线

自定义组件想要使用Spring容器底层的一些组件

在这里插入图片描述

新建一个red类

package com.great.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {

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

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
       //解析字符串
        String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
        System.out.println("解析的字符串:"+resolveStringValue);
    }

}

这样就完成自定义组件想要使用Spring容器底层的一些组件

咱们来看一下原理
以ApplicationContextAware接口为列例子
在这里插入图片描述
咱们在Red类里面的setApplicationContext上面打断点
咱们debug进去
在这里插入图片描述
能够看出来是ApplicationContextAwareProcessor和BeanPostProcessor在起做用

在这里插入图片描述

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

原理总结:

咱们的bean在初始化的时候,利用后置处理器,判断这个bean是否实现某个aware接口,而后调用相应的方法把组件传过来

取配置文件里面的常量值

在这里插入图片描述

@Profile注解

package com.great.config;


import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import com.great.bean.Yellow;
import com.mchange.v2.c3p0.ComboPooledDataSource;


/**
 * Profile:
 *      Spring为咱们提供的能够根据当前环境,动态的激活和切换一系列组件的功能;
 *
 * 开发环境、测试环境、生产环境;
 * 数据源:(/A)(/B)(/C);
 *
 *
 * @Profile:指定组件在哪一个环境的状况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
 *
 *      1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
 *      2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的全部配置才能开始生效
 *      3)、没有标注环境标识的bean在,任何环境下都是加载的;
 */

@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{

    @Value("${db.user}")
    private String user;

    //实现EmbeddedValueResolverAware接口把咱们的值解析器引进来
    //这也是一种方式用于解析值
    private StringValueResolver valueResolver;

    private String  driverClass;

    @Bean
    public Yellow yellow(){
        return new Yellow();
    }

    //默认是default
//    @Profile("default")
    @Profile("test")
    @Bean("testDataSource")         //@Value注解也能够直接写在参数上面
    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}");
    }

}
package com.great.test;

import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.great.bean.Yellow;
import com.great.config.MainConfigOfProfile;

public class IOCTest_Profile {

    //一、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
    //二、代码的方式激活某种环境;
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext =
                //这里用无参构造,目的是后面设置值
                new AnnotationConfigApplicationContext();
        //一、建立一个applicationContext
        //二、设置须要激活的环境
        applicationContext.getEnvironment().setActiveProfiles("dev");
        //三、注册主配置类
        applicationContext.register(MainConfigOfProfile.class);
        //四、启动刷新容器
        applicationContext.refresh();

        String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String string : namesForType) {
            System.out.println(string);
        }

        Yellow bean = applicationContext.getBean(Yellow.class);
        System.out.println(bean);
        applicationContext.close();
    }

}

AOP功能测试

在这里插入图片描述
【动态代理】

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

建一个目标类
在这里插入图片描述
而后是切面类
在这里插入图片描述
而后是配置类
在这里插入图片描述
测试一下
在这里插入图片描述
没有异常的时候运行结果以下

div运行。。。@Before:参数列表是:{[1, 1]}
MathCalculator...div...
div结束。。。@After
div正常返回。。。@AfterReturning:运行结果:{1}

有异常的时候运行结果以下

div运行。。。@Before:参数列表是:{[1, 0]}
MathCalculator...div...
div结束。。。@After
div异常。。。异常信息:{java.lang.ArithmeticException: / by zero}

AOP原理

//给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
@EnableAspectJAutoProxy

一切从这个注解提及。
要明白enable开头的看给容器中注册了什么组件,这个组件何时工做,这个组件的功能是什么?
在这里插入图片描述
在这里插入图片描述
上面流程能够看出注解@EnableAspectJAutoProxy注入了AnnotationAwareAspectJAutoProxyCreator.class

AnnotationAwareAspectJAutoProxyCreator.class是一个后置处理器,这个能够从他的父类看的出来

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

如今咱们重点研究AnnotationAwareAspectJAutoProxyCreator.

相关文章
相关标签/搜索