- 学无止境 Java工程师的进阶之旅
一、组件注册(IOC)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
概述
- @Configuration 用于标注配置类
- @Bean 结合@Configuration使用。可以导入第三方组件
- 包扫描@ComponentScan (@ComponentScans可以配置多个扫描,@TypeFilter:指定过滤规则,自己实现TypeFilter类)
组件(@Service、@Controller、@Repository):包扫描+组件注解导入注解。 - @Scope:设置组件作用域 1.prototype:多例的2.singleton:单例的(默认值)
- @Lazy 懒加载
- @Conditional:按照一定的条件进行判断,满足条件给容器中注册Bean,传入Condition数组,,使用时需自己创建类继 承Condition然后重写match方法。
- @Import[快速给容器中导入一个组件]
1、Import(类名),容器中就会自动注册这个组件,id默认是组件的全名
2、ImportSelector:返回需要导入的组件的全类名的数组
3、ImportBeanDefinitionRegistrar:手动注册bean - FactoryBean:工厂Bean,交给spring用来生产Bean到spring容器中.可以通过前缀&来获取工厂Bean本身.
1、@Configuration @Bean
给容器注册组件
1、配置文件注册bean(旧)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.laptoy.bean.Person">
<property name="age" value="21"></property>
<property name="name" value="jiali"></property>
</bean>
</beans>
public class MainTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
2、注解方式注册bean
@Configuration
public class MainConfig {
//给容器注册bean,id默认用方法名id
//也可以通过@Bean注解name属性指定id
@Bean(name = "person")
public Person person() {
return new Person("jiali", 21);
}
}
public class MainTest {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
2、@ComponentScan
自动扫描组件&自定义扫描规则
1、xml文件配置(旧)
<!-- 包扫描:只要标注了@Controller、@Service、@Reposity、@Component -->
<context:component-scan base-package="com.laptoy" />
2、注解配置
1.配置类配置
@Configuration
@ComponentScan(value = "com.laptoy")
//value 指定要扫描的包
public class MainConfig {
@Bean
public Person person() {
return new Person("jiali", 21);
}
}
2.添加controller和service
@Controller
public class BookController {
}
@Service
public class BookService {
}
@Test
public void test01(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
3、测试属性excludeFilters
(指定扫描的时候排除那些)
@Configuration
@ComponentScan(value = "com.laptoy", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})
public class MainConfig {
@Bean
public Person person() {
return new Person("jiali", 21);
}
}
4、测试属性 includeFilters
(指定扫描的时候只扫描哪些),需要关闭默认规则useDefaultFilters = false
@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)
public class MainConfig {
@Bean
public Person person() {
return new Person("jiali", 21);
}
}
5、FilterType
过滤规则
@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
@Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
}, useDefaultFilters = false)
//FilterType.ANNOTATION 按照注解
//FilterType.ASSIGNABLE_TYPE 按照指定的类型
//FilterType.ASOECTJ 使用ASPECTJ表达式
//FilterType.REGEX 使用正则
//FilterType.CUSTOM 使用自定义规则
public class MainConfig {
@Bean
public Person person() {
return new Person("jiali", 21);
}
}
6、自定义TypeFilter
过滤规则
MyTypeFilter
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader:读取到的当前正在扫描的类的信息
* @param metadataReaderFactory:可以获取到其他任何信息的
* return true表示组件加入容器
*/
@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);
//如果类名包含er则加入容器
if (className.contains("er")) {
return true;
}
return false;
}
}
MainConfig
@Configuration
@ComponentScan(value = "com.laptoy", includeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
@Bean
public Person person() {
return new Person("jiali", 21);
}
}
3、@Scope
设置组件作用域
1、prototype:多实例的,IOC容器启动不会创建对象,每次获取才会调用方法创建对象
2、singleton:单实例的(默认),IOC容器启动会调用方法创建对象并放到ioc容器中,以后每次获取就是直接从容器(map.get())中拿,只会创建一次
不常用
3、request:同一个请求创建一个实例
4、session:同一个session创建一个实例
验证singleton模式IOC容器启动会调用方法创建对象并放到ioc容器中
@Scope("singleton")
@Bean
public Person person() {
System.out.println("给容器中注册Bean。。。");
return new Person("jiali", 22);
}
@Test
public void test02() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("==容器加载完成==");
//默认单实例
Object bean1 = context.getBean("person");
Object bean2 = context.getBean("person");
System.out.println(bean1 == bean2);
}
验证prototype(多实例的,IOC容器启动不会创建对象,每次获取才会调用方法创建对象)
@Scope("prototype")
@Bean
public Person person() {
System.out.println("给容器中注册Bean。。。");
return new Person("jiali", 22);
}
@Test
public void test02() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("==容器加载完成==");
//默认单实例
Object bean1 = context.getBean("person");
Object bean2 = context.getBean("person");
System.out.println(bean1 == bean2);
}
4、@Lazy
单实例bean,默认容器创建就创建对象,只会创建一次
懒加载,容器启动不创建对象,第一次使用(获取)Bean创建对象并初始化
@Lazy
@Bean
public Person person() {
System.out.println("给IOC添加Person。。。。");
return new Person("jiali", 22);
}
@Test
public void test02() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("==容器加载完成==");
//默认单实例
Object bean1 = context.getBean("person");
Object bean2 = context.getBean("person");
System.out.println(bean1 == bean2);
}
5、@Conditional
按照一定的条件进行判断,满足条件才给容器注册Bean
@Configuration
public class MainConfig3 {
@Conditional({WindowCondition.class})
@Bean("bill")
public Person person1() {
return new Person("Bill Gates", 62);
}
@Conditional({LinuxCondition.class})
@Bean("linus")
public Person person2() {
return new Person("linus", 48);
}
}
//判断是否为linux系统
public class LinuxCondition implements Condition {
/**
* @param context: 判断条件能使用的上下文(环境)
* @param metadata:注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//1、获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2、获取类加载器
ClassLoader beanClassLoader = context.getClassLoader();
//3、获取环境变量
Environment environment = context.getEnvironment();
//4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}
return false;
}
}
//判断是否为win系统
public class WindowCondition 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;
}
}
@Test
public void test03() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig3.class);
for (String name : context.getBeanNamesForType(Person.class)) {
System.out.println(name);
}
//获取环境变量
ConfigurableEnvironment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
System.out.println(property);
Map<String, Person> persons = context.getBeansOfType(Person.class);
System.out.println(persons);
}
6、@Import
6.1、@Import
快速给容器导入组件,id默认是组件的全类名
@Configuration
@Import({Color.class})
public class MainConfig4 {
}
private void printBeans(AnnotationConfigApplicationContext applicationContext) {
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Test
public void test04() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
printBeans(applicationContext);
}
6.2、ImportSelector
与@Import一起使用,该选择器能够读取标注@Import注解的类的其他注解信息
返回需要导入的组件的全类名数组
下面例子从MyImportSelector将我们的Pink,Red全类名数组返回给@Import实现组件导入
@Configuration
@Import({Color.class, MyImportSelector.class})
public class MainConfig4 {
}
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
/**
* @param importingClassMetadata:当前标注@Import注解的类的所有注解信息
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.laptoy.bean.Pink","com.laptoy.bean.Red"};
}
}
@Test
public void test04() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
printBeans(applicationContext);
}
6.3、ImportBeanDefinitionRegistrar
可以把需要的组件手工注册进容器
@Configuration
@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class MainConfig4 {
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata:当前类的注解信息
* @param registry:BeanDefinitionRegistry注册类: registerBeanDefinition()方法可以把需要的组件手工注册进容器
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//注册Bean并指定bean名
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Yellow.class);
registry.registerBeanDefinition("yellow", rootBeanDefinition);
}
}
@Test
public void test04() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4.class);
printBeans(applicationContext);
}
7、FactoryBean工厂注册
验证FactoryBean默认创建的对象是调用getObject创建的对象(单多实例有上面的基础可以自行验证)
public class ColorFactoryBean implements FactoryBean<Color> {
//返回一个Color对象,对象会添加到容器中
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean。。。getObject。。。");
return new Color();
}
//指定为Color类
@Override
public Class<?> getObjectType() {
return Color.class;
}
//true:单实例,在容器中保存一分
//false:多实例,每次获取都会创建一个新的bean
@Override
public boolean isSingleton() {
return true;
}
}
@Configuration
public class MainConfig5 {
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}
@Test
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig5.class);
System.out.println("==验证colorFactoryBean创建的对象是指定的对象==");
Object color = applicationContext.getBean("colorFactoryBean");
System.out.println(color.getClass());
System.out.println("==验证获取到ColorFactoryBean本身==");
Object colorFactoryBean = applicationContext.getBean("&colorFactoryBean");
System.out.println(colorFactoryBean.getClass());
}
要获取到工厂Bean本身,在id前加&
二、Bean生命周期
概述
创建—初始化----销毁
可以自定义bean的初始化和销毁方法
1、构造方法(对象创建)
- 单实例:在容器启动的时候创建
- 多实例:在每次获取的时候创建
2、初始化(init):对象创建构造完成,并赋值好,开始调用初始化方法
3、销毁(destroy):
- 单实例:容器关闭自动销毁
- 多实例:需要手动销毁
实现方式:
- 通过@Bean 指定init-method和destroy-method
- 实现InitializingBean定义初始化逻辑,实现DisposableBean定义销毁方法
- JSP250:
@PostConstruct:在bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestory:在容器销毁bean之前通知我们进行清理 - 实现BeanPostProcessor接口的后置拦截器放入容器中,可以拦截bean初始化,并可以在被拦截的Bean的初始化前后进行一些处理工作。
1、@Bean指定init-method和destroy-method
xml配置(旧)
<bean id="person" class="com.laptoy.bean.Person" init-method="" destroy-method="">
<property name="age" value="21"></property>
<property name="name" value="jiali"></property>
</bean>
注解配置
1、验证单实例
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destroy")
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 destroy(){
System.out.println("car...destroy..");
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
System.out.println("关闭容器");
applicationContext.close();
}
2、验证多实例
@Configuration
public class MainConfigOfLifeCycle {
@Scope("prototype")
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
Object car = applicationContext.getBean("car");
System.out.println("关闭容器");
applicationContext.close();
}
2、InitializingBean和DisposableBean
InitializingBean:定义初始化逻辑()
DisposableBean:定义销毁逻辑
@Component
public class Cat implements InitializingBean, DisposableBean {
public Cat(){
System.out.println("cat...constructor");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat...afterPropertiesSet(init)");
}
@Override
public void destroy() throws Exception {
System.out.println("cat...destroy");
}
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
System.out.println("关闭容器");
applicationContext.close();
}
3、JSR250
1、@PostConstruct
在bean创建完成并且属性赋值完成,来执行初始化方法
/**
* The PostConstruct annotation is used on a method that needs to be executed
* after dependency injection is done to perform any initialization. This
* method MUST be invoked before the class is put into service. This
* annotation MUST be supported on all classes that support dependency
* injection. The method annotated with PostConstruct MUST be invoked even
* if the class does not request any resources to be injected. Only one
* method can be annotated with this annotation. The method on which the
* PostConstruct annotation is applied MUST fulfill all of the following
*/
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
2、@PreDestory
在容器销毁bean之前通知我们进行清理
/**
* The PreDestroy annotation is used on methods as a callback notification to
* signal that the instance is in the process of being removed by the
* container. The method annotated with PreDestroy is typically used to
* release resources that it has been holding. This annotation MUST be
* supported by all container managed objects that support PostConstruct
* except the application client container in Java EE 5. The method on which
* the PreDestroy annotation is applied MUST fulfill all of the following
*/
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
测试
@Component
public class Dog {
public Dog(){
System.out.println("dog constructor");
}
@PostConstruct
public void init(){
System.out.println("dog...@PostConstruct(init)");
}
@PreDestroy
public void destroy(){
System.out.println("dog...@PreDestory(destroy)");
}
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
}
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
System.out.println("关闭容器");
applicationContext.close();
}
4、BeanPostProcessor(后处理器)
Spring底层对BeanPostProcess的使用
bean赋值,注入其他组件@Autowired,生命周期注解,异步@Async
bean的后置处理器:在bean的初始化前后进行一些处理工作
接口源码
public interface BeanPostProcessor {
//bean初始化之前进行处理
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化后进行处理
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
自定义BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
//返回要使用的bean实例,无论是原始的还是包装好的;如果为null,则不会调用后续的BeanPostProcessors
@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;
}
}
public class Car {
public Car(){
System.out.println("car...constructor");
}
public void init(){
System.out.println("car...init..");
}
public void destroy(){
System.out.println("car...destroy..");
}
}
@ComponentScan("com.laptoy.bean")
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destroy")
public Car car() {
return new Car();
}
}
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
System.out.println("容器创建完成");
System.out.println("关闭容器");
applicationContext.close();
}
5、容器创建原理基础
1、从Annotation注解开始
2、刷新IOC容器
3、完成Bean工厂的初始化
4、预实例化单例
5、获取bean
6、获取单实例
7、获取不到就自行创建
8、先执行populateBean为bean赋值后才初始化bean
9、关键就在这
执行调用初始化方法invokeInitMethods()
之前执行了 applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
之后执行了applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
遍历得到容器中所有BeanProcessor,依次执行postProcessAfterInitialization(),如果返回null,跳出for循环,不会执行后面的postProcessAfterInitialization()
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
总结:
执行initializeBean()之前执行populateBean()对bean赋值,之后就是
执行前置applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
执行调用初始化方法invokeInitMethods()
执行后置applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
三、属性赋值
1、@Value&@PropertySource
@Value:对属性进行赋值
@PropertySource:导入配置文件
application.properties
server.port=80
public class User {
@Value("1")
private int id;
@Value("jiali")
private String name;
@Value("${server.port}")
private String port;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", port='" + port + '\'' +
'}';
}
}
@PropertySource(value = {"classpath:/application.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public User user() {
return new User();
}
}
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.clas
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
Object person = applicationContext.getBean("user");
System.out.println(person);
}
四、自动装配
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
1、@Autowired&@Qualifier&@Primary[Spring规范的注解]
@Autowired
private BookDao bookDao;
1、默认优先按照类型去容器中找对应的组件
applicationContext.getBean(bookDao.class)
2、如果找到多个相同类型的组件,再将属性的id去容器中寻找
applicationContext.getBean("bookDao")
3、当容器有多个相同类型的bean时,可以使用 @Qualifier
指定需要装配的组件的id
@Qualifier("bookDao")
@Autowired
private BookDao bookDao;
4、自动装配默认一定要将属性赋值好,没有就会报错,可以使用@Autowired(required=false)
解决,需要装配的属性不存在则返回null
@Autowired(required = false)
private BookDao bookDao;
5、@Primary:让Spring进行自动装配的时候,默认使用该bean
也可以继续使用@Qualifier进行id指定
@Primary
@Bean
public BookDao bookDao(){
return new BookDao();
}
2、@Resource(JSR250)@Inject(JSR330)[java规范的注解]
1、@Resource:可以和@Autowired一样实现自动装配功能,默认是按照组件名称进行装配
没有支持@Primary和@Autowired(requird=false)
2、@Inject:需要导入javax.inject的包,和@Autowired功能一样,也没有@Autowired(requird=false)
3、@Autowired
1、标注在方法位置上:@Bean+方法参数,参数从容器中获取。默认不写@Autowired效果是一样的
2、标在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略
3、放在参数位置
4、Aware
自定义组件想要使用Spring底层的一些组件(ApplicationContext,BeanFactory。。。)
实现xxxAware接口:创建对象的时候,会调用规定的方法注入相关组件,把Spring底层的一些组件注入到自定义bean中
@Component
public class Red implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的IOC" + applicationContext);
this.applicationContext = applicationContext;
}
}
如下源码:依旧是调用BeanPostProcessor进行处理
5、@Profile
可以根据当前环境:动态地激活和切换一系列组件的功能
1、使用命令行动态参数:在虚拟机位置加载-Dspring.profiles.active=dev
2、使用代码激活
@Configuration
public class MainConfigOfProfile {
@Profile("test")
@Bean
public MyDataSource testSource() {
return new MyDataSource();
}
@Profile("dev")
@Bean
public MyDataSource devSource() {
return new MyDataSource();
}
//未标注@Profile的默认生效
@Bean
public MyDataSource source() {
return new MyDataSource();
}
}
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.getEnvironment().setActiveProfiles("dev");
applicationContext.register(MainConfigOfProfile.class);
applicationContext.refresh();
for (String s : applicationContext.getBeanNamesForType(MyDataSource.class)) {
System.out.println(s);
}
}
二、AOP
1、AOP注解功能测试
- 业务类和切面类@Aspect加入容器
- 切面类方法标志通知注解@Before。。。
- 配置类开启基于注解的模式@EnableAspectJAutoProxy
类型 | 运行时机 |
---|---|
前置通知(@Before) | 在目标方法运行前运行 |
后置通知(@After) | 在目标方法运行后运行(无论方法正常结束还是异常) |
返回通知(@AfterReturning) | 在目标方法正常返回后执行 |
异常通知(@AfterThrowing) | 在目标方法出现异常后执行 |
环绕通知(@Around) | 动态代理,手动推进目标方法运行(joinPoint.procced()) |
测试
1、导入aop模块
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
2、定义一个业务逻辑类(MathCalculator),在业务逻辑运行的时候将日志进行打印(方法之前,运行,结束,异常)
public class MathCalculator {
public int divisor(int i, int j) {
System.out.println("MathCalculator...divisor...");
return i / j;
}
}
3、定义一个日志切面类(LogAspects)动态感知业务逻辑类并@Aspect
- @PonitCut:抽取公共切入点表达式
- JoinPoint属性:可以获取方法的信息(一定要出现在方法参数列表的第一个位置,否则spring无法识别)
- @AfterReturing:returing属性可以用来获取方法的返回值
- @AfterThrowing:throwing属性可以用来获取方法的异常信息
切面类
@Aspect
public class LogAspects {
@Pointcut("execution(public int com.laptoy.aop.MathCalculator.*(..))")
public void pointCut() {
}
@Before("execution(public int com.laptoy.aop.MathCalculator.divisor(int,int))")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("" + joinPoint.getSignature().getName() + "运行。。。@Before。。。参数列表是:{" + Arrays.asList(args) + "}");
}
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println("" + joinPoint.getSignature().getName() + "结束。。。@After。。。");
}
//returning属性可以用来获取方法返回值
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("" + joinPoint.getSignature().getName() + "正常返回。。。@AfterReturning。。。运行结果是:{" + result + "}");
}
//throwing属性可以用来获取异常信息
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println("" + joinPoint.getSignature().getName() + "异常@AfterThrowing。。。异常信息{" + exception + "}");
}
}
4、将切面类和业务逻辑类加入容器并开启切面类注解配置@EnableAspectJAutoProxy
xml开启切面功能(旧)等同于@EnableAspectJAutoProxy
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
5、将获取类交给spring管理
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
//不要自己创建对象(这样spring无法接管对象导致aspect无法执行)
//MathCalculator mathCalculator = new MathCalculator();
//mathCalculator.divisor(4,2);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.divisor(4, 2);
//mathCalculator.divisor(4, 0);
}
mathCalculator.divisor(4, 0);
2、AOP原理
1、@EnableAspectJAutoProxy
调用@Import
给容器导入了AspectJAutoProxyRegistrar.class
2、利用registerBeanDefinitions()
自定义给容器导入组件
此处可以参考上文的一、组件注册 6、@import 3、ImportBeanDefinitionRegistrar
3、debug开始测试
进入该方法,发现是給容器注册了AnnotationAwareAspectJAutoProxyCreator
重点分析AnnotationAwareAspectJAutoProxyCreator
流程:
1、传入配置类,创建ioc容器
2、注册配置类,调用refresh()
刷新
3、registerBeanPostProcessors(beanFactory)
注册bean的后置处理器来方便拦截bean的创建
registerBeanPostProcessors(beanFactory)
方法流程:
3.1、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
3.2、给容器中加别的BeanPostProcessor
3.3、优先注册实现了PriorityOrdered
接口的BeanPostProcessor
再给容器注册实现了Ordered
接口的BeanPostProcessor
3.4、接下来注册没实现优先级接口的BeanPostProcessor
当前的AnnotationAwareAspectJAutoProxyCreator
父类实现了Ordered接口(所以是走图3.3的Next流程)
注册BeanPostProcessor
实际上就是创建BeanPostProcessor
对象,保存在容器中(容器本身是没有这个bean的,但还是会尝试获取,获取不到就创建):下图快速展示下获取及获取不到流程
获取不到就创建bean
4、创建bean
5、初始化bean(初始化bean之前调用了populateBean
给bean的属性赋值)
6、调用感知方法invokeAwareMethods
题外话这里不是该流程:仅仅作为加强bean初始化BeanPostProcessor
概念
调用初始化方法invokeInitMethods()
(之前和之后都会调用bean的后置处理器)
applyBeanPostProcessorsBeforeInitialization()
初始化之前调用后置处理器
applyBeanPostProcessorsAfterInitialization()
初始化之后调用后置处理器
这里以applyBeanPostProcessorsBeforeInitialization()
为例子,其实两个任意一个为例都可以:获取所有的BeanPostProcessor
回到流程
7、判断是否实现了BeanFactoryAware
接口
8、如果是,就来调用setBeanFactory方法
9、AnnotationAwareAspectJAutoProxyCreator
创建成功
调用initBeanFactory
快速执行下一步,最后
10、把BeanPostProcessor
注册到BeanFactory中