作用:表示这个类是一个spring配置类,相当于spring的xml配置文件!
ps:其本身也是一个component组件
作用:写在Configuration配置类中,其功能是声明一个Bean(写在一个方法上),相当于xml配置文件中的bean标签,默认使用方法名作为id,使用方法类型作为Bean的类型
@Bean("自定义的bean的id")
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
作用:组件,向spring中注册
@ComponentScan(value = "com.atguigu",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
})
//指定排除规则
FilterType过滤规则
FilterType.ANNOTATION:按照注释
FilterType.ASSIGNABLE_TYPE:按照给定的类型
FilterType.ASPECTJ:使用ASPECTJ表达式(不常用)
FilterType.REGEX:使用正则表达式
FilterType.CUSTOM:使用自定义规则(TypeFilter的实现类)
package com.atguigu.filter;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
/**
* @Author 回到原点
* @Date 2021/4/20 14:35
* @Version 1.0
*/
public class MyTypeFilter implements TypeFilter {
//metadataReader:读取到当前正在扫描的类的信息
//metadataReaderFactory:可以获取到其他任何类的信息
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;
}
}
作用:指定作用域,prototype(多实例),singleton(单实例)
ps:在创建多个相同的对象时是新建,还是指向同一个
@Test
public void test02() {
//加载配置类
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
//spring默认单实例,多次获取的是同一个
Person person = (Person) applicationContext.getBean("person");
Person person2 = (Person) applicationContext.getBean("person");
System.out.println(person == person2);
}
package com.atguigu.config;
import com.atguigu.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* @Author 回到原点
* @Date 2021/4/20 14:46
* @Version 1.0
*/
@Configuration
public class MainConfig2 {
/**
* ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
* * @since 4.2
* * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE prototype
* * @see ConfigurableBeanFactory#SCOPE_SINGLETON singleton
* * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
* @return
*
* prototype:多实例 ioc容器并不会调用方法放在容器中,每次获取的时候才会调用方法创建对象
* singleton:单实例(默认的) ioc容器启动,会调用方法创建对象放到ioc容器中,以后每次获取就直接从(map.get())容器中拿
*/
@Scope("prototype")
@Bean("person")
public Person person() {
return new Person("小明",20);
}
}
懒加载:容器启动先不创建对象,第一次获取bean的时候再去创建对象,并初始化
@Lazy
@Bean("person")
public Person person() {
return new Person("小明",20);
}
作用:按照一定条件进行判断,满足条件给容器中注册Bean(放在类上,满足条件类中所有的bean都会被装配)
需求:判断当前系统是window还是linux,如果是windows注入windows相应的bean
配置类
package com.atguigu.config;
import com.atguigu.Person;
import com.atguigu.condition.LinuxCondition;
import com.atguigu.condition.WindowsCondition;
import org.springframework.context.annotation.*;
/**
* @Author 回到原点
* @Date 2021/4/20 14:46
* @Version 1.0
*/
@Configuration
public class MainConfig2 {
@Lazy
@Bean("person")
public Person person() {
return new Person("小明",20);
}
@Bean
public Person person01() {
return new Person("jack",15);
}
@Conditional({WindowsCondition.class})
@Bean("windows")
public Person person02() {
return new Person("bill gus",60);
}
@Conditional({LinuxCondition.class})
@Bean("linux")
public Person person03() {
return new Person("linus",48);
}
}
配置windos规则
package com.atguigu.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;
/**
* @Author 回到原点
* @Date 2021/4/20 15:41
* @Version 1.0
*/
public class WindowsCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取到ioc使用的BeanFactory
ConfigurableListableBeanFactory 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("Windows")) {
return true;
}
return false;
}
}
linux规则
package com.atguigu.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;
/**
* @Author 回到原点
* @Date 2021/4/20 15:41
* @Version 1.0
*/
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
//获取当前操作系统
String property = environment.getProperty("os.name");
if (property.contains("linux")) {
return true;
}
return false;
}
}
测试
@Test
public void test03() {
//根据类型获取bean
String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
}
作用:通过快速导入的方式实现把实例加入spring的IOC容器中(只能作用在类上)
加入IOC容器的方式有很多种,@Import注解就相对很牛皮了,**@Import注解可以用于导入第三方包** ,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷
三种用法
@Import({类名.class, 类名.class})
public class MainConfig2 {
}
返回需要导入的组件的全类名数组,将实现了这个接口的类写到Import注解上,这样返回的全类名就都会被装配到ioc容器中
同样是一个接口,类似于第二种ImportSelector用法,相似度80%,只不过这种用法比较自定义化注册,可以实现一些简单的逻辑判断
FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程
先来看一下FactoryBean中有什么东西
public interface FactoryBean<T> {
//返回的对象实例
T getObject() throws Exception;
//Bean的类型
Class<?> getObjectType();
//true是单例,false是非单例 在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回true
boolean isSingleton();
}
具体实现
public class MyFactoryBean implements FactoryBean {
public Object getObject() throws Exception {
System.out.println("getobject.....");
return new Color();
}
public Class<?> getObjectType() {
return Color.class;
}
public boolean isSingleton() {
return true;
}
}
在配置类中注册Bean
@Bean
public MyFactoryBean factoryBean() {
return new MyFactoryBean();
}
测试
@Test
public void testImport() {
Object bean = applicationContext.getBean("factoryBean");
System.out.println(bean+"的类型");
soutBeanNames(applicationContext);
}
运行结果
如果标注在接口的方法上,那么实现接口后不会强制重写方法
)以上两个注解在程序运行的过程中不会起任何作用,只会在IDE、编译器、FindBugs检查、生成文档的时候有做提示;我使用的IDE是STS,不会做自动的检查,只有安装了FindBugs插件并运行后会做对应的提示,
bean的生命周期:
在bean上指定初始化和销毁方法 initMethod destroyMethod
通过让bean实现 InitializingBean(定义初始化逻辑)
DisposableBean(定义销毁逻辑)
可以使用JSR250:@PostConstruct,在bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestroy,在容器销毁bean之前通知我们进行清理工作
BeanPostProcessor:bean的后置处理器;在bean初始化前后进行一些处理工作;
spring底层对于BeanPostProcessor的使用;
bean的赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx 都是使用的BeanPostProcessor
指定初始化和销毁方法 @Bean(initMethod = “方法名”,destroyMethod = “方法名”)
package com.atguigu.config;
import com.atguigu.beans.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
/**
* @Author 回到原点
* @Date 2021/4/21 15:17
* @Version 1.0
*
*
* 构造(对象创建)
* 单实例,在容器启动的时候创建对象
* 多实例,在每次获取的时候创建对象
* 初始化:
* 对象创建完成,并赋值好,调用初始化方法....
* 销毁:
* 单实例:容器关闭的时候
* 多实例:容器不会管理这个bean,容器不会调用销毁方法;手动调用
*
* 1)、在bean上指定初始化和销毁方法 initMethod destroyMethod
* 2)、通过让bean实现 InitializingBean(定义初始化逻辑)
DisposableBean(定义销毁逻辑)
3)、可以使用JSR250:@PostConstruct,在bean创建完成并且属性赋值完成,来执行初始化方法
@PreDestroy,在容器销毁bean之前通知我们进行清理工作
4)、BeanPostProcessor:bean的后置处理器
*/
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod = "init",destroyMethod = "destory")
public Car car() {
return new Car();
}
}
car.class
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......");
}
}
通过让bean实现接口
public class Car implements InitializingBean, DisposableBean {
public Car() {
System.out.println("car constructor......");
}
public void destroy() throws Exception {
System.out.println("car destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("car init...");
}
}
使用JSR250
public class Car {
public Car() {
System.out.println("car constructor......");
}
@PostConstruct
public void init() {
System.out.println("car init......");
}
@PreDestroy
public void destory() {
System.out.println("car destory......");
}
}
BeanPostProcessor
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
package com.atguigu.beans;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @Author 回到原点
* @Date 2021/4/21 16:01
* @Version 1.0
* 后置处理器
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
//初始化前
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//谁被创建这个bean获取的就是谁
System.out.println("before..."+beanName+"=>"+bean);
return bean;
}
//初始化后
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before..."+beanName+"=>"+bean);
return bean;
}
}
ps:每一个对象在初始化之前,都会调用方法(aop概念)
普通组件获取ioc容器的方法
(实现ApplicationContextAware接口)
@Component
public class Cat implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
作用:写在实体类的属性上方,可以为属性赋值
1.可以写基本数据类型
2.可以写spEL;#{}
3.可以写${},取出配置文件中的值
person实体类
package com.atguigu;
import org.springframework.beans.factory.annotation.Value;
/**
* @Author 回到原点
* @Date 2021/4/20 14:47
* @Version 1.0
*/
public class Person {
/**
* 使用@value赋值
* 1.可以写基本数据类型
* 2.可以写spEL;#{}
* 3.可以写${},取出配置文件中的值
*/
@Value("张三")
private String name;
@Value("#{25-10}")
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;
}
}
结果
作用:读取外部配置文件,写在Configuration配置类上、
和@Value配合使用:${},取出配置文件中的值
配置文件
//使用PropertySource获取配置文件中的key-value保存到环境变量中
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person() {
return new Person();
}
}
作用:自动装配(注入)bean;先按类型查找,如果有多个相同的类型,根据id名称查找(默认名称是方法名首字母小写);注意,是从ioc容器中获取并自动注入到当前类中,而@Bean是将类注册到IOC容器中,两个的功能并不相同
1.1 @Autowired(构造器,参数,方法,属性;都是从容器中获取参数的组件值)
@Autowired是一种注解,可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作,@Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上,也可以放在任意方法上表示,自动执行当前方法,如果方法有参数,会在IOC容器中自动寻找同类型参数为其传值。
这里必须明确:@Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier使用;
1.1.1 构造器注入
@RestController
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
}
1.1.2 setter方法注入
@RestController
public class UserController {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
1.1.3 field反射注入
@RestController
public class UserController {
@Autowired
private UserService userService;
}
上面已经说到@Autowired按类型装配Spring Bean。如果容器中有多个相同类型的bean,则框架将抛出NoUniqueBeanDefinitionException, 以提示有多个满足条件的bean进行自动装配。程序无法正确做出判断使用哪一个,通过将@Qualifier注解与我们想要使用的特定Spring bean的名称一起进行装配,Spring框架就能从多个相同类型并满足装配要求的bean中找到我们想要的,
@Component("studentInfo")
public class StudentInfo implements UserInfo {
public String userName() {
return "student";
}
}
@Component("teacherInfo")
public class TeacherInfo implements UserInfo {
public String userName {
return "teacher";
}
}
@Component
public class UserService {
@Autowired
@Qualifier("studentInfo")
private UserInfo userInfo;
//todo
}
作用:让spring进行自动装配的时候,默认使用首选的bean,不能和Qualifier同时使用,(用于有多个相同的类型不同的名称的bean)
@Configuration
public class MainConfigOfPropertyValues {
@Primary
@Bean
public Person person() {
return new Person();
}
}
spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
作用:类似Autowired也可以完成自动装配,默认按照组件名称进行装配没有能支持@Primary功能没有支持@Autowired(reqiured=false);
@Resource(name = "bookDao")
@Inject:需要导入javax.inject的包,和Autowired的功能一样
区别:Autowired是Spring定义的功能相对来说要多,Resource和Inject是java定义的规范脱离spring框架也可以使用
自定义组件想要实现Spring容器底层的一些组件(ApplicationContext,BeanFactory,xxx)时,自定义组件只需要实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件;
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的Ioc"+applicationContext);
}
public void setBeanName(String s) {
System.out.println("当前bean的名字"+s);
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
//解析字符串
resolver.resolveStringValue("你好${os.name} 我是#{80-50}");
}
}
xxxAware:对应的功能使用xxxProcessor实现
作用:Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;(在springboot中被配置文件所替代)
开发环境develop、测试环境test、生产环境master
数据源:(/dev) (/test) (/master)
@Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
package com.spring.config;
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* Profile:
* Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能;
*
* 开发环境develop、测试环境test、生产环境master
* 数据源:(/dev) (/test) (/master)
*
* @Profile:指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件
*
* 1) 加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
* 2) 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
* */
@PropertySource("classpath:/dbconfig.properties")
@Configuration
public class MainConfigOfProfile implements EmbeddedValueResolverAware{
@Value("${db.user}")
private String user;
private String driverClass;
@Profile("default")
@Bean("test")
public DataSource testDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("dev")
@Bean("dev")
public DataSource devDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
@Profile("master")
@Bean("master")
public DataSource masterDataSource(@Value("${db.password}")String password) throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setDriverClass(driverClass);
return dataSource;
}
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String driverClass = resolver.resolveStringValue("${db.driverClass}");
this.driverClass = driverClass;
}
}
package com.spring.test;
import java.util.Arrays;
import javax.sql.DataSource;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.spring.config.MainConfigOfProfile;
public class IOCTestProfile {
//1. 使用命令行动态参数:在虚拟机参数位置加载 -Dspring.profiles.active=test
//2. 使用代码的方式激活某种环境;
@Test
public void test01() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
//1. 创建一个applicationContext
//2. 设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev","master");
//3. 注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4. 启动刷新容器
applicationContext.refresh();
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
System.out.println(Arrays.toString(beanNamesForType));
applicationContext.close();
}
@Test
public void test02() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
String[] beanNamesForType = applicationContext.getBeanNamesForType(DataSource.class);
System.out.println(Arrays.toString(beanNamesForType));
applicationContext.close();
}
}
导入包
org.springframework
spring-aspects
5.3.2
下方一系列注解使用方法详见最后的代码部分
作用:开启切面自动代理写在Configuration配置类上;详见最后的代码部分
作用:告诉spring当前类是切面类;详见最后的代码部分
作用:抽取要切入的目标函数;详见最后的代码部分
/**
* 抽取切入函数
* *代表所有方法,..代表所有参数
*/
@Pointcut("execution(public int com.atguigu.beans.MathMaratic.*(..))")
作用:在目标方法(除法div)运行之前运行;详见最后的代码部分
作用:在目标方法(除法div)运行之后运行;详见最后的代码部分
作用:在目标方法(除法div)正常返回之后运行;详见最后的代码部分
作用:在目标方法(除法div)出现异常以后运行;详见最后的代码部分
=======================================================================================================================================
config配置类
package com.atguigu.config;
import com.atguigu.Aspect.LogAspects;
import com.atguigu.beans.MathMaratic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* @Author 回到原点
* @Date 2021/4/23 9:41
* @Version 1.0
*/
@Configuration
@EnableAspectJAutoProxy//开启切面自动代理
public class MainConfigOfAspect {
@Bean(initMethod = "init",destroyMethod = "destory")
public MathMaratic mathMaratic() {
return new MathMaratic();
}
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
业务逻辑类
package com.atguigu.beans;
/**
* @Author 回到原点
* @Date 2021/4/23 9:30
* @Version 1.0
*/
public class MathMaratic {
public int div(int i, int j) {
System.out.println("div excutive");
return i/j;
}
/*这里尝试了一下@Bean注解的init和destroy,init的优先级要高于切入
public void init() {
System.out.println("init");
}
public void destory() {
System.out.println("destory");
}*/
}
切面配置类
package com.atguigu.Aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import java.util.Arrays;
/**
* @Author 回到原点
* @Date 2021/4/23 9:31
* @Version 1.0
*/
//告诉spring这是切面类
@Aspect
public class LogAspects {
/**
* 抽取切入函数
* *代表所有方法,..代表所有参数
*/
@Pointcut("execution(public int com.atguigu.beans.MathMaratic.*(..))")
public void pointCut(){};
/**
* Before(要切入的目标函数)
* 可以直接使用上方抽取的函数的方法名
*/
@Before("pointCut()")
public void logBefore(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("除法执行前...方法名{"+joinPoint.getSignature().getName()+"}...参数"+ Arrays.asList(args));
}
@After("pointCut()")
public void logAfter() {
System.out.println("除法正常执行后...");
}
@AfterReturning(value = "pointCut()",returning = "result")
public void logAfterReturn(Object result) {
System.out.println("除法正常返回...."+result);
}
@AfterThrowing(value = "pointCut()",throwing = "except")
public void logException(JoinPoint joinPoint,Exception except) {
//这里joinpoint一定要放在第一个
System.out.println(joinPoint.getSignature().getName()+"除法异常...."+except);
}
}
JoinPoint:可以获取到目标函数的名字,参数等
JoinPoint参数一定要出现在参数的第一位
@Import(AspectJAutoProxyRegistrar.class)
作用:开启基于注解的事务管理功能,写在config类上
作用:在方法上标注@Transactional注解,表示当前方法是一个事务方法;如果出现任何异常,回滚状态(需要和EnableTransactionManagement配合使用)
要想成功使用事务必须要注入PlatformTransactionManager
@Bean
public PlatformTransactionManager platform() {
//事务必须要管理数据源
return new DataSourceTransactionManager(dataSource())
}