Spring注解驱动开发-day2

@Profile

指定组件在哪个环境下才能被注册到容器,不指定任何环境下都能注册这个组件
1.加了环境表示的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境
2.写在配置类上:只有是指定的环境的时候,整个配置里面的所有配置才能生效
3.没有标注环境标识的bean在任何环境下都是加载的

//spring为我们提供的可以根据当前还款,动态的激活和切换一系列组件的功能
//开发环境、测试环境、生成环境

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

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

    private StringValueResolver valueResolver;

    private String driverClass;

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

    @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/information_schema");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("dev")
    @Bean("dataSourceDev")
    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/mybatis_plus");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    @Profile("prod")
    @Bean("dataSourceProd")
    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/mysql");
        dataSource.setDriverClass(driverClass);
        return dataSource;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.valueResolver = resolver;
        driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }
}

测试类为:【报错】

public class IOCTest_Profile {

    //1.使用命令行动态参数,在虚拟机参数位置加载-Dspring.profile.active=test
    //2.代码的方式激活某种环境
    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigProfile.class);

        //1.创建一个applicationContext\
        //2.设置需要激活的环境
        applicationContext.getEnvironment().setActiveProfiles("test","dev");
        //3.注册主配置类
        applicationContext.register(MainConfigProfile.class);
        //4.启动刷新容器
        applicationContext.refresh();
        String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
        for (String string : beanNamesForType){
            System.out.println(string);
        }
        Yellow bean = applicationContext.getBean(Yellow.class);
        System.out.println(bean);
    }
}

AOP功能测试:

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

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

新建类:

public class MathCalculator {

    public int div(int i,int j){
        System.out.println("div调用");
        return i/j;
    }
}

切面类:

//@Aspect 告诉Spring当前类是一个切面类
@Aspect
public class LogAspects {

    //抽取公共的切入点表达式
    //1.本类引用
    //2.其他的切面引用
    @Pointcut("execution(public int com.yutou.aop.MathCalculator.*(..))")
    public void pointCut(){};

    //@Before 目标放之前切入:切入点表达式(指定在哪个方法切入)
    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        System.out.println("" + joinPoint.getSignature().getName()+"运行.. @Before:参数列表是:{"+ Arrays.asList(args)+ "}");
    }

    //
    @After("com.yutou.aop.LogAspects.pointCut()")
    public void logEnd(JoinPoint joinPoint){
        System.out.println("" + joinPoint.getSignature().getName() + "结束");
    }

    //JoinPoint joinPoint一定要出现在参数表的第一位
    @AfterReturning(value = "pointCut()",returning = "result")
    public void logReturn(JoinPoint joinPoint,Object result){
        System.out.println("" + joinPoint.getSignature().getName() +"除法正常返回,运行结果:{"+ result +"}");
    }

    @AfterThrowing(value = "pointCut()",throwing = "exception")
    public void logException(JoinPoint joinPoint, Exception exception){
        System.out.println("" + joinPoint.getSignature().getName()+"除法异常。异常信息:{}");
    }
}

将切面类和类加入容器,并开启aop注解

@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {

    //业务逻辑类加入容器中
    @Bean
    public MathCalculator calculator(){
        return new MathCalculator();
    }

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

测试类:

public class IOCTest_AOP {

    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);

        //不要自己创建对象
//        MathCalculator mathCalculator = new MathCalculator();
//        mathCalculator.div(1,1);
        MathCalculator bean = applicationContext.getBean(MathCalculator.class);
        bean.div(1,1);
    }
}

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

声明式事务:
环境搭建:
1.导入相关依赖 数据库驱动、Spring-jdbc模块
2.配置数据源:JdbcTemplate(Spring提供的简化数据操作的工具)操作数据

@Repository
public class UserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void insert(){
        String sql = "INSERT INTO `user`(username,age) VALUES(?,?)";
        String username = UUID.randomUUID().toString().substring(0, 5);
        jdbcTemplate.update(sql,username,19);

    }
}

3.给方法上标注@Transactional 表示当前方法是一个事务方法

@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    @Transactional
    public void insertUser(){
        userDao.insert();
        //otherDao.other();xxx
        System.out.println("插入完成");
        int i = 10/0;
   }
}

