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