文章目录
原来xml方式
https://www.jianshu.com/p/2692bf784976
BeanFactory 调用 initializeBean , initializeBean 调用所有的 BeanPostProcessor , BeanPostProcessor 调用 postProcessBeforeInitialization + postProcessAfterInitialization
注解驱动
Maven:spring-context
配置类
@Configuration
思路是用一个java类代替原来的xml文件
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
//用ComponentScans可以指定多个扫描规则,也可以直接写ComponentScan
@ComponentScans(//相当于原来xml配置的包扫描//带s
value = {
@ComponentScan(value="com.atguigu",
includeFilters = {
/*
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
@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:使用自定义规则
public class MainConfig {
//给容器中注册一个Bean;也可以在配置类上@Import({.class})
//类型为返回值的类型,id默认是用方法名作为id
@Bean("person")//id//还有@Scope()//@Lazy//
//@Conditional({})//@Conditional(LinuxCondition.class)这个类实现了Condition接口,具体可以去代码中看,实现了一个matches方法,返回bool//context.getEnvironment()获取操作环境,然后environment.getProperty("os.name")获取系统
public Person person01(){
return new Person("lisi", 20);
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
}
@ComponentScan【bean注册方法1】
是扫描配置类外加了如下这些注解的类:@Component、@Service、@Repository、@Controller
加了这些注解后,对应的类就会注册一个bean实例,为了让配置类知道去找到你注解了类,需要在配置类上用@ComponentScans指明去哪里扫描。
//配置类==配置文件
@Configuration //告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value="com.atguigu",includeFilters = {
/* @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
@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:使用自定义规则
public class MainConfig {
//给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
@Bean【bean注册方法2】
在配置类中直接在一个自定义方法上注解@Bean,返回的对象就会注册到容器中。
略嫌麻烦,常用@Import
//默认是单实例的
/**
* @Scope:调整作用域
* prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
* 每次获取的时候才会调用方法创建对象;
* singleton:单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。
* 以后每次获取就是直接从容器(map.get())中拿,
* request:同一次请求创建一个实例,需要在web环境
* session:同一个session创建一个实例,需要在web环境
*
* 懒加载:
* 单实例bean:默认在容器启动的时候创建对象;
* 懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;
*/
// @Scope("prototype")
@Lazy
@Bean("person")//@Bean(initMethod="init",destroyMethod="detory")//对应的init方法也在Person类里
//还有其他方法看生命周期部分
public Person person(){
System.out.println("给容器中添加Person....");
return new Person("张三", 25);
}
@Conditional
在@Bean的同个方法上注解@Conditional({自定义.class})
: 按照一定的条件进行判断,满足条件给容器中注册bean。
注解里可以传入一个自定义的class对象,
/**
* @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean
* 如果系统是windows,给容器中注册("bill")
* 如果是linux系统,给容器中注册("linus")
*/
@Conditional(LinuxCondition.class)//linux环境下才注册这个bean//自定义的class,返回bool
@Bean("linus")
public Person person02(){
return new Person("linus", 48);
}
//---------package com.atguigu.condition;-----------------------------------
//判断是否linux系统
public class LinuxCondition implements Condition {
/**
* ConditionContext:判断条件能使用的上下文(环境)
* AnnotatedTypeMetadata:注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系统
//1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
//3、获取当前环境信息
Environment environment = context.getEnvironment();
//4、获取到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;
}
}
@Import【bean注册方法3】
@Import标注在config注册类上,在注册ac的时候就会自动把@Import(Dog.class)里标注的类实例化注册到容器中
- @Import(要导入到容器中的组件.class);容器中就会自动注册这个组件,id默认是全类名
- @Import(new ImportSelector(){@Override}:返回需要导入的组件的全类名数组;(事务源码中会用到)
- @Import(new ImportBeanDefinitionRegistrar(,){@Override}:手动注册bean定义到容器中(aop源码中会使用)
在Configuration类上标注:;
@Import({
Color.class,Red.class,
MyImportSelector.class,//返回需要导入的组件的全类名数组;实现ImportSelector接口,函数式接口返回数组即可。可以返回一个空数组,但不要返回一个null
MyImportBeanDefinitionRegistrar.class}//手动注册bean到容器中。实现ImportBeanDefinitionRegistrar接口,一个void方法,
)
//返回需要导入的组件的全类名数组
public class MyImportSelector implements ImportSelector {
//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//importingClassMetadata
//方法不要返回null值
return new String[]{
"com.atguigu.bean.Blue","com.atguigu.bean.Yellow"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//逻辑:如果有红蓝的bean,就可以生成彩虹bean
boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
if(definition && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean定义,指定bean名,但还没有创建bean,getBean后才创建
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}
}
@FactoryBean【bean注册方法4】
给容器中注册组件;
1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]
2)、@Bean[导入的第三方包里面的组件]
4)、使用Spring提供的 FactoryBean(工厂Bean);
1)、默认获取到的是工厂bean调用getObject创建的对象
2)、要获取工厂Bean本身,我们需要给id前面加一个&
&colorFactoryBean
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Color.class;
}
//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}
}
//------在config配置类中----这是个工厂bean------
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();//注册的不是ColorFactoryBean,而是Color
}
组件注册
//介绍一个方法:
String[] =ac.getBeanNamesForType(Person.class);
生命周期
1)指定初始化和销毁方法;
-
通过@Bean指定init-method和destroy-method;
2)通过让Bean实现InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑);
3)可以使用JSR250;在bean类中的方法上注解
-
@PostConstruct:在bean创建完成并且属性赋值完成;来执行初始化方法
-
@PreDestroy:在容器销毁bean之前通知我们进行清理工作
4)实现接口BeanPostProcessor,并在该类上@Component
-
在bean初始化前后进行一些处理工作;
-
postProcessBeforeInitialization(bean,beanName)方法:在初始化之前工作。通过指定形参来确定操作的bean。每个bean前后都会执行
-
postProcessAfterInitialization():在初始化之后工作
package com.atguigu.config;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.atguigu.bean.Car;
/**
* bean的生命周期:
* bean创建---初始化----销毁的过程
* 容器管理bean的生命周期;
* 我们可以自定义初始化和销毁方法;容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
*
* 构造(对象创建)
* 单实例:在容器启动的时候创建对象
* 多实例:在每次获取的时候创建对象
*
* BeanPostProcessor.postProcessBeforeInitialization
* 初始化:
* 对象创建完成,并赋值好,调用初始化方法。。。
* BeanPostProcessor.postProcessAfterInitialization
* 销毁:
* 单实例:容器关闭的时候
* 多实例:容器不会管理这个bean;容器不会调用销毁方法;
*
*
* 遍历得到容器中所有的BeanPostProcessor;挨个执行beforeInitialization,
* 一但返回null,跳出for循环,不会执行后面的BeanPostProcessor.postProcessorsBeforeInitialization
*
* BeanPostProcessor原理
* populateBean(beanName, mbd, instanceWrapper);给bean进行属性赋值
* initializeBean{
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);执行自定义初始化
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
*}
*
* Spring底层对 BeanPostProcessor 的使用;
* bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor;
*/
//配置类
@ComponentScan("com.atguigu.bean")
@Configuration
public class MainConfigOfLifeCycle {
//@Scope("prototype")
@Bean(initMethod="init",destroyMethod="detory")
public Car car(){
return new Car();
}
}
@Component
public class Cat implements InitializingBean,DisposableBean {
//实现初始化和销毁接口
public Cat(){
System.out.println("cat constructor...");
}
@Override
public void destroy() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat...destroy...");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("cat...afterPropertiesSet...");
}
}
@Component
public class Dog implements ApplicationContextAware {
//实现ApplicationContextAware就自动调用setApplicationContext(ac)
//@Autowired
private ApplicationContext applicationContext;
public Dog(){
System.out.println("dog constructor...");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除对象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext = applicationContext;
}
}
后置处理器
实现接口BeanPostProcessor
,并且在该类上@Component
-
在bean初始化前后进行一些处理工作;
-
postProcessBeforeInitialization(bean,beanName)方法:在初始化之前工作。通过指定形参来确定操作的bean。每个bean前后都会执行
-
postProcessAfterInitialization():在初始化之后工作
new AppContext的时候就调用后置处理器了,倒数第二个调用refresh(),
一层一层的调用关系:
================
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
==================
refresh();
=============
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);//初始化所有剩下的非懒加载的单例
============
beanFactory.preInstantiateSingletons();
============
this.getBean(beanName);
===========
public Object getBean(String name) throws BeansException {
return this.doGetBean(name, (Class)null, (Object[])null, false);
}
===============
sharedInstance = this.getSingleton(beanName, new ObjectFactory<Object>() {
public Object getObject() throws BeansException {
try {
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
} catch (BeansException var2) {
AbstractBeanFactory.this.destroySingleton(beanName);
throw var2;
}
}
});
=================
singletonObject = singletonFactory.getObject();
newSingleton = true;
====================
return AbstractBeanFactory.this.createBean(beanName, mbd, args);
====================
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
//calculator时为空
return bean;//如果生成了代理对象,就直接返回了。没有生成则继续往下执行
}
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
===================
//创建实例
//属性赋值
this.populateBean(beanName, mbd, instanceWrapper);//给bean进行属性赋值setter getter
//初始化
if (exposedObject != null) {
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}
====================
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);//卡在这//
//打开是:getBeanPostProcessors().iterator();//循环遍历得到所有beanProcessor,挨个执行beanProcessor.postProcessBeforeInitialization()
/*
do {
if (!var4.hasNext()) {
return result;
}
BeanPostProcessor beanProcessor = (BeanPostProcessor)var4.next();
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
} while(result != null);
*/
}
try {
this.invokeInitMethods(beanName, wrappedBean, mbd);//执行bean初始化方法
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);//后置
}
===================
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
@Component//下面全部的bean都会经过
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;
}
}
ApplicationContextAwareProcessor帮组件注入IoC容器,我们用的是implements ApplicationContextAware,会自动注入到setApplicationContext()的方法参数中,这个功能就是ApplicationContextAwareProcessor做的,比如她的方法
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
//判断bean实现了Aware接口没有
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();//把ac传过来
}
if (acc != null) {
//ac有值
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareInterfaces(bean);//if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);、//调用set方法
}
return null;
}
}, acc);
}else
属性赋值@Value
在bean类中–属性上使用@Value("")赋值,相当于原来的<property>
。字符串内容可以是
- 基本数值
- 可以写SpEL; #{}
- 可以写
${}
;在配置类上注解@PropertySource(value="classpath:/person.properties")
。然后在bean类中@Value("${person.nickName}")
去除值
public class Person {
//使用@Value赋值;
//1、基本数值
//2、可以写SpEL; #{}
//3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
@Value("${person.nickName}")//去除配置文件/环境变量中的值//需要在配置类上加上注解@PropertySource("classpath:/dbconfig.properties")
private String nickName;
getter...setter...;
}
自动装配
@Autowired+@Qualifier+@Primary
spring支持3种自动装配
-
@Qualifier("bookDao")
:放在空成员上,指定要装配的bean id -
@Autowired(required=false);
:放在空成员上 -
@Primary("")
:下面两个是放在bean上。指定这是一个首选的bean,放在@Bean或@Component周围
java的自动装配:
@Resource
:可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;- @Inject
先按照类型去查找,有多个时候再将属性的名称作为组件的id去查找。或者在属性上使用@Qualifier("")指定id。找不到bean会报错,可以通过@Autowired(required=“false”)指定该属性可以不装配。
- 还可以@Primary让spring装配首选的bean,
- spring还支持使用@Resource(JSR250)和@Inject(JSR330)。java规范的注解。@Resource(name="")默认是通过组件名称装配的。没有能支持@Primary功能没有支持@Autowired(required=false)。@Inject需要导入依赖javax.inject,也没有required=false功能。@Autowired是spring定义的。@Resource和@Inject是java的规范。区别在于后面两个没有required=false
/**
* 自动装配;
* Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值;
*
* 1)、@Autowired:自动注入:
* 1)、默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
* 2)、如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找。ac.getBean("bookDao")
* 3)、@Qualifier("bookDao"):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
* 4)、自动装配默认一定要将属性赋值好,没有就会报错;
* 可以使用@Autowired(required=false);
* 5)、@Primary:让Spring进行自动装配的时候,默认使用首选的bean;
* 也可以继续使用@Qualifier指定需要装配的bean的名字
*
* 2)、Spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
* @Resource:
* 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;
* 没有能支持@Primary功能,没有支持@Autowired(reqiured=false);
* @Inject:
* 需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
* @Autowired:Spring定义的; @Resource、@Inject都是java规范
*
* AutowiredAnnotationBeanPostProcessor:解析完成自动装配功能;
*
* 3)、 @Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值
* 1)、[标注在方法位置]:标注在方法,Spring容器创建当前对象,就会调用方法,完成赋值;//方法使用的参数,自定义类型的值从ioc容器中获取
@Bean+方法参数;参数从容器中获取;默认不写@Autowired效果是一样的;都能自动装配 //
* 2)、[标在构造器上]:构造器用的组件也是从环境中自动获取。但是如果组件只有一个有参构造器(没有无参构造器),这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取,一个@Autowired都不需要加
* 3)、放在方法的参数位置:效果同理。同样还可以标在@Bean修饰的参数上
*
* 4)、自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);
* 只需要自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的set方法注入相关组件;Aware;
* 把Spring底层一些组件注入到自定义的Bean中;
* xxxAware:功能使用xxxProcessor;
* ApplicationContextAware==》ApplicationContextAwareProcessor;
*/
@Service
public class BookService {
//@Qualifier("bookDao")
//@Autowired(required=false)
//@Resource(name="bookDao2")//java规范
@Inject//JAVA规范
private BookDao bookDao;
public void print(){
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService [bookDao=" + bookDao + "]";
}
}
Aware
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx);自定义组件实现xxxAware;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware接口中有写。
-
把Spring底层一些组件注入到自定义的Bean中;比如实现了ApplicationContextAware接口,就会实现了setApplicationContext方法,会自动传入ApplicationContext。还有Bean
-
xxxAware:功能使用xxxProcessor;
-
ApplicationContextAware==》ApplicationContextAwareProcessor;
@Component
public class Red implements ApplicationContextAware,BeanNameAware,EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override//无需写Autowired
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
// TODO Auto-generated method stub
System.out.println("传入的ioc:"+applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
// TODO Auto-generated method stub
System.out.println("当前bean的名字:"+name);
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
// TODO Auto-generated method stub
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串:"+resolveStringValue);
}
}
@Profile:
-
Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
-
开发环境、测试环境、生产环境;数据源:(/A)(/B)(/C);
-
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
-
1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
-
2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
-
3)、没有标注环境标识的bean在,任何环境下都是加载的;
/**
* Profile:
* Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
*
* 开发环境、测试环境、生产环境;
* 数据源:(/A)(/B)(/C);
* 在pom.xml中引入c3p0数据源,mysql驱动
*
* @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
*
* 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
* 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* 3)、没有标注环境标识的bean在,任何环境下都是加载的;
*/
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String driverClass;
@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/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) {//另一种获取环境变量的方法
// TODO Auto-generated method stub
this.valueResolver = resolver;
driverClass = valueResolver.resolveStringValue("${db.driverClass}");
}
}
-------------------------------------------------
public class IOCTest_Profile {
//1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test、dev
//2、代码的方式激活某种环境;
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
//1、创建一个applicationContext
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4、启动刷新容器
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
- 导包spring-aspects
- 在配置类上@EnableAspectJAutoProxy
AOP:【动态代理】指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
1、导入aop模块;Spring AOP:(spring-aspects)
2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
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)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
* 2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
* 3)、开启基于注解的aop模式;@EnableAspectJAutoProxy
*
* AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】
* @EnableAspectJAutoProxy;
* 1、@EnableAspectJAutoProxy是什么?
* @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar
* 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion
* internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator
*
* 给容器中注册一个AnnotationAwareAspectJAutoProxyCreator;
*
* 2、 AnnotationAwareAspectJAutoProxyCreator:
* AnnotationAwareAspectJAutoProxyCreator
* ->AspectJAwareAdvisorAutoProxyCreator
* ->AbstractAdvisorAutoProxyCreator
* ->AbstractAutoProxyCreator
* implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
* 关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory
*
* AbstractAutoProxyCreator.setBeanFactory()
* AbstractAutoProxyCreator.有后置处理器的逻辑;
*
* AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory()
*
* AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
*
*
* 流程:
* ①1)、传入配置类,创建ioc容器
* ②2)、注册配置类,调用refresh()刷新容器;
* ③3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建;
* 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
* 2)、给容器中加别的BeanPostProcessor
* 3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
* 4)、再给容器中注册实现了Ordered接口的BeanPostProcessor;
* 5)、注册没实现优先级接口的BeanPostProcessor;
* 6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
* 创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
* 1)、创建Bean的实例
* 2)、populateBean;给bean的各种属性赋值
* 3)、initializeBean:初始化bean;
* 1)、invokeAwareMethods():处理Aware接口的方法回调
* 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
* 3)、invokeInitMethods();执行自定义的初始化方法
* 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
* 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder
* 7)、把BeanPostProcessor注册到BeanFactory中;
* beanFactory.addBeanPostProcessor(postProcessor);
* =======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程========
*
* AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor
* ④4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean
* 1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
* getBean->doGetBean()->getSingleton()->
* 2)、创建bean
* 【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】
* 1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建;只要创建好的Bean都会被缓存起来
* 2)、createBean();创建bean;
* AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例
* 【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】
* 【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】
* 1)、resolveBeforeInstantiation(beanName, mbdToUse);希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续
* 1)、后置处理器先尝试返回对象;
* bean = applyBeanPostProcessorsBeforeInstantiation():
* 拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor;
* 就执行postProcessBeforeInstantiation
* if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
*
* 2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样;
* 3)、
*
------------
* AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
* 1)、每一个bean创建之前,调用postProcessBeforeInstantiation();
* 关心MathCalculator和LogAspect的创建
* 1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
* 2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,
* 或者是否是切面(@Aspect)
* 3)、是否需要跳过
* 1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
* 每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
* 判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
* 2)、永远返回false
*
* 2)、创建对象
* postProcessAfterInitialization;
* return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下
* 1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors
* 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
* 2、获取到能在bean使用的增强器。
* 3、给增强器排序
* 2)、保存当前bean在advisedBeans中;
* 3)、如果当前bean需要增强,创建当前bean的代理对象;
* 1)、获取所有增强器(通知方法)
* 2)、保存到proxyFactory
* 3)、创建代理对象:Spring自动决定
* JdkDynamicAopProxy(config);jdk动态代理;
* ObjenesisCglibAopProxy(config);cglib的动态代理;
* 4)、给容器中返回当前组件使用cglib增强了的代理对象;
* 5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;
*
*
* 3)、目标方法执行 ;
* 容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);
* 1)、CglibAopProxy.intercept();拦截目标方法的执行
* 2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链;
* List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
* 1)、List<Object> interceptorList保存所有拦截器 5
* 一个默认的ExposeInvocationInterceptor 和 4个增强器;
* 2)、遍历所有的增强器,将其转为Interceptor;
* registry.getInterceptors(advisor);
* 3)、将增强器转为List<MethodInterceptor>;
* 如果是MethodInterceptor,直接加入到集合中
* 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
* 转换完成返回MethodInterceptor数组;
*
* 3)、如果没有拦截器链,直接执行目标方法;
* 拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)
* 4)、如果有拦截器链,把需要执行的目标对象,目标方法,
* 拦截器链等信息传入创建一个 CglibMethodInvocation 对象,
* 并调用 Object retVal = mi.proceed();
* 5)、拦截器链的触发过程;
* 1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;
* 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
* 拦截器链的机制,保证通知方法与目标方法的执行顺序;
*
* 总结:
* 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)、效果:
* 正常执行:前置通知-》目标方法-》后置通知-》返回通知
* 出现异常:前置通知-》目标方法-》后置通知-》异常通知
*/
声明式事务
pom:数据源c3p0 数据库驱动mysql jdbc(简化数据库和事务控制)
配置数据源,JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
- 声明式事务:
环境搭建:
-
1、导入相关依赖:数据源、数据库驱动、Spring-jdbc模块
-
2、配置数据源、JdbcTemplate(Spring提供的简化数据库操作的工具)操作数据
-
3、给方法上标注 @Transactional 表示当前方法是一个事务方法;
-
4、
@EnableTransactionManagement
开启基于注解的事务管理功能;@EnableXXX -
5、配置事务管理器来控制事务;
@Bean public PlatformTransactionManager transactionManager()
/**
* 原理:
* 1)、@EnableTransactionManagement
* 利用TransactionManagementConfigurationSelector给容器中会导入组件
* 导入两个组件
* AutoProxyRegistrar
* ProxyTransactionManagementConfiguration
* 2)、AutoProxyRegistrar:
* 给容器中注册一个 InfrastructureAdvisorAutoProxyCreator 组件;
* InfrastructureAdvisorAutoProxyCreator:?
* 利用后置处理器机制在对象创建以后,包装对象,返回一个代理对象(增强器),代理对象执行方法利用拦截器链进行调用;
*
* 3)、ProxyTransactionManagementConfiguration 做了什么?
* 1、给容器中注册事务增强器;
* 1)、事务增强器要用事务注解的信息,AnnotationTransactionAttributeSource解析事务注解
* 2)、事务拦截器:
* TransactionInterceptor;保存了事务属性信息,事务管理器;
* 他是一个 MethodInterceptor;
* 在目标方法执行的时候;
* 执行拦截器链;
* 事务拦截器:
* 1)、先获取事务相关的属性
* 2)、再获取PlatformTransactionManager,如果事先没有添加指定任何transactionmanger
* 最终会从容器中按照类型获取一个PlatformTransactionManager;
* 3)、执行目标方法
* 如果异常,获取到事务管理器,利用事务管理回滚操作;
* 如果正常,利用事务管理器,提交事务
*/
@EnableTransactionManagement
@ComponentScan("com.atguigu.tx")
@Configuration
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
//
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件,不会创建new两个对象
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器 在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());//事务管理器管理事务源
}
}
@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);
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Transactional//配置类上还需要@EnableTranctionManagement//原来是<tx-annotation-driven/>//还需要事务管理器DataSourceTransactionManager
public void insertUser(){
userDao.insert();
//otherDao.other();xxx
System.out.println("插入完成...");
int i = 10/0;//出错需要回滚
}
}