4.@EnableTransactionManagement 开启基于注解的事务管理功能
5.配置事务管理器来控制事务

//声明式事务
@EnableTransactionManagement
@ComponentScan("com.yutou.tx")
@Configuration
public class TxConfig {
    @Bean
    public DataSource dataSource() throws Exception{
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setUser("root");
        dataSource.setPassword("root123");
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/tbl_user");
        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());
    }
}

扩展原理:
@BeanPostProcessor:bean后置处理器,bean创建对象初始化前后进行拦截工作的
@BeanFactoryPostProcessor:beanFactory的后置处理器,在BeanFactory标准初始化之后调用,所有的bean定义已经把偶才能加载到beanFactory,但是bean的实例还未创建

1.ioc容器创建对象

@ComponentScan("com.yutou.ext")
@Configuration
public class ExtConfig {

    @Bean
    public Blue blue(){
        return new Blue();
    }
}

2.invokeBeanFactoryPostProcessors(beanFactory),执行BeanFactoryPostProcessor.如何找到所有的BeanFactoryPostProcessor并执行他们的方法:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("MyBeanFactoryPostProcessor..");
        int count = beanFactory.getBeanDefinitionCount();
        String[] names = beanFactory.getBeanDefinitionNames();
        System.out.println("当前beanFactory中有" + count +"个bean");
        System.out.println(Arrays.asList(names));
    }
}

测试类:

public class IOCTest_Ext {

    @Test
    public void test01(){
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);


        applicationContext.close();
    }
}

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

BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor

postProcessorBeanDefinitionRegistry() 在所有bean定义信息将要被加载,bean实例还未创建

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

优先于BeanFactoryPostProcessor执行,利用BeanDefinitionRegistryPostProcessor给容器中再额外添加一些组件

原理:
1.ioc创建对象
2.refresh() -> invokeBeanFactoryPostProcessors(beanFactory)
3.从容器中获取到所有的BeanDefinitionRegistryPostProcessor组件
3.1)依次触发所有的postProcessorBeanDefinitionRegistry()方法
3.2)再来触发postProcessBeanFactory()方法BeanFactoryPostProcessor
4.再来从容器中找到BeanFactoryPostProcessor组件,然后依次触发postProcessBeanFactory()方法

ApplicationListener:监听容器中发布的事件,事件驱动模型开发
public interface ApplicationListener监听ApplicationEvent及其下面的子事件

步骤:
1.写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
2.把监听器加入到容器
3.只要容器中有相关事件的发布,我们就能监听到这个事件
CountRefreshedEvent,容器刷新完成(所有bean都完全创建)会发布这个事件
CountClosedEvent,关闭容器会发布这个事件
4.发布一个事件:applicationContext.publishEvent();
ContextRefreshedEvent、IOCTest_Ext$1[source=我发布的时间]、ContextClosedEvent,
1.ContextRefreshedEvent事件
1.1)容器创建对象,refresh()
1.2)finishRefresh() 容器刷新完成
1.3) publishEvent(new ContextRefreshedEvent(this));
事件发布流程:
1.获取事件的多播器(派发器):getApplicationEventMulticaster()
2.multicastEvent派发事件
3.获取到所有的ApplicationListner:

事件多播器(派发器):
1.容器创建对象:refresh()
2.initApplicationEventMulticaster() 初始化ApplicationEventMulticaster
2.1)先去容器中找有没有id="applicationEventMulticaster"的组件
2.2)如果没有this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory)并且加入到容器中,我们就可以在其他组件要派发事件,自动注入这个applicationEventMulticaster

容器中有哪些监听器:
1.容器创建对象:refresh()
2.registerListeners():从容器中拿到所有的监听器,把他们注册到applicationEventMulticaster中

SmartInitializingSingleton原理:
1.ioc容器创建对象并refresh()
2.finishBeanFactoryintialization(beanFactory) 初始化剩下的单实例bean
2.1)先创建所有的单实例bean,getbean()
2.2)获取所有创建好的单实例bean,判断是否是SmartInitializingSingleton类型的。
如果是就调用afterSingletonsInstantiated()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qtayu

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

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

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

打赏作者

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

抵扣说明:

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

余额充值