1,@Configuration注解,表明这是一个配置类
相当于一个application.xml配置文件
2,@Bean 向容器中注册一个bean,类型为返回值类型,id默认是用方法名作为id,也可以指定id-》@Bean(“person”) 表示id为person
相当于xml中的<bean id="xxx" class="com.atsun.xxx">
在IOC容器启动时,会将方法的返回对象放入到容器中 默认方法名作为组件id
XML配置
<bean id="person" class="com.atsun.bean.Person">
<property name="name" value="zhangsan"></property>
<property name="age" value="20"></property>
</bean>
JAVA配置类
@Configuration
@Import({Color.class,MyImportSelect.class,MyImportBeanDefinitionRegistrar.class}) //快速引入组件
public void MyConfig{
@Bean("person")
public Person getPerson(){
return new Person("zhangsan",20);
}
}
public Person{
........
}
public @interface Bean {
@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
@Configuration 声明这是一个配置类
@Bean 给容器中注册一个bean,类型为返回值类型。id默认为方法名,bean注解中可传入参数指定id
@Import 快速给容器中导入一个组件
1)@Import({Color.class}) 容器中就会自动注册这个组件,id默认时全类名
2)@Import({MyImportSelect.class}):ImportSelector:返回需要导入的组件的全类名数组 引入多个Bean
实现ImportSelect接口重写selectImports方法返回要导入的组件的全类名数组
3)@Import({ImportBeanDefinitionRegistrar})
自定义类实现ImportBeanDefinitionRegistrar接口,手动注册bean到容器中
使用Spring提供的FactoryBean(工厂Bean接口)
实现工厂bean接口(FactoryBean)
默认获取到的bean是工厂bean调用getObject()方法创建的对象,
要获取工厂bean本身,我们需要给id前面加上& xxxxx.getBean("&colorFactoryBean")
2------》@Import({MyImportSelect.class})
//返回值就是要导入到容器中的组件的全类名
//AnnotationMetadata 当前标注@Import注解的类的所有注解信息
public class MyImportSelect implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.atsun.Person1","com.atsun.Person2"};
}
}
3----》3)@Import({MyImportBeanDefinitionRegistrar.class})
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean flag = registry.containsBeanDefinition("com.atsun.bean.Color");//在容器中是否有这个bean
if(flag){
注册bean-->
//指定bean的定义信息(可以指定Bean的类型,作用域等)
RootBeanDefinition beanDefinition = new RootBeanDefinition(Red.class);
//注册一个bean,指定bean名称
registry.registerBeanDefinition("red",beanDefinition);
}
}
}
//虽然装配的是ColorFactoryBean,但实际返回的是工厂bean调用getObject()方法创建的对象
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}
//创建spring定义的工厂bean
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个color对象,这个对象会添加到容器中
public Color getObject() throws Exception {
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
//是单例吗 true 是单实例,在容器中保存一份
// false 多实例 每次获取都会创建一个新的bean(就是调用getObject()方法)
public boolean isSingleton() {
return true;
}
}
@PropertySource
datasource.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_day01
jdbc.username=root
jdbc.password=1234
/**
* spring的配置类
* 用于替代xml配置
*/
@Configuration
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfiguration {
}
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
}
@Bean
作用:
它写在方法上,表示把当前方法的返回值存入spring的ioc容器。
同时还可以出现在注解上,作为元注解来使用。
属性:
name:
用于指定存入spring容器中bean的标识。支持指定多个标识。当不指定该属性时,默认值是当前方法的名称。
value:
此属性是在4.3.3版本之后加入的。它和name属性的作用是一样的。
autowireCandidate:
用于指定是否支持自动按类型注入到其他bean中。只影响@Autowired注解的使用。不影响@Resource注解注入。默认值为true,意为允许使用自动按类型注入。
initMethod:
用于指定初始化方法。
destroyMethod:
用于指定销毁方法。
使用场景:
通常情况下,在基于注解的配置中,我们用于把一个类存入spring的ioc容器中,首先考虑的是使用@Component以及他的衍生注解。但是如果遇到要存入容器的Bean对象不是我们写的类,此时无法在类上添加@Component注解,这时就需要此注解了。
示例:
例如,在我们配置JdbcTemplate使用Spring内置数据源DriverManagerDataSource时,数据源类是spring-jdbc这个jar包中类,此时我们无法编辑,在上面加注解,此时就可以使用@Bean注解配置。
@Import
import导入的类在容器中的bean名称是全限定类名
ImportSelector和ImportBeanDefinitionRegistrar介绍
特别说明:
我们在注入bean对象时,可选的方式有很多种。
例如:
我们自己写的类,可以使用@Component,@Service,@Repository,@Controller等等。
我们导入的第三方库中的类,可以使用@Bean(当需要做一些初始化操作时,比如DataSource),也可以使用@Import注解,直接指定要引入的类的字节码。
但是当我们的类很多时,在每个类上加注解会很繁琐,同时使用@Bean或者@Import写起来也很麻烦。此时我们就可以采用自定义ImportSelector或者ImportBeanDefinitionRegistrar来实现。顺便说一句,在SpringBoot中,@EnableXXX这样的注解,绝大多数都借助了ImportSelector或者ImportBeanDefinitionRegistrar。在我们的spring中,@EnableTransactionManagement就是借助了ImportSelector,而@EnableAspectJAutoporxy就是借助了ImportBeanDefinitionRegistrar。
共同点:
他们都是用于动态注册bean对象到容器中的。并且支持大批量的bean导入。
区别:
ImportSelector是一个接口,我们在使用时需要自己提供实现类。实现类中返回要注册的bean的全限定类名数组,然后执行ConfigurationClassParser类中中的processImports方法注册bean对象的。
ImportBeanDefinitionRegistrar也是一个接口,需要我们自己编写实现类,在实现类中手动注册bean到容器中。 可以使用包扫描注册到容器中
注意事项:
实现了ImportSelector接口或者ImportBeanDefinitionRegistrar接口的类不会被解析成一个Bean注册到容器中。
同时,在注册到容器中时bean的唯一标识是全限定类名,而非短类名。
PropertySource
作用:
用于指定读取资源文件的位置。注意,它不仅支持properties,也支持xml文件,并且通过YAML解析器,配合自定义PropertySourceFactory实现解析yml配置文件(详情请参考第五章第8小节自定义PropertySourceFactory实现YAML文件解析)。
属性:
name:
指定资源的名称。如果没有指定,将根据基础资源描述生成。
value:
指定资源的位置。可以是类路径,也可以是文件路径。
ignoreResourceNotFound:
指定是否忽略资源文件有没有,默认是false,也就是说当资源文件不存在时spring启动将会报错。
encoding:
指定解析资源文件使用的字符集。当有中文的时候,需要指定中文的字符集。
factory:
指定读取对应资源文件的工厂类,默认的是PropertySourceFactory。
使用场景:
我们实际开发中,使用注解驱动后,xml配置文件就没有了,此时一些配置如果直接写在类中,会造成和java源码的紧密耦合,修改起来不方法。此时一些配置可以使用properties或者yml来配置就变得很灵活方便。
2,注解扫描
XML 配置
<!-- 包扫描 只要标注了@Controller @Service @Component @Repository 都会被自动扫描加入容器-->
<context:component-scan base-package="com.atsun" use-default-filters="false">
<context:exclude-filter type="annotation" expression=""/>
<context:include-filter type="annotation" expression=""/> 只包含,需要禁用默认规则
</context:component-scan>
<bean id="person" class="com.atsun.bean.Person">
<property name="name" value="zhangsan"></property>
<property name="age" value="20"></property>
</bean>
JAVA配置类
作用:
用于指定创建容器时要扫描的包。该注解在指定扫描的位置时,可以指定包名,也可以指定扫描的类。同时支持定义扫描规则,例如包含哪些或者排除哪些。同时,它还支持自定义Bean的命名规则
属性:
value:
用于指定要扫描的包。当指定了包的名称之后,spring会扫描指定的包及其子包下的所有类。
basePackages:
它和value作用是一样的。
basePackageClasses:
指定具体要扫描的类的字节码。 扫描的是指定类所在包下的所有子包
nameGenrator:
指定扫描bean对象存入容器时的命名规则。详情请参考第五章第4小节的BeanNameGenerator及其实现类。
scopeResolver:
用于处理并转换检测到的Bean的作用范围。
soperdProxy:
用于指定bean生成时的代理方式。默认是Default,则不使用代理。详情请参考第五章第5小节ScopedProxyMode枚举。
resourcePattern:
用于指定符合组件检测条件的类文件,默认是包扫描下的 **/*.class 当前包及其子包,任意类(*.clsaa)
useDefaultFilters:
是否对带有@Component @Repository @Service @Controller注解的类开启检测,默认是开启的。
includeFilters:
自定义组件扫描的过滤规则,用以扫描组件。
FilterType有5种类型:
ANNOTATION, 注解类型 默认
ASSIGNABLE_TYPE,指定固定类
ASPECTJ, ASPECTJ类型
REGEX,正则表达式
CUSTOM,自定义类型
详细用法请参考第五章第6小节自定义组件扫描过滤规则
excludeFilters:
自定义组件扫描的排除规则。
lazyInit:
组件扫描时是否采用懒加载 ,默认不开启。
使用场景:
在注解驱动开发时,我们自己编写的类都使用注解的方式进行配置,要想让spring添加到ioc容器中,就需要使用此注解来实现组件的扫描。
细节:
在spring4.3版本之后还加入了一个@ComponentScans的注解,该注解就是支持配置多个@ComponentScan。
spring默认命名规则 首字母小写
先找自己指定的
解析注解信息
BeanDefinition test1 = ctx.getBeanDefinition("appConfig");
if(test1 instanceof AnnotatedBeanDefinition){
AnnotatedBeanDefinition meta = (AnnotatedBeanDefinition) test1;
AnnotationMetadata metadata = meta.getMetadata();
Set<String> annotationTypes = metadata.getAnnotationTypes();
for (String annotationType : annotationTypes) {
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(annotationType, false));
Object value = annotationAttributes.get("value");
if (value instanceof String) {
System.out.println(((String) value));
}
}
}
默认规则:
@Configuration
@ComponentScan(value = "com.atsun",includeFilters = {
/* @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class}),*/
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}) 自定义扫描,实现TypeFilter 接口
},useDefaultFilters = false)
public class MainConfig {
@Bean(name = "person")
public Person getPerson(){
return new Person("张三",18);
}
}
/*
@ComponentScan value:指定要扫描的包
--excludeFilters =Filter[] 排除那些组件,例如:排除包含Controller和service注解的
--includeFilters =Filter[] 包含那些组件 需要禁用默认规则useDefaultFilters = false
--FilterType.ANNOTATION 按照注解
--FilterType.ASSIGNABLE_TYPE 按照给定的类型
--ASPECTJ, 按照切入点表达式
--REGEX, 按照正则匹配
--CUSTOM; 自定义过滤规则
*/
----》自定义过滤规则 CUSTOM
//MetadataReader 读取到当前正在扫描的类信息
//MetadataReaderFactory 可以获取到其他任何类信息
public class MyTypeFilter implements TypeFilter {
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);
if(className.contains("er")){
return true;
}
return false;
}
}
作用域 @Scope注解
@Configuration
public class MyConfug2 {
@Scope(value = "singleton")
@Lazy 对单实例启用
@Bean(value = "person")
public Person getPerson(){
return new Person("zhangsan",22);
}
}
//默认时单实例的
@Scope
prototype 多实例的
IOC容器启动并不会去调用方法创建对象放入容器中
每次获取的时候才会调用方法创建对象,每次获取都会创建一个对象
singleton单实例 默认值
IOC容器启动会调用方法创建对象放入到ioc容器中
以后每次获取直接从容器中拿
request 同一次请求创建一个
session 同一个session创建一个
@Lazy 懒加载,对单实例有效
* 单实例bean 默认在容器启动时创建对象
* 懒加载 容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化
按照条件判断给容器中注册组件
@Conditional({参数必须为Condition类型}) Condition是一个接口 所以需要继承Contidion,并实现matches方法,返回true则满足
public @interface Conditional {
Class<? extends Condition>[] value();
}
既可以作用在方法上又可以作用在类上
@Configuration
//类中组件统一设置,满足当前条件,这个类中的配置的说有bean才能生效
@Conditional({LishiCon.class}) 如果LishiCon-->matches返回true则当前类中注入的bean有效
public class MyConfug2 {
@Scope(value = "singleton")
@Lazy //懒加载 只对单实例有效
@Bean(value = "person")
public Person getPerson() {
return new Person("zhangsan", 22);
}
@Bean("zhangsan")
@Conditional({LishiCon.class})
public Person person1(){
return new Person("zhangsan",21);
}
@Bean("lishi")
@Conditional(zhangsanCon.class)
public Person person2(){
return new Person("lishi",23);
}
}
Condition.class
public class LishiCon implements Condition {
/*
* conditionContext 判断条件能使用的上下文(环境)
* annotatedTypeMetadata 注释信息
*
* 如果容器中有person 则返回true,创建id为 zhangsan的容器
* */
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
获取bean工厂
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
获取环境变量
Environment environment = conditionContext.getEnvironment();
//获取bean定义的注册类,所有的bean都在这里注册 能注册 移除 查询 bean
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//在ioc容器中有person时生效
boolean person1 = registry.containsBeanDefinition("person");
System.out.println(person1+"-->");
return person1;
}
}
Bean的生命周期
bean创建-----------------》初始化-----------------------销毁过程
容器管理bean的生命周期
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法
指定初始化和销毁方法
xml init-method="该类初始化方法名" destroy-method="该类销毁方法名"
1、启动Spring容器
2、创建Bean对象 ----> 实际是在调用Bean对象的构造器
3、给Bean添加属性
4、调用Bean对象的初始化init-method
5、getBean获取某个bean对象,调用bean对象的某一个方法
6、调用Bean对象的销毁方法destory-method
7、Spring容器销毁
XML
<bean id="" class="" init-method="该类初始化方法名" destroy-method="该类销毁方法名"/>
注解
BeanPostProcessor.postProcessBeforeInitialization 初始化之前
初始化 对象完成并赋值好,调用初始化方法。 多实例bean获取bean才创建容器,再进行初始化
BeanPostProcessor.postProcessAfterInitialization 初始化之后
销毁 容器关闭的时候 多实例bean容器不会管理这个bean,容器不会调用销毁方法
1)指定初始化和销毁方法
通过@Bean指定init-method和 destory-method
public void test01(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfigofLifeCycle.class);
System.out.println("IOC created finish.......");
//关闭容器
applicationContext.close();
}
@Configuration
public class MainConfigofLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destory")
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 destory(){
System.out.println("car .....destory...");
}
}
控制台
car constructor.... 容器创建
car.....init.... 初始化
IOC created finish....... 创建完成
car .....destory... 销毁
2)通过让bean实现InitalizingBean(定义初始化逻辑)
DisposableBean(定义销毁逻辑)
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat() {
System.out.println("cat constructor.....");
}
//销毁,单实例bean容器关闭后调用
public void destroy() throws Exception {
System.out.println("destory........");
}
//初始化 bena创建完成并且属性都赋好值以后调用
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet.....");
}
}
@Configuration
@ComponentScan("com.atsun.bean")
public class MainConfigofLifeCycle {
/* @Bean(initMethod = "init",destroyMethod = "destory")
public Car car(){
return new Car();
}*/
}
public void test01(){
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfigofLifeCycle.class);
System.out.println("IOC created finish.......");
//关闭容器
applicationContext.close();
}
控制台
cat constructor.....
afterPropertiesSet.....
IOC created finish.......
destory........
3)使用JSR250
@PostConstruct 在bean创建完成并且属性赋值完成来执行初始化
@PreDistory 在容器销毁之前进行调用
@Component
public class Dog {
public Dog() {
System.out.println("Dog constructor......");
}
//对象创建并赋值之后调用
@PostConstruct
public void init(){
System.out.println("@PostConstruct.......");
}
//在容器移除对象之前回调
@PreDestroy
public void distory(){
System.out.println("@PreDestroy.........");
}
}
4)BeanPostProcessor【interface】 bean的后置处理器
在bean的初始化前后进行一些处理工作
--postProcessBeforeInitialization bean 任何初始化方法之前 进行后置处理工作
--postProcessAfterInitialization bean 任何初始化之后进行 后置处理工作
/*
* 初始化前后进行处理
* @param bean 刚创建的bean实例
* beanName 这个bean实例在容器中的名字
*
* return 可以直接返回刚创建的bean,也可以包装bean之后再返回
* */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization.."+beanName+"..."+bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization.."+beanName+"..."+bean);
return bean;
}
}
控制台
cat constructor..... 创建bean对象
postProcessBeforeInitialization..cat...com.atsun.bean.Cat@25359ed8 初始化方法之前
afterPropertiesSet..... 初始化
postProcessAfterInitialization..cat...com.atsun.bean.Cat@25359ed8 初始化方法之后
Dog constructor......
postProcessBeforeInitialization..dog...com.atsun.bean.Dog@12405818
@PostConstruct.......
postProcessAfterInitialization..dog...com.atsun.bean.Dog@12405818
IOC created finish.......
@PreDestroy.........
destory........
BeanPostProcessor原理
// Initialize the bean instance.初始化bean实例
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper); 为bean进行属性赋值(get/set等)
==========================================================================================================
exposedObject = initializeBean(beanName, exposedObject, mbd); 调用初始化方法
}
//初始化给定的bean实例,应用工厂回调
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
遍历容器中的所有BeanPostProcessors挨个执行postProcessBeforeInitialization
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); 初始化之前,应用所有的。。。
}
try {
invokeInitMethods(beanName, wrappedBean, mbd); 执行初始化
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); 初始化之后
}
return wrappedBean;
}
遍历容器中的所有BeanPostProcessors挨个执行postProcessBeforeInitialization,返回null不会执行后面的
BeanPostProcessor.postProcessBeforeInitialization
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
@Value注解
参数 1,基本数值
2,SPEL #{}
3,${} 取出配置文件的值(在运行时配置文件的参数都会放入换环境变量中)
public void Test02(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
//获取环境变量,配置文件中的属性都会加载到环境变量中,可以根据key直接取出来
ConfigurableEnvironment environment = applicationContext.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
}
properties
person.nickName=xiaozhangsan
//PropertySource读取外部配置文件中的K/V保存到环境变量中;加载完为外部配置文件后用${}取出配置文件中的值
@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
public class Person {
//使用@Value赋值
//1,基本数值
//2,SPEL #{}
//3,${} 取出配置文件的值(在运行环境变量的值--运行时都会放入换环境变量中)
@Value("zhangsan")
private String name;
@Value("#{20-2}")
private int age;
@Value("${person.nickName}")
private String nickName;
自动装配
Spring利用依赖注入(DI)完成对ioc容器中各个组件的依赖关系赋值
@Autowired:自动注入
------》required=false是否必须
1--》 默认优先按照类型去容器中找对应的组件,找到就赋值
2--》 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
3--》 没有找到就会报错 可以使用@Autowired(required=false)不是必须的,如果找到就装配找不到就不装配
@Qualifier(“组件id”)
指定需要装配的组件id,而不是使用属性名
@Primary:让spring进行自动装配的时候,默认使用首选的bean
优先级 @Primary----》@Qualifier-----》@Autowired(默认)
@Configuration
@ComponentScan({"com.atsun.Service","com.atsun.Dao","com.atsun.controller"})
public class MainConfigOfAutowired {
@Primary //作为首选的
@Bean
public BookDao bookDao(){
return new BookDao();
}
}
Spring还支持使用@Resource(JSR250)和@Inject(JSR330) 【java规范】
@Resource:默认按照组件名称进行装配
@Inject:需要导入javax-inject依赖,和Autowiredz功能一样,没有required=false
@Autowired:能标注在构造器,参数,方法,属性;都是从容器中获取参数组件的值
1)标注再方法上,Spring容器创建当前对象,就会调用方法完成赋值,方法使用的参数,自定义类型的值从ioc容器中获取
2)标注在有参构造器上,自动注入构造器所需参数,如果组件只有一个有参构造器,这个有参构造器的antowired可以省略,参数位置的组件还是自动从容器中获取
3)标注在方法参数上
@Bean标注的方法创建对象的时候,方法参数值冲容器中获取(Car)
@Bean
public Color color(Car car/*自动注入,省略@Autowired*/){
Color color=new Color();
color.setCar(car);
return color;
}
AOP
以前使用AOP注解需要在xml中开启基于注解的aop模式
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
现在使用配置类配置类
@EnableAsprctJAutoProxy 开启基于注解的aop模式
@Configuration
public class MainConfigOfAop{
@Bean
略........
}
@Component
@Aspect
public class LogAspect {
@Pointcut("execution(* com.atsun.service.Hello.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void hehe() {
System.out.println("before ...");
}
@After("pointcut()")
public void haha(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("After ...");
}
@AfterReturning(value = "pointcut()",returning = "rt")
public void xixi(JoinPoint joinPoint, Object rt) {
System.out.println("AfterReturning ...");
}
@AfterThrowing("pointcut()")
public void thr() {
System.out.println("AfterThrowing ...");
}
/*@Around("point()")
public void xxx(ProceedingJoinPoint pj) {
try {
System.out.println(pj.getThis());
System.out.println(pj.getTarget());
System.out.println(pj.getSourceLocation());
System.out.println("Around aaa ...");
pj.proceed();
System.out.println("Around bbb ...");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}*/
}
AnnotationAwareAspectJAutoProxyCreator 的注册
注册了AnnotationAwareAspectJAutoProxyCreator的bean定义信息
- 继承关系图
- 可以看到它实现了bean的后置处理器 表明他可能会在bean的创建过程中做一些事情
AnnotationAwareAspectJAutoProxyCreator的创建
在创建spring工厂的过程中,会有一部注册bean的后置处理器
在这个方法中就会注册拦截Bean创建的BeanPostProcess。AnnotationAwareAspectJAutoProxyCreator就会在这里进行创建,放在容器中,
invokeInitMethods
BeanFactoryAware#setBeanFactory()接口的回调,父类的setBeanFactory被调用
AbstractAdvisorAutoProxyCreator#setBeanFactory()
后置处理器初始化对象的时候回调
创建完成后注册到bean工厂中(有两份,单例池和后置处理器map中)
AnnotationAwareAspectJAutoProxyCreator的调用
因为是bean的后置处理器,并且已完成对象的创建,在未来创建其他组件时就会拦截创建过程
已创建好了对象,并且已在容器中,因为是在注册后置处理器时触发的,但是其他的后置处理器可能还未进行初始化,在其他后置处理器进行初始化时就可以进行干预了(因为是调用工厂的getBean() ,所以必须触发一串增强逻辑)
以后只要所有人创建Bean,都会在所有人创建真正对象之前,先拦截尝试能不能返回代理对象
第一次调用
当前要创建的bean是否是不能创建代理的类,是就放入advisedBeans
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法会被后置处理器调用 (这也是第一次后置处理器的调用)
给BeanPostProcessors一个返回代理而不是目标bean实例的机会。(这里基本上后不会有后置处理器进行真正处理)
就会调用到 AbstractAutoProxyCreator#postProcessBeforeInstantiation,目的就是找出一些不需要进行代理的对象缓存起来,后面才会进行真正的代理
进入前会先判断当前bean是否在advisedBeans中 (advisedBeans保存了所有不需要进行增强的bean)
如果是下面这些类型的bean都会被放进advisedBeans中
isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName){
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
Pointcut.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
或者标注了Aspect注解
(AnnotationUtils.findAnnotation(clazz, Aspect.class) != null
或者是否需要跳过,获取候选的增强器,(切面里面的通知方法)
每一个通知方法的增强器是InstantiationModelAwarePointcutAdvisor类型,
遍历每个增强器,看是否是AspectJPointcutAdvisor类型,如果是就会返回true,就代表会放进advisedBeans中,也就是不会(适合)被代理的集合。我们的增强器显然不是这个类型
找到容器中所有组件, 遍历判断是否是切面(标注了Aspect注解),将切面类的方法进行解析,每个增强方法都封装成一个Advisor(InstantiationModelAwarePointcutAdvisorImpl),将所有Advisor封装成list集合返回
将找到的Advisors缓存起来,下次来的时候直接就返回封装的所有的增强方法的集合
获取切面的所有方法
将所有增强方法封装成InstantiationModelAwarePointcutAdvisor返回
解析注解,根据不同的注解创建不同的对象 其中 After,AtAfterThrowing注解创建的对象实现了MethodInterceptor接口(后面就不需要适配器了)
第二次调用 创建代理
AbstractAutowireCapableBeanFactory#initializeBean
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
如果已被代理就直接返回
会去当前类型的所有增强器,
1)拿到所有增强器 前面解析类上的所有方法的注解就缓存起来了的 切入点
2)查看增强器能不能应用到这个对象上 并返回能应用的增强器 (用切入点表达式算每一个方法是否能匹配)
- 遍历所有增强器,查看是否能应用到当前类上
3) 拓展增强器 前面加一个MethodInterceptor
4)对增强器进行排序 决定了谁先执行谁后执行
5)返回能应用的增强器
- 如果有能应用的增强器,那么进行创建代理
1)获取所有增强器(通知方法)
2)保存到proxyFactory
3)创建代理对象:spring决定使用那种动态代理的方式
JDK,CGLib
将当前类存入不需要被代理的集合,并返回代理对象,存入容器中
以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程
- 创建代理工厂 使用代理工厂进行创建代理
加入拦截器,放方法执行时就会调用这个类的intercept方法
将proxyFactory也放进了代理对象,返回代理对象
目标方法的执行
容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xXx)
1)CGLliProxy.DynamicAdvisedInterceptor.intercept拦截目标方法的执行
2)根据ProxyFactory对象获取将要执行的目标方法的拦截器链(增强器(保存信息)包装成拦截器(可以执行目标方法)) 适配器包装
包装After注解的类以实现了MethodInterceptor在上面解析注解时有说到
3)没有拦截器链就直接执行目标方法
拦截器链 (每一个通知方法又被包装为拦截器,后来方法的执行都是利用MethodInterceptor机制)
4)如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等所有信息传入创建一个CglibMethodInvocation,并调用proceed方法
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
5)拦截器链的触发过程
如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后拦截器)执行目标方法;
链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;
拦截器的机制:保证通知方法与目标方法的执行顺序