【SpringBoot_ANNOTATIONS】 总集篇
组件注册 01 @Configuration @Bean
Spring重要特性:IOC/DI (控制反转 / 依赖注入)
传统方式
使用beans.xml
- 创建bean类
package com.example.annotations.bean;public class Person {private String name;private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
- idea中创建beans.xml
new – xml configuration file – Spring config
<bean id="person" class="com.example.annotations.bean.Person"><property name="name" value="lxl"/><property name="age" value="21"/>
</bean>
- 测试
@Testvoid beanTest(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Person person = (Person) applicationContext.getBean("person");System.out.println(person);}
注解方式 @Configuration @Bean
编写MainConfig类:配置类等同于beans.xml
package com.example.annotations.config;import com.example.annotations.bean.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;// @Configuration : 告诉Spring这是个配置类 @Configuration public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")public Person getPerson(){return new Person("qd",21);} }
测试
@Testvoid beanConfigTest(){ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);Person person = applicationContext.getBean(Person.class);//获取对应类型组件在IOC容器中的名字 返回一个数组String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class); // System.out.println(Arrays.toString(beanNamesForType));for (String str:beanNamesForType){System.out.println("toString:" + str.toString());}}
Notes
- @Configuration:告诉Spring这是个配置类
- @Bean:给容器注入一个bean- 标注在类上:类型为bean类的类型,id默认为类的小写- 标注在方法上:类型为返回值的类型,id默认使用方法的id
组件注册 02 @ComponentScan 自动扫描组件 & 指定扫描规则
资源准备
package com.example.annotations.controller;import org.springframework.stereotype.Controller;@Controller
public class BookController {
}
package com.example.annotations.service;import org.springframework.stereotype.Service;@Service
public class BookService {
}
传统方式
beans.xml中:
<context:component-scan base-package="com.example.annotations.controller"></context:component-scan><bean id="person" class="com.example.annotations.bean.Person"><property name="name" value="lxl"/><property name="age" value="21"/></bean>
此后 只要在该包下标注了 @Controller @Service @Repository @Component 都会被加载进IOC容器
测试:
@Testvoid packageScanTest(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for (String str:beanDefinitionNames){System.out.println(str);}}
有person是因为在beans.xml手动注入的
注解方式 @ComponentScan()
使用的时候注释掉 beans.xml中的 包扫描属性
<!-- 这行 注释掉 -->
<!-- <context:component-scan base-package="com.example.annotations.controller"></context:component-scan>--> <bean id="person" class="com.example.annotations.bean.Person"><property name="name" value="lxl"/><property name="age" value="21"/></bean>
直接扫描
在MainConfig上加上注解 @Configuration(value = “com.example.annotations.service”)
// @Configuration : 告诉Spring这是个配置类 @Configuration @ComponentScan(value = "com.example.annotations") // 包扫描注解:只要在该包下标注了 @Controller @Service @Repository @Component 都会被加载进IOC容器 public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")public Person getPerson(){return new Person("qd",21);} }
测试
@Testvoid packageScanTest(){//xml // ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for (String str:beanDefinitionNames){System.out.println(str);}}
结果:
注入进容器的id默认是类名的小写(不严格是小写,实际为驼峰命名)
不玩钱为什么会有mainConfig?MainConfig.class上标注了@Configuration注解
@Configuration 源码
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component // 有这个注解,会被加载进容器 public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";boolean proxyBeanMethods() default true; }
过滤规则
@includeFilters
// @Configuration : 告诉Spring这是个配置类
@Configuration
@ComponentScan(value = "com.example.annotations",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = { Service.class,Controller.class})},useDefaultFilters = false
)
public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")public Person getPerson(){return new Person("qd",21);}
}
测试:
@excludeFilters
???
// @Configuration : 告诉Spring这是个配置类
@Configuration
@ComponentScan(value = "com.example.annotations",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, Service.class})},useDefaultFilters = false
)
public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")public Person getPerson(){return new Person("qd",21);}
}
测试:可看到过滤掉了controller和Service
组件注册 03 FilterType
FilterType.ANNOTATION 注解
常用
@ComponentScan(value = "com.example.annotations",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = { Service.class,Controller.class})},useDefaultFilters = false
)
FilterType.ASSIGNABLE_TYPE 按照给定的类型
@ComponentScan(value = "com.example.annotations",includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes ={MyTypeFilter.class, BookController.class})},useDefaultFilters = false
)
FilterType.ASPECTJ 不常用 使用ASPECTJ表达式
FilterType.REGEX 使用正则表达式
FilterType.CUSTOM 自定义规则
注意:bean不能重名 不然会报错
@ComponentScan(value = "com.example.annotations",includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes ={MyTypeFilter.class})},useDefaultFilters = false
)
需要实现TypeFilter接口 并重写match方法
public class MyTypeFilter implements TypeFilter {@Overridepublic 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" + className);return className.contains("er")?true:false;//true表示加载进容器 false表示不加载进容器}
}
组件注册 04 @Scope 设置组件作用域
参数详解
常用
prototype:多实例
singleton:单实例
不常用
request:同一次请求创建一个实例
session:同一个session创建一个实例
示例
单例模式(默认情况)
配置类
@Configuration
public class MainConfig2 {// 由于容器中已经有了一个名为"person"的bean, 所以如果再注入"person"的时候会报错 需要修改下注入的bean的名称// 不写scope的情况下 默认是singleton 相当于 @Scope("singleton")@Bean("person2") public Person person(){return new Person("qd2",22);}
}
测试
@Testvoid scopeTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);Object bean = applicationContext.getBean("person2");Object bean1 = applicationContext.getBean("person2");System.out.println("bean :"+bean.toString());System.out.println("bean1:"+bean1.toString());System.out.println(bean == bean1);}
多例模式
@Configuration
public class MainConfig2 {// 由于容器中已经有了一个名为"person"的bean, 所以如果再注入"person"的时候会报错 需要修改下注入的bean的名称@Bean("person2")@Scope("prototype")public Person person(){return new Person("qd2",22);}
}
等同于
<bean id="person" class="com.example.annotations.bean.Person" scope="prototype"><property name="name" value="lxl"/><property name="age" value="21"/></bean>
测试结果:
Notes
单实例
单例模式下(singleton)
- 在容器加载的时候加载bean
- IOC容器启动会调用方法创建对象放进IOC容器中
- 以后每次获取直接从容器(map.get())中获取
多实例
多例模式下(prototype)
- IOC容器启动并不会调用方法创建对象放在容器中
- 每次获取的时候才会调用方法创建对象
组件注册 05 @Lazy 懒加载
单实例bean:默认在容器启动的时候创建对象
多实例bean:容器启动的时候不创建对象 第一次使用(获取)Bean的时候创建对象 并初始化
测试代码
@Testvoid scopeTest2(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);System.out.println("容器创建完毕");System.out.println("准备获取bean...");Object person = applicationContext.getBean("person");System.out.println("创建之后获取的 "+person.toString());}
单实例 测试
默认情况
@Configuration
public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")public Person getPerson(){System.out.println("准备创建person...");return new Person("qd",21);}
}
运行结果
加上懒加载 @Lazy
@Configuration
public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")@Lazypublic Person getPerson(){System.out.println("准备创建person...");return new Person("qd",21);}
}
多实例 测试
@Configuration
public class MainConfig {//给容器注入一个bean,类型为返回值的类型,id默认使用方法的id@Bean("person")@Scope("prototype")public Person getPerson(){System.out.println("准备创建person...");return new Person("qd",21);}
}
组件注册 06 @Conditional 按照条件注册bean
SpringBoot底层大量使用的注解,表示按照一定的条件进行判断,若满足条件则给容器中注册bean
@Conditional可以标注在类上 也可以标注在方法上
@Testvoid conditionTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);System.out.println("容器创建完毕");System.out.println("准备获取bean...");Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);for (String person:beansOfType.keySet()){System.out.println(beansOfType.get(person));}}
MainConfig2.java
@Configuration
public class MainConfig2 {// 由于容器中已经有了一个名为"person"的bean, 所以如果再注入"person"的时候会报错 需要修改下注入的bean的名称// @Scope("prototype")@Bean("person2")public Person person(){System.out.println("person2创建中...");return new Person("qd2",22);}@Bean("bill")public Person person1(){System.out.println("bill创建中...");return new Person("bill",62);}@Bean("linus")public Person person2(){System.out.println("linus创建中...");return new Person("linus",48);}
}
加上条件判断
LinuxCondition.java
package com.example.annotations.condition;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;public class LinuxCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取IOC使用的beanFactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();// 获取类加载器ClassLoader classLoader = context.getClassLoader();// 获取bean定义的注册类BeanDefinitionRegistry registry = context.getRegistry();// 获取当前环境信息Environment environment = context.getEnvironment();// 动态获取环境变量的值String property = environment.getProperty("os.name");if (property.contains("Linux")){System.out.println("the running environment is 'Linux'");return true;}return false;}
}
注意
- 实现
Condition
接口的时候注意导的包- 需要实现的方法:
matches
参数解释:
- ConditionContext:判断条件能使用的上下文环境
- AnnotationTypeMetadata:注释信息
BeanDefinitionRegistry registry = context.getRegistry();
所有bean定义都在这个类,也可以用其来注册/移除/查询/一个bean定义
WindowsCondition.class
package com.example.annotations.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;public class WindowsCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 获取运行时环境Environment environment = context.getEnvironment();// 动态获取环境变量的值String property = environment.getProperty("os.name");// 匹配环境if (property.contains("Windows")){System.out.println("the running environment is 'Windows'");return true;}return false;}
}
@Configuration
public class MainConfig2 {// 由于容器中已经有了一个名为"person"的bean, 所以如果再注入"person"的时候会报错 需要修改下注入的bean的名称@Bean("person2")public Person person(){System.out.println("person2创建中...");return new Person("qd2",22);}@Conditional(WindowsCondition.class)@Bean("bill")public Person person1(){System.out.println("bill创建中...");return new Person("bill",62);}@Conditional(LinuxCondition.class)@Bean("linus")public Person person2(){System.out.println("linus创建中...");return new Person("linus",48);}
}
运行结果;
组件注册 07 @Import 给容器快速导入一个组件
给容器注册组件:
- 包扫描 + 组件标注注解(@Controller @Service @Repository @Component),局限于自己写的
- @Bean(可导入第三方包里的组件)
- @Import(快速给容器中导入组件)
- @Import([要导入容器中的组件]):容器中就会自动注册这个组件,id默认为全类名
@Import(Color.class)
@Import({Color.class,Red.class})
- 实现ImportSelector接口:返回需要导入的组件的全类名数组
- 实现ImportBeanDefinitionRegistrar接口:手动注册bean到容器中
- 使用Spring提供的FactoryBean工厂
配置类 MainConfig3.class
测试
@Testvoid importTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);System.out.println("容器创建完毕");System.out.println("准备获取bean...");String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();for (String name:beanDefinitionNames){System.out.println(name);}}
ImportSelector 测试
Color.java
package com.example.annotations.bean;public class Color {
}
Red.java
package com.example.annotations.bean;public class Red {
}
MyImportSelector.java
package com.example.annotations.importselector;import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;/*** 自定义逻辑返回需要导入的组件*/
public class MyImportSelector implements ImportSelector {// 返回值:导入到容器中的组件 (全类名)// AnnotationMetadata:当前标注@Import注解的类的所有注解信息@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// 不能返回 null 可以是空数组return new String[]{"com.example.annotations.bean.Color","com.example.annotations.bean.Red"};}
}
运行结果:
实现ImportBeanDefinitionRegistrar接口
MyImportBeanDefinitionRegistrar.class
package com.example.annotations.importselector;import com.example.annotations.bean.RainBow;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {/**** @param importingClassMetadata 当前类的注解信息* @param registry BeanDefinition的注册类 负责将bean添加进容器*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 指定bean定义信息(Bean的类型 Bean的作用域 ...)RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(RainBow.class);// 注册:指定bean名if (!registry.containsBeanDefinition("rainbow")){System.out.println(" start create 'rainbow' ...");registry.registerBeanDefinition("rainbow",rootBeanDefinition);}else {System.out.println("'rainbow' already exists ");}}
}
运行结果
使用FatoryBean
普通bean
将bean导入到容器后,容器会调用其无参构造器,默认创建一个对象注册在容器中
FactoryBean
- 是一个接口
- 整合第三方框架的时候用的多
- 默认获取的是工厂bean调用getObject创建的对象
- 要获取工厂bean本身,需要给id前加一个
&
需要实现方法
getObject()
getObjectType()
isSingleton() :默认是true 单实例
实例测试
ColorFactoryBean.java
package com.example.annotations.bean;import org.springframework.beans.factory.FactoryBean;/*** 创建一个Spring定义的FactoryBean*/
public class ColorFactoryBean implements FactoryBean<Color> {/*** 返回分Color对象会被添加到容器中* @return Color* @throws Exception*/@Overridepublic Color getObject() throws Exception {return new Color();}/**** @return*/@Overridepublic Class<?> getObjectType() {return Color.class;}/*** true: 单实例 在容器中保存一份* false:多实例 每获取一次就会创建一个新bean* @return*/@Overridepublic boolean isSingleton() {return true;}
}
MainConfig2.java
@Configuration
public class MainConfig2 {@Beanpublic ColorFactoryBean colorFactoryBean(){return new ColorFactoryBean();}
}
测试
@Testvoid factoryBeanTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);System.out.println("容器创建完毕");System.out.println("准备获取bean...");Object bean1 = applicationContext.getBean("colorFactoryBean");//Color.classObject bean2 = applicationContext.getBean("colorFactoryBean");//Color.classSystem.out.println("bean1 == bean2? => " + (bean1 == bean2));}
测试结果
现将ColorFactoryBean.java中的isSingleton改为false,再次运行
查看获取的类型
@Testvoid factoryBeanTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);System.out.println("容器创建完毕");System.out.println("准备获取bean...");Object bean1 = applicationContext.getBean("colorFactoryBean");//Color.class 获取的是工厂bean调用Object bean2 = applicationContext.getBean("&colorFactoryBean");//ColorFactoryBean.classSystem.out.println("bean1 == bean2? => " + (bean1 == bean2));System.out.println("bean1 " + bean1.getClass());System.out.println("bean2 " + bean2.getClass());}
生命周期 01 @Bean指定初始化和销毁方法
bean生命周期:bean创建 – 初始化 – 销毁
容器管理bean的生命周期
可自定义初始化和销毁方法:容器在bean进行到当前生命周期的时候来调用自定义的初始化和销毁方法
package com.example.annotations.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 ...");}}
指定初始化和销毁方法(传统方式)
beans.xml
<bean id="bean_xml_car" class="com.example.annotations.bean.Car" init-method="init" destroy-method="destroy"/>
使用注解
scope采用默认
@Configurationpublic class MainConfigOfLifeCycle {@Bean(initMethod = "init" ,destroyMethod = "destroy")public Car car(){return new Car();}}
测试
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");}
测试结果
现在test中容器关闭
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//关闭容器applicationContext.close();}
scope改为prototype
MainConfigOfLifeCycle
@Configuration
public class MainConfigOfLifeCycle {@Bean(value = "car4LifeCycle",initMethod = "init" ,destroyMethod = "destroy")@Scope("prototype")public Car car(){return new Car();}
}
测试
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//关闭容器applicationContext.close();}
测试结果
手动获取bean
test
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Object car4LifeCycle = applicationContext.getBean("car4LifeCycle");System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果
可看出,prototype下 不会调用销语句
Notes
构造(对象创建)
- 单实例:容器启动的时候创建对象
- 多实例:每次获取的时候创建对象
初始化
对象创建完成并赋值完成 调用初始化方法
销毁
- 单实例:容器关闭的时候
- 多实例:容器不会管理这个bean;销毁方法不会由容器调用
单实例
- 容器启动的时候创建对象
- 会在创建容器完毕前 获取bean并完成初始化方法
- 容器关闭的时候会调用bean的销毁方法
多实例
- 容器创建的时候并不会创建或者调用初始化
- 每次获取的时候创建对象 调用初始化
- 销毁:容器不会管理这个bean,销毁方法不会由容器来调用
生命周期 02 实现InitializingBean, DisposableBean接口
Cat.java
package com.example.annotations.bean;import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;/*** 生命周期测试*/
public class Cat implements InitializingBean, DisposableBean {/*** 构造*/public Cat() {System.out.println("cat constructor ...");}/*** 做初始化方法* 初始化方法不可以有参数 但可以抛异常*/public void init() {System.out.println("cat init ...");}/*** 初始化设置完属性后* 来源于 InitializingBean* @throws Exception*/@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("cat afterPropertiesSet ...");}/*** 销毁的时候调用* 来源于 DisposableBean* @throws Exception*/@Overridepublic void destroy() throws Exception {System.out.println("cat destroy ...");}public void destroyMethod() {System.out.println("cat destroyMethod ...");}
}
MainConfigOfLifeCycle.class中添加以下方法
@Bean(value = "cat4LifeCycle",initMethod = "init",destroyMethod = "destroyMethod")public Cat cat(){return new Cat();}
测试
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Object car4LifeCycle = applicationContext.getBean("cat4LifeCycle");System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果:
生命周期 03 @PostConstruct & @PreDestroy
使用JSR250
@PostConstruct:在bean创建完成并且属性赋值完成后执行此方法
@PreDestroy:在容器销毁bean之前通知进行清理工作
Dog.java
package com.example.annotations.bean;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** 测试* @PostConstruct* @PreDestroy*/
public class Dog {public Dog() {System.out.println("dog construct ...");}/*** 对象创建并赋值之后调用*/@PostConstructpublic void init(){System.out.println("dog @PostConstruct ...");}/*** 容器移除对象前调用*/@PreDestroypublic void destroy(){System.out.println("dog @PreDestroy ...");}}
配置新加
@Bean(value = "dog4LifeCycle")public Dog dog(){return new Dog();}
测试
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
结果
现添加之前的生命周期方法
package com.example.annotations.bean;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** 测试* @PostConstruct* @PreDestroy*/
public class Dog {public Dog() {System.out.println("dog construct ...");}/*** 做初始化方法* 初始化方法不可以有参数 但可以抛异常*/public void init() {System.out.println("dog init ...");}/*** 对象创建并赋值之后调用*/@PostConstructpublic void postConstruct(){System.out.println("dog @PostConstruct ...");}/*** 容器移除对象前调用*/@PreDestroypublic void preDestroy(){System.out.println("dog @PreDestroy ...");}/*** 做销毁方法* 销毁方法不可以有参数 但可以抛异常*/public void destroyMethod() {System.out.println("dog destroyMethod ...");}}
配置类:
@Bean(value = "dog4LifeCycle",initMethod = "init",destroyMethod = "destroyMethod")public Dog dog(){return new Dog();}
测试结果
扩展 JSR250
原文连接
简介
JSR 250(Java Specification Requests
),Java注解规范,定义了一系列基于Java EE和Java SE通用注解。它避免了不同框架或组件间重复(或冗余)的注解。JSR 250正式发布于2006年5月11日。随着申明式注解配置被越来越多地应用在Java框架(比如Spring),JSR 250可能在未来会持续增长,所以在使用诸如Spring之类的框架时,尽量使用JSR 250中定义的注解,避免和特定框架紧耦合。注解列表
注解 作用 @Generated 标记该资源是自动生成的 @Resource 定义了对某个资源的引用 @Resources 容器针对多资源的注解 @PostConstruct 标记在方法上用于依赖注入的初始化动作 @PreDestroy 标记在方法上用于该对象实例从容器销毁时的前置操作 @Priority 定义了该类执行的优先级 如拦截器的优先级 @RunAs 定义了应用在Java EE容器运行时的角色 @RolesAllowed 定义了什么安全角色被允许执行方法 @PermitAll 标记在类或方法上允许所有角色执行(所有)方法 @DenyAll 标记在方法上不允许所有角色调用 @DeclareRoles 用于定义系统的安全角色 @DataSourceDefinition 定义一个数据源,并通过JNDI注册到容器 @ManagedBean 定义一个被容器管理的对象
生命周期 04 BeanPostProcessor 后置处理器
BeanPostProcessor是一个接口:bean的后置处理器
在bean初始化前后进行一些处理工作
postProcessBeforeInitialization:初始化之前调用
postProcessAfterInitialization:初始化之后调用
自定义后置处理器
package com.example.annotations.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;/*** 测试 BeanPostProcessor(后置处理器)* 初始化前后进行处理工作*/
public class MyBeanPostProcessor implements BeanPostProcessor {/*** 初始化之前调用* @param bean* @param beanName* @return* @throws BeansException*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessBeforeInitialization === " + beanName +" => " + bean );return bean;}/*** 初始化之后调用* @param bean* @param beanName* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("postProcessAfterInitialization === " + beanName +" => " + bean );return bean;}
}
配置类
@Import(MyBeanPostProcessor.class) :
- 将MyBeanPostProcessor纳入到容器中;
- 这样该容器中的所有bean在初始化前后都会执行该后置处理器中的逻辑,即使未定义初始化方法这些逻辑也会执行的
@Configuration
@Import(MyBeanPostProcessor.class) //将MyBeanPostProcessor纳入到容器中;这样该容器中的所有bean在初始化前后都会执行该后置处理器中的逻辑,即使未定义初始化方法这些逻辑也会执行的
public class MainConfigOfLifeCycle {@Bean(value = "dog4LifeCycle",initMethod = "init",destroyMethod = "destroyMethod")public Dog dog(){return new Dog();}
}
测试
@Testvoid lifeCycleTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);System.out.println("容器创建完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果
属性赋值 01 @Value赋值
传统方式
public class Person {private String name;private Integer age;private String nickName;public Person() {}public Person(String name, Integer age, String nickName) {this.name = name;this.age = age;this.nickName = nickName;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", nickName='" + nickName + '\'' +'}';}
}
beans.xml
<context:property-placeholder location="classpath:person.properties" /><bean id="person4PropertyValues" class="com.example.annotations.bean.Person"><property name="age" value="21"/><property name="name" value="qd"/><property name="nickName" value="${person.nickName}"/></bean>
配置文件
person.properties
person.nickName=Aimer
测试
@Testvoid xmlPropertyValuesTest(){//传统方式ClassPathXmlApplicationContext xmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Object person4PropertyValues = xmlApplicationContext.getBean("person4PropertyValues");System.out.println(person4PropertyValues.toString());System.out.println("获取完毕");//关闭容器xmlApplicationContext.close();System.out.println("容器已关闭");}
运行结果:
注解方式
- 基本数值
- SPEL表达式 : #{22-1} => 21
- ${} :取出配置文件的值 [在运行环境变量中的值]
Person.java
public class Person {@Value("qd")private String name;@Value("#{22-1}")private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
配置类
@Configuration
public class MainConfig4PropertyValues {@Bean("person4PropertyValues")public Person person(){return new Person();}
}
测试
@Testvoid propertyValuesTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4PropertyValues.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Object person4PropertyValues = applicationContext.getBean("person4PropertyValues");System.out.println(person4PropertyValues.toString());System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果
注意 无论是beans.xml 还是配置类 都要保证bean的名字不重复
属性赋值 02 @PropertySource赋值
使用@PropertySource读取外部配置文件中的 k-v 保存到运行的环境变量中
加载完外部的配置文件以后 使用${}
取出配置文件中的值
配置类
package com.example.annotations.config;import com.example.annotations.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfig4PropertyValues {@Bean("person4PropertyValues")public Person person(){return new Person();}
}
Person.java
package com.example.annotations.bean;import org.springframework.beans.factory.annotation.Value;public class Person {@Value("qd")private String name;@Value("21")private Integer age;@Value("${person.nickName}")private String nickName;public Person(String name, Integer age, String nickName) {this.name = name;this.age = age;this.nickName = nickName;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", nickName='" + nickName + '\'' +'}';}
}
配置文件
person.properties
person.nickName=Aimer
测试
@Testvoid propertyValuesTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4PropertyValues.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Object person4PropertyValues = applicationContext.getBean("person4PropertyValues");System.out.println(person4PropertyValues.toString());System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果
扩展:
@Testvoid propertyValuesTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4PropertyValues.class);System.out.println("容器创建完毕");// //获取bean
// System.out.println("准备获取bean");
// Object person4PropertyValues = applicationContext.getBean("person4PropertyValues");
// System.out.println(person4PropertyValues.toString());
// System.out.println("获取完毕");//运行环境测试输出System.out.println("运行环境测试输出");ConfigurableEnvironment environment = applicationContext.getEnvironment();String property = environment.getProperty("person.nickName");System.out.println("person.nickName" + " => " + property);//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果
- 可以使用
@ProPertySources注解
指定多个PropertySource
自动装配 01 @Autowired & @Qualifier & @Primary
自动装配:Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
常用@Autowired
- 默认优先按照类型去容器中找对应的组件
- 若ioc中有多个同类型的bean,再将属性名称作组件id去容器中查找(会报错 )
也可以使用
@Qualifier("[beanName]")
来指定注入bean,这种情况下可以存在相同类型的多个bean‘如果没有bean,会报错 自动装配默认一定要将属性赋值好,否则会报错
可以修改这种默认设置:
@Autowired(required=false)
多数据源的时候用的多@Primary:让Spring进行自动装配的时候默认使用首选的bean,也可以继续使用
@Qulifier
指定需要装配的bean的名字优先级:@Qulifier > @Primary
@Primary测试
常用@Autowired
BookDao.java
@Repository
public class BookDao {private Integer lable = 1;public Integer getLable() {return lable;}public void setLable(Integer lable) {this.lable = lable;}@Overridepublic String toString() {return "BookDao{" +"lable=" + lable +'}';}
}
BookService.java
@Service
public class BookService {@Autowiredprivate BookDao booKDao;@Overridepublic String toString() {return "BookService{" +"booKDao=" + booKDao +'}';}
}
BookController.java
@Controller
public class BookController {@Autowiredprivate BookService bookService;
}
配置类
MainConfig4Autowire.java
package com.example.annotations.config;import com.example.annotations.dao.BookDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
@ComponentScan(value = {"com.example.annotations.service","com.example.annotations.dao","com.example.annotations.controller"})
public class MainConfig4Autowire {@Bean("bookDao2")public BookDao bookDao(){BookDao bookDao = new BookDao();bookDao.setLable(2);return bookDao;}
}
测试:
@Testvoid autowiredTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4Autowire.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");BookService bookService = applicationContext.getBean(BookService.class);System.out.println(bookService.toString());System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果:
配置类不加 @Primary
的时候,启动测试会报错,因为有两个BookDao的bean;配置类如果不指定注入的话,运行结果如下:
@Qualifier测试
配置类:
@Configuration
@ComponentScan(value = {"com.example.annotations.service","com.example.annotations.dao","com.example.annotations.controller"})
public class MainConfig4Autowire {// @Primary@Bean("bookDao2")public BookDao bookDao(){BookDao bookDao = new BookDao();bookDao.setLable(2);return bookDao;}
}
BookService.java
@Service
public class BookService {@Autowired@Qualifier("bookDao2")private BookDao booKDao;@Overridepublic String toString() {return "BookService{" +"booKDao=" + booKDao +'}';}
}
运行结果:
自动装配 02 @Resource & @Inject
Spring还支持使用@Resource 属于JSR250
和 @Inject 属于JSR330
@Resource & @Inject
属于java规范的注解
@Autowired
属于Spring的注解
@Resource
- 可以和@Autowired一样实现自动装配功能,默认是按照组件名称装配的
- 没有能支持@Primary 或者 @Autowired(required = false) 或者@Qulifier
@Service
public class BookService {@Resourceprivate BookDao bookDao;@Overridepublic String toString() {return "BookService{" +"booKDao=" + bookDao +'}';}
}
运行结果
@Inject
- 需要在maven库中找到 javax.inject 包中的central依赖
- 和**@Autowired**功能相似,但没有required属性
没有实现代码
自动装配 03 方法 构造器位置的自动装配
@Autowired的位置:构造器 方法 参数 属性 都是从容器中获取参数的值
- 方法上:Spring容器创建当前对象就会调用此方法 完成赋值
- 方法使用的参数 自定义类型就会从ioc容器中获取
- 构造器上: 构造器要用的组件都是从容器中获取
- 若bean中只有一个有参构造方法,
@Autowired
注解可以省掉,默认会调用这个参数位置的组件还是会从ioc容器获取
- 若bean中只有一个有参构造方法,
- 参数上:@Bean + 方法参数:默认不写@Autowired ,也能自动装配 也是从ioc容器中获取
Boss.java
@Component
public class Boss {private Car car;public Car getCar() {return car;}@Autowiredpublic void setCar(Car car) {this.car = car;}@Overridepublic String toString() {return "Boss{" +"car=" + car +'}';}
}
Car.java
/*** 生命周期测试* 自动装配测试*/
@Component
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 ...");}
}
配置类
@Configuration
@ComponentScan(value = {"com.example.annotations.service","com.example.annotations.dao","com.example.annotations.controller","com.example.annotations.bean"})
public class MainConfig4Autowire {}
测试:
@Testvoid autowiredTest(){//配置类测试AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4Autowire.class);System.out.println("容器创建完毕");//获取beanSystem.out.println("准备获取bean");Boss boss = applicationContext.getBean(Boss.class);Car car = applicationContext.getBean(Car.class);System.out.println(boss.toString());System.out.println(car.toString());System.out.println("获取完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
@Bean + 方法参数
@Component
public class Boss {private Car car;public Boss(Car car) {System.out.println("构造方法注入");this.car = car;}public Car getCar() {return car;}// @Autowiredpublic void setCar(Car car) {System.out.println("setter注入");this.car = car;}@Overridepublic String toString() {return "Boss{" +"car=" + car +'}';}
}
运行测试之后可看到有相应输出
自动装配 04 Aware 注入Spring底层组件 & 原理
总接口:
Aware
- 自定义组件想要使用Spring容器底层的一些组件(ApplicationContext BeanFactory 等等)
- 需要让自定义组件实现xxAware接口:在创建对象的时候会调用接口规定的方法注入相关组件 总体参照
Aware
接口 ,把Spring底层一些组件注入到自定义的bean中 - xxxAware功能:使用xxProcessor 如ApplicationContextAware ==》 ApplicationContextAwareProcessor,利用后置处理器判断实现了哪个接口,就将Bean转成相应的接口类型,再选择注入相应类型的applicationContext
/*** 生命周期 感知接口测试*/
@Component//注入后自动调用实现的感知接口方法
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {private ApplicationContext applicationContext;@Overridepublic void setBeanName(String name) {System.out.println("调用了 setBeanName");System.out.println("当前bean的名字 " + name);}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;System.out.println("调用了 setApplicationContext");}/*** 解析占位符* @param resolver*/@Overridepublic void setEmbeddedValueResolver(StringValueResolver resolver) {System.out.println("调用了 setEmbeddedValueResolver");String resolveString = resolver.resolveStringValue(" hello ${os.name} ; age #{22-1}");System.out.println("解析的字符串 " + resolveString);}
}
配置类
@Configuration
@ComponentScan(value = {"com.example.annotations.service","com.example.annotations.dao","com.example.annotations.controller","com.example.annotations.bean"})
public class MainConfig4Autowire {}
测试
@Testvoid autowiredTest(){//配置类测试System.out.println("准备创建容器");AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4Autowire.class);System.out.println("容器创建完毕");//关闭容器applicationContext.close();System.out.println("容器已关闭");}
测试结果:
自动装配 05 @Profile环境搭建
和condition
相似
@Profile:Spring为我们提供的可以根据当前环境动态的激活和切换一系列组件的功能 也可以根据环境切换数据源
dev
test
prov
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定的时候任何环境下都能注册这个组件
- 加了环境标识的bean 只有这个环境被激活的时候才能注册到容器中。
- 写在配置类上,只有是指定的环境的时候,整个配置类中的所有配置才能开始生效
- 没有标注环境标识的bean,任何环境下都是加载的。默认是
default
修改运行环境的几种方式
- 使用命令行动态参数:在虚拟机参数位置加载
-Dspring.profiles.active=[运行环境]
:-Dspring.profiles.active=test
- 手动代码设置
- 获取ApplicationContext :注意要使用无参方式获取,否则刷新之后还是没有注入的状态(具体可查看源码分析)
- 设置需要激活的环境
- 注册主配置类
- 启动刷新容器
代码测试
配置类
/*** 用于测试数据源和运行环境* 使用了多种方式设置参数*/
@PropertySource("classpath:/dbConfig.properties")
@Configuration
public class MainConfig4Profile implements EmbeddedValueResolverAware {public MainConfig4Profile() {}@Value("${db.user}")private String user;// 用于EmbeddedValueResolverAware接口测试private String driverClass;@Profile("prov")@Bean("provDataSouce")public DataSource dataSource4Prov() throws PropertyVetoException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser("root");dataSource.setPassword("");dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/sb_anno_prov?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");// 加载驱动的时候会抛异常dataSource.setDriverClass("com.mysql.jdbc.Driver");return dataSource;}@Profile("dev")@Bean("devDataSouce")public DataSource dataSource4Dev() throws PropertyVetoException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser("${db.user}");dataSource.setPassword("${db.password}");dataSource.setJdbcUrl("${db.user}");dataSource.setDriverClass("${db.driverClass}");return dataSource;}@Profile("test")@Bean("testDataSouce")public DataSource dataSource4Test(@Value("${db.password}") String pwd) throws PropertyVetoException {ComboPooledDataSource dataSource = new ComboPooledDataSource();dataSource.setUser(user);dataSource.setPassword(pwd);dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/sb_anno_test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");dataSource.setDriverClass(driverClass);return dataSource;}@Overridepublic void setEmbeddedValueResolver(StringValueResolver resolver) {this.driverClass = resolver.resolveStringValue("${db.driverClass}");System.out.println("解析后的driverClass " + driverClass);}
}
dbConfig.properties
db.user=root
db.password=
db.driverClass=com.mysql.jdbc.Driver
pom.xml
<dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><!-- 注意版本问题,本地安装的是5.几的版本,而springboot默认导入的是最新的,需要手动指定版本号--><version>5.1.46</version><scope>runtime</scope></dependency>
测试
@Testvoid profileTest(){//配置类测试System.out.println("准备创建容器");// 1. 获取ApplicationContext 注意要使用无参构造器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();System.out.println("容器创建完毕");// 2. 设置需要激活的环境applicationContext.getEnvironment().setActiveProfiles("test","dev");// 3. 注册配置类applicationContext.register(MainConfig4Profile.class);// 4. 启动刷新容器applicationContext.refresh();System.out.println("容器刷新完毕");// 获取beanString[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);for (String name : beanNamesForType) {System.out.println("当前环境下注入的bean" + name);}// 关闭容器applicationContext.close();System.out.println("容器已关闭");}
运行结果:
AnnotationConfigApplicationContext.java部分源码(无参构造):
public AnnotationConfigApplicationContext() {StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");this.reader = new AnnotatedBeanDefinitionReader(this);createAnnotatedBeanDefReader.end();this.scanner = new ClassPathBeanDefinitionScanner(this);}
AnnotationConfigApplicationContext.java部分源码(有参构造):
/*** 有参方式调用的时候,最后的refresh相当于没有设置*/public AnnotationConfigApplicationContext(Class<?>... componentClasses) {this();register(componentClasses);refresh();}
AOP 01 AOP功能测试
AOP:动态代理
指在程序运行期间动态的将某段代码切入到指定位置进行运行的编程方式
步骤
- 导入AOP模块
- 定义业务逻辑类:在业务逻辑运行的时候打印日志(方法前后 异常 …)
- 定义日志切面类:切面里的方法需动态感知目标方法运行到哪里然后执行
- 给切面类的目标方法标注何时何地运行(通知注解)
- 将切面类和业务逻辑类(目标方法所在类)都加到容器中
- 必须告诉Spring哪个是切面类(给切面类加上
@Aspect
注解) - 给配置类中的@EnableAspectJAutoProxy开启基于注解的AOP模式
Spring中很多的@EnableXXX表示用来开启某一功能
通知注解
作用 | 注解 | 备注 |
---|---|---|
前置通知 | @Before | |
后置通知 | @After | 无论正常结束还是异常结束都会调用 |
返回通知 | @AfterReturning | 正常返回后运行 |
异常通知 | @AfterThrowing | |
环绕通知 | @Around |
动态代理 手动推进目标方法运行joinPoint.proceed() 相当于一个最底层的通知
|
代码测试
业务逻辑类
MathCaculator.java
/*** @Classname MathCaculator* @Description AOP测试_业务逻辑类*/public class MathCaculator {public int div(int i,int j){System.out.println("MathCaculator 运行 div() ...");return i/j;}
}
切面类
/*** @Classname LogAspects* @Description 切面类* @Date 2021/8/5 10:34* @Created by LXLDEMO*/
@Aspect
public class LogAspects {/*** 抽取公共的切入点表达式* 指向目标方法* 1. 本类引用* 2. 其他的切面引用*/@Pointcut("execution(public int com.example.annotations.bean.MathCaculator.div(int ,int ))")public void pointCut(){}@Before("pointCut()")public void logStart(JoinPoint joinPoint){Object[] args = joinPoint.getArgs();System.out.println("运行@Before");System.out.println("方法名字 " + joinPoint.getSignature().getName());System.out.println("方法参数 " + Arrays.toString(args));}/*** 注解中参数的这种写法属于其他切面的调用方式* 全包名* @param joinPoint*/@After("com.example.annotations.aop.LogAspects.pointCut()")public void LogEnd(JoinPoint joinPoint){System.out.println("运行@After " + joinPoint.getSignature().getName());System.out.println(joinPoint.getSignature().getName() + "运行结束");}/*** 当有多个参数的时候 JoinPoint必须作为第一个参数* @param joinPoint* @param result*/@AfterReturning(value = "pointCut()",returning = "result")public void logReturning(JoinPoint joinPoint,Object result){System.out.println("运行@AfterReturning");System.out.println(joinPoint.getSignature().getName()+"正常返回" + result);}/*** 当有多个参数的时候 JoinPoint必须作为第一个参数* @param joinPoint* @param exception*/@AfterThrowing(value = "pointCut()",throwing = "exception")public void logException(JoinPoint joinPoint,Exception exception){System.out.println("运行@AfterThrowing");System.out.println("" + joinPoint.getSignature().getName() +" === 异常信息:"+exception);}
}
配置类
/*** @Classname MainConfig4AOP* @Description 配置类* @Date 2021/8/5 10:31* @Created by LXLDEMO*/
@EnableAspectJAutoProxy
@Configuration
public class MainConfig4AOP {@Beanpublic MathCaculator caculator(){System.out.println("准备注入 MathCaculator ...");return new MathCaculator();}@Beanpublic LogAspects logAspects(){System.out.println("准备注入 LogAspects ...");return new LogAspects();}
}
测试
@Testvoid Aoptest(){//配置类测试System.out.println("准备创建容器");// 注意要使用无参构造器AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig4AOP.class);System.out.println("容器创建完毕");// 获取beanMathCaculator mathCaculator = applicationContext.getBean(MathCaculator.class);mathCaculator.div(1,1);// 关闭容器applicationContext.close();System.out.println("容器已关闭");}
测试结果
将参数换成(1,0)
to be continued …
【SpringBoot_ANNOTATIONS】 总集篇相关推荐
- CS231n官方笔记授权翻译总集篇发布
CS231n简介 CS231n的全称是CS231n: Convolutional Neural Networks for Visual Recognition,即面向视觉识别的卷积神经网络.该课程是斯 ...
- 机器学习从零到一的基础知识总集篇
机器学习是一门多领域交叉学科,涉及概率论.统计学.逼近论.凸分析.算法复杂度理论等多门学科.专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性 ...
- 使用c++SFML制作月圆之夜总集篇
写在开头 重新以时间线的形式整理一下去年使用c++的SFML库制作月圆之夜(游戏程序设计大作业)的开发过程,括号里面是新的补充以及对一年前自己的吐槽 因为是在大二转专业后做首次接触游戏开发后才做的,当 ...
- Java SE——专栏总集篇
前言: Java 语言,是相对于其他语言而言,门槛低,而且功能还强大的一门编程语言,本人十分看好这一门语言,但是,它也是有深度的,看过本人的<数据结构与算法>专栏的同学们有福了,因为本人在 ...
- 红橙Darren视频笔记 自定义View总集篇
本节目的 了解 ActivityManagerService Activity ActivityManager Window WindowManager WindowManagerService Se ...
- 黑马程序员——总集篇
-----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 本人编写技术博客的时候只是针对章节的一些比较重要的知识点来编写的: 个人感觉质量 ...
- 小圣求职记B:总集篇
1. 搜狐sohu 搜狐在正式招聘前邀请了部分应聘者到武汉研发中心开座谈会(因此简历尽量早投,机会多些),有研发的也有产品的,40人左右,座谈会期间介绍了搜狐汽车.北京研发中心.武汉研发中心和搜狐媒体 ...
- 弘辽科技:直通车总集篇(中篇)
二十一.直通车匹配方式的适用场景有哪些? 拉搜索的车:大词精准小词广泛 测款测图的车:大类目精准,小类目可以广泛,但广泛匹配的时候要看实时进店的关键词是否精准 二十二.什么时候开人群? 测试:测试阶段 ...
- 弘辽科技:直通车总集篇(上篇)
今天小编就来和大家说说,我们开车过程中会经常遇见的问题,也是对直通车答疑贴的问题总结: 一.搜索流量下滑,是怎么回事呢? 1.查看关键词的点击率有没有下滑: 2.查看店铺是否出现刷单违规,店铺降权的情 ...
- 我要学编程,看什么书好?--^_^,这里推荐一些个人觉得很不错的书(五)小集篇
虽然一直想写点什么,但是感觉自己怎么写肯定都不如书上写的好,所以到最后感觉还是推荐点书比较好. 下面列举一些自己感觉还不错的书,可能会和以前的有重复,就当做总集篇吧.一般好书都有些难度,而且看很多书还 ...
最新文章
- Modelsim下进行功能仿真没问题,可是在ISE综合报错,如何解决?
- 手机实名制的一个应用,电信行业的一个可改进之处
- linux oel7没有网络,rhel7/oel7上修改默认内核启动顺序的方法
- seo优化源码_seo按天计费系统,无需登陆批量查询关键词价格
- linux内核_查看Linux内核版本
- python子类分配
- notes_2019
- 怎么解决相位抵消_如何理解音频相位
- mybatis-plus 从2.x到3.x升级指南
- java 开源im_开源IM项目-InChat登录接口设计与实现(基于Netty)
- 用python计算100以内所有奇数的和_Python-while 计算100以内奇数和的方法
- 马成荣版计算机应用基础 教案,课改理念在中职《计算机应用基础》教学中的应用...
- Python有限状态机FMS结合测试用例
- Android应用上架国内各大应用市场对应用Logo、应用截图要求整理
- 教你制作在线签名 【电驴技巧,转verycd】
- pytorch绘制loss和accuracy曲线
- c语言课设菜单,c语言课程设计菜单
- Android11 禁止第三方APP安装,通过白名单管理
- Java多线程——线程池使用示例
- 第5次作业+105032014124+高小娟
热门文章
- (转)对冲基金投身“另类数据”淘金热
- (转)如何看待IT对于证券行业的价值
- 阿里云资深专家起底云网络平台的技术架构升级之路-洛神3.0
- c语言怎样用vc绘图,大佬们,小菜鸟想问一问用vc编译器做简易画图软件
- 【图像加密】基于matlab混沌算法图像加密解密【含Matlab源码 1218期】
- ubuntu安装vmware-tools
- php多个 运算_php计算多个集合的笛卡尔积实例详解
- 计算机应用基础模块3实操题正确答案,国开20秋计算机应用基础作业3 模块4 PowerPoint 2010实操题答案...
- linux maven 添加项目,Eclipse中Maven项目添加jar包
- java zip_Java压缩技术(二) ZIP压缩——Java原生实现