一、bean 注解的使用
在不使用spring注解之前,我们如果要往 IOC 容器中放入一个 java Bean ,需要以下几个步骤:
- 创建一个spring的配置文件 bean.xml
<?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.liuqiuyi.demo.beans.Person">
<property name="name" value="刘秋意"/>
<property name="age" value="24"/>
</bean>
</beans>
-
创建 spring ioc 容器,从容器中获取我们配置的 person 对象
public class SpringStartTest { public static void main(String[] args) { // 指定配置文件所在的目录 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); Person person = (Person) applicationContext.getBean("person"); System.out.println(person); } }
使用注解方式完成以上操作:
- 首先我们需要新建一个配置类 MainConfig ,添加上对应的注解,例如:
/**
* 配置类信息
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Configuration // 需要标识这是一个配置类
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean
* 方法名称默认为 bean在ioc容器中的id。bean类型为该方法的返回值类型
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean
public Person person() {
return new Person("刘秋意", 24);
}
}
-
在创建 ioc 容器时,需要使用 AnnotationConfigApplicationContext
public class SpringStartTest { public static void main(String[] args) { // 注意使用的是 AnnotationConfigApplicationContext ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); // 直接通过类型获取Bean Person person = applicationContext.getBean(Person.class); System.out.println(person); } }
MainConfig 中的 person 方法的方法名称就是ioc中bean的id,可以使用 @Bean(value = "person") 修改
二、包扫描注解
之前使用xml方式的时候,我们需要在xml文件中进行如下的配置:
<!-- 配置包扫描,如果添加了 @Controller @Service @Repository @Component ,就能将对应的类加载到ioc容器中 -->
<context:component-scan base-package="com.liuqiuyi.demo"/>
现在通过注解的方式进行配置:
- 在配置类 MainConfig 上添加 @ComponentScan 扫描注解,效果和上面的xml配置一样
/**
* 配置类信息
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Configuration // 需要标识这是一个配置类
@ComponentScan(value = "com.liuqiuyi.demo")
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean(value = "person")
public Person person() {
return new Person("刘秋意", 24);
}
}
- @ComponentScan 注解可以指定扫描时的排除规则,例如:
/**
* 配置类信息
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Configuration // 需要标识这是一个配置类
@ComponentScan(value = "com.liuqiuyi.demo", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean(value = "person")
public Person person() {
return new Person("刘秋意", 24);
}
}
以上的配置表示,在进行包扫描时,根据注解排除掉 @Controller ,这样加了 @Controller 注解的就不会被加载到 IOC 容器中
- 还可以通过 includeFilters 指定只加载哪些注解,但是需要将 useDefaultFilters 的值设置为false (为true时表示默认加载所有的配置,如果我们想自定义加载哪些注解,就需要设置为 false),具体的可以看下 @ComponentScan 的具体实现
- @ComponentScans 注解里面可以指定多种扫描策略
三、Scope 作用域的配置
之前我们可以在 xml 配置中指定 javaBean 的 作用域,默认为单例的,我们可以修改为多例的,例如:
<!-- 修改bean的作用域为多例的 -->
<bean id="person" class="com.liuqiuyi.demo.beans.Person" scope="prototype">
<property name="name" value="刘秋意"/>
<property name="age" value="24"/>
</bean>
现在我们可以使用对应的注解完成,在 MainConfig 中 bean配置上加上 @Scope("prototype") ,默认是 singleton 单例的
/**
* 配置类信息
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Configuration // 需要标识这是一个配置类
@ComponentScan(value = "com.liuqiuyi.demo", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean(value = "person")
@Scope("prototype") // 设置bean的作用域为多例的
public Person person() {
return new Person("刘秋意", 24);
}
}
四、Lazy 懒加载配置
如果我们希望某一个bean在IOC容器初始化的时候不要被创建,有以下两种方式:
- 通过上面说得的 Scope 注解将bean配置为多例的
- 如果不能将bean的作用域设置为多例的,可以添加 @Lazy 注解,注意这个只针对单例的场景
/**
* 配置类信息
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Configuration // 需要标识这是一个配置类
@ComponentScan(value = "com.liuqiuyi.demo", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean(value = "person")
@Scope("prototype") // 设置bean的作用域为多例的
@Lazy // 默认值为true
public Person person() {
return new Person("刘秋意", 24);
}
}
五、Conditional 按照条件注册
在不同的运行环境下,我需要注入不同的bean对象,这个时候可以使用 @Conditional 注解,例如:如果是Windows环境下,我需要注入 Person 对象,如果是linux环境下,我就不注入这个Person对象
-
创建一个 WindowsCondition 类,实现 Condition 接口,重写里面的方法:
public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Environment environment = context.getEnvironment(); // 获取运行环境 String property = environment.getProperty("os.name"); System.out.println("========: " + property); // 如果运行环境名称中包含 Windows ,则返回 true return property.contains("Windows"); } } -
在配置文件对应的bean上加上注解
@Configuration // 需要标识这是一个配置类 @ComponentScan(value = "com.liuqiuyi.demo", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)}) public class MainConfig { /** * ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id * * @author liuqiuyi * @date 2020-04-28 */ @Bean(value = "person") @Scope("prototype") // 设置bean的作用域为多例的 @Lazy @Conditional(value = WindowsCondition.class) public Person person() { return new Person("刘秋意", 24); } }@Conditional 注解也可以放在 MainConfig 类上
六、Import 配置要加入IOC的bean
除了上面说的 @ComponentScan 注解和 @Bean 注解之外,spring还提供了 @Import 注解,示例:
@Configuration
@Import(Blue.class) // 这里表示将 Blue 这个类加载到IOC容器中
public class MainConfig {
/**
* ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id
*
* @author liuqiuyi
* @date 2020-04-28
*/
@Bean(value = "person")
@Scope("prototype") // 设置bean的作用域为多例的
@Lazy
@Conditional(value = WindowsCondition.class)
public Person person() {
return new Person("刘秋意", 24);
}
}
@Import 注解添加的Bean,bean 的id为类的全路径,例如:com.liuqiuyi.demo.beans.Blue
如果我们一次性要添加很多个,可以配合 ImportSelector 接口使用:
-
新建一个 MyImportSelector 类,需要实现 ImportSelector ,重写对应的方法:
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { // 返回要导入的Bean,bean id 也是类的全路径 return new String[]{"com.liuqiuyi.demo.beans.Red"}; } } -
在 @Import 注解上加上这个 MyImportSelector 类
@Configuration // 需要标识这是一个配置类 @Import({Blue.class, MyImportSelector.class}) public class MainConfig { /** * ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id * * @author liuqiuyi * @date 2020-04-28 */ @Bean(value = "person") @Scope("prototype") // 设置bean的作用域为多例的 @Lazy @Conditional(value = WindowsCondition.class) public Person person() { return new Person("刘秋意", 24); } }
通过 ImportSelector 接口能够完成Bean的注入,但是我们无法指定名称,这时可以使用 ImportBeanDefinitionRegistrar 接口完成自定义的bean注入
-
新建一个 MyImportBeanDefinitionRegistrar 类,实现 ImportBeanDefinitionRegistrar 接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 如果IOC容器中不包含 red 这个Bean,注入 Red 对象 if (!registry.containsBeanDefinition("red")) { // 新建一个BeanDefinition对象 BeanDefinition definition = new RootBeanDefinition(Red.class); registry.registerBeanDefinition("red", definition); } } } -
使用时,在 @Import 注解后加上 MyImportBeanDefinitionRegistrar 这个类
@Configuration // 需要标识这是一个配置类 @Import({Blue.class, MyImportBeanDefinitionRegistrar.class}) public class MainConfig { /** * ‘@Bean’ 注解标识这是一个 javaBean,方法名称默认为 bean在ioc容器中的id * * @author liuqiuyi * @date 2020-04-28 */ @Bean(value = "person") @Scope("prototype") // 设置bean的作用域为多例的 @Lazy @Conditional(value = WindowsCondition.class) public Person person() { return new Person("刘秋意", 24); } }
七、使用 FactoryBean 注册Bean
我们也可以使用 spring 提供的 FactorBean 来注册对应的bean
-
写一个 MyFactoryBean 类,实现 FactoryBean接口,接口的泛型为返回的Bean类型
public class MyFactoryBean implements FactoryBean<Red> { @Override public Red getObject() throws Exception { return new Red(); } @Override public Class<?> getObjectType() { return Red.class; } /** * 返回false表示不是单例 * 返回true表示是单例 */ @Override public boolean isSingleton() { return true; } } -
在对应的配置文件 MainConfig 中加上对应的 Bean 扫描
@Configuration // 需要标识这是一个配置类 public class MainConfig { @Bean("myFactoryBean") public MyFactoryBean myFactoryBean() { return new MyFactoryBean(); } }
注意:
-
从 IOC 容器中获取 bean id 为 myFactoryBean 的对象时,返回的并不是工厂类本身,而是工厂类中返回的对象,例如:
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); Object myFactoryBean = applicationContext.getBean("myFactoryBean"); System.out.println("类型为:" + myFactoryBean); // com.liuqiuyi.demo.beans.Red@2f8dad04 System.out.println("原始类型为:" + applicationContext.getBean("&myFactoryBean")); //com.liuqiuyi.demo.beans.MyFactoryBean@29e495ff如果我们需要获取工厂类 myFactoryBean 本身,那么需要在 bean id 前面加上一个 “&” 符号
八、配置 bean 初始化 和 销毁方法
之前在使用 xml 配置 bean的时候,可以指定 bean 初始化时调用的方法和销毁时调用的方法:
<bean id="person" class="com.liuqiuyi.demo.beans.Person" scope="prototype" init-method="method_a" destroy-method="method_b">
<property name="name" value="刘秋意"/>
<property name="age" value="24"/>
</bean>
对应的注解写法为:
@Bean(value = "person",initMethod = "init",destroyMethod = "destroy")
public Person person() {
return new Person("刘秋意", 24);
}
注意:
- 构造方法在初始化方法之前执行
- bean的作用域为单例的情况下,容器初始化时会调用bean的初始化方法,容器关闭时会调用bean的销毁方法
- bean的作用域为多例的情况下,容器初始化时不会调用bean的初始化方法,在获取bean的时候才会调用初始化方法,并且容器关闭是不调用bean的销毁方法的
除了上面的实现方式,我们还可以通过让 bean 实现 InitializingBean, DisposableBean 接口,重写里面的 afterPropertiesSet() 和 destroy() 方法
public class Person implements InitializingBean, DisposableBean {
public Person() {
}
/**
* 创建bean时调用的初始化方法
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化方法。。。。");
}
@Override
public void destroy() throws Exception {
System.out.println("销毁时方法。。。。");
}
}
第三种实现方法,可以使用 @PostConstruct 和 @PreDestroy 注解完成对应的功能
public class Person {
public Person() {
}
/**
* 创建bean时调用的初始化方法
*/
@PostConstruct
public void init() throws Exception {
System.out.println("初始化方法。。。。");
}
@PreDestroy
public void destroy() throws Exception {
System.out.println("销毁时方法。。。。");
}
}
九、BeanPostProcessor 前置处理器接口
spring 提供了 BeanPostProcessor 接口,用于在 bean 初始化之前 和 初始化完成 之后回调的方法,
-
新建一个类 MyBeanPostProcessor ,实现 BeanPostProcessor 接口,重写对应的方法
public class MyBeanPostProcessor implements BeanPostProcessor { /** * 在bean的初始化方法之前调用 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("前置处理器,在初始化前调用 。。。" + beanName); // 这个bean可以直接返回,也可以包装后返回,但是不能返回null return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("前置处理器,在初始化后调用 。。。" + beanName); return bean; } }
这个 MyBeanPostProcessor 添加了之后,只要是 spring 管理的 bean ,在初始化之前后都会调用 前置处理器中的方法,包括 spring 框架自身的 bean 也会调用
十、Value 注解
如果我们想给 bean 的属性注入值,之前 xml 的配置中,我们可以使用 标签设置对应的值,例如:
<bean id="person" class="com.liuqiuyi.demo.beans.Person" scope="prototype">
<property name="name" value="刘秋意"/>
<property name="age" value="24"/>
</bean>
我们也可以使用 @Value 注解完成这个操作,例如:
-
新建一个Book类,有三种方法赋值
public class Book { /** * 可以手动直接赋值 */ @Value("JAVA从入门到放弃") private String name; /** * 可以使用 ${} 从配置文件中读取值,需要读取对应的配置文件 */ @Value("${book.author}") private String author; /** * 可以使用spring SpEL 表达式设置值 */ @Value("#{100-20}") private Integer price; // 省略 构造方法 和 get/set 方法 } -
如果使用了第二种 ${} 方式赋值,我们需要读取对应的配置文件,此处在 resources 下新建一个配置类 book.preperties
注意 book.preperties 的文件编码,一定要是 UTF-8 的,否则会出现中文乱码
- 在 spring 配置类上,我们还需要加上 @PropertySource 注解,需要告诉 spring 要从哪里读取文件:
@Configuration
@PropertySource(value = {"classpath:book.properties"},encoding = "UTF-8")
public class TestConfig {
@Bean("book")
public Book book() {
return new Book();
}
}
十一、@Autowired 自动注入注解
-
默认优先按照类型去属性中找对应的bean,如果有多个类型的bean,在将属性名称作为bean id 去容器中查找
-
可以使用 @Qualifier 注解根据指定的 bean id 去容器中查找,而不是使用属性名称
-
@Autowired 注解添加之后,默认是一定要找到对应的 bean 的,否则容器启动过程中会报错,如果我们想在有 bean 的情况下注入,没有的情况下不注入,可以设置对应的 required 属性为 false
@Autowired(required = false) BookService bookService -
也可以使用 @Primary 注解标明哪个 bean 的优先级更高,这样在根据属性自动注入时,就会优先匹配加了 @Primary 注解的 bean
@Service @Primary public class BookA implements Books @Service public class BookB implements Books @Autowired Books book //此时会优先匹配BookA
@Resource 和 @Inject 注解:
- 这两个注解都是 java 提供的,@Resource(JSP250)规范提供,@Inject(JSP330)规范提供
- @Resource:可以和@Autowired一样实现自动装配的功能,但是默认是按照 bean 名称进行装配的;并且不能支持 @Primary 和 required = false 的功能
- @Inject:需要导入javax.inject的包,能支持 @Primary 功能,但是不支持 required = false 的功能
另外,@Autowired 注解还可以加在 构造器、参数、方法和属性上,这些情况下都是从容器中获取值,例如:
-
标注在方法位置:
@Component public class Blue { private Red red; public Red getRed() { return red; } /** * autowired 注解可以不加,都是默认从ioc容器中取值 */ @Autowired public void setRed(Red red) { // 不加 autowired 注解 或者 加在这个地方都可以实现从ioc容器中取值 // public void setRed(@Autowired Red red) { this.red = red; } } -
标在构造器上:
@Component public class Blue { private Red red; /** * autowired 注解可以不加,都是默认从ioc容器中取值 */ @Autowired public Blue(Red red) { // 不加 autowired 注解 或者 加在这个地方都可以实现从ioc容器中取值 // public Blue(@Autowired Red red) { this.red = red; } }
十二、xxxAware 接口
如果我们自定义的 bean 想要使用 spring 容器的底层组件(ApplicationContext,BeanFactory 等等),可以让自定义的 bean 实现实现对应的 xxxAware 接口,例如:
@Component
public class Blue implements ApplicationContextAware {
/**
* 使用一个成员变量接收
*/
private ApplicationContext applicationContext;
/**
* 如果我们想使用 ApplicationContext ,可以实现对应的 ApplicationContextAware 接口
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
-
ApplicationContextAware 接口是 Aware 接口的子类,Aware 接口还有其它的实现类来完成不同的功能,使用方法和 ApplicationContextAware 类似,Aware 接口的实现类如下:
-
xxxAware 接口实现原理:使用到了后置处理器,ApplicationContextAware 之所以能够完成将 ApplicationContext 注入到自定义类中,是因为有对应的后置处理器 ApplicationContextAwareProcessor 来实现
十三、@Profile 注解
@profile 注解是 spring 提供的可以根据当前环境,动态切换和激活一系列组件的功能。接下来模拟一个不同环境下链接不同数据库的场景:
- 编写一个配置类 DateSourceConfig 和一个配置文件类 dateSource.properties
@Configuration
@PropertySource(value = "classpath:dateSource.properties", encoding = "UTF-8")
public class DateSourceConfig {
@Value("${username}")
private String useName;
@Value("${password}")
private String password;
@Value("${dev.hosts.url}")
private String devUrl;
@Value("${test.hosts.url}")
private String testUrl;
@Bean("devData")
@Profile("dev") // 表示在dev环境下激活
public DruidDataSource devDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(devUrl);
druidDataSource.setUsername(useName);
druidDataSource.setPassword(password);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return druidDataSource;
}
@Bean("testData")
@Profile("test") // 表示在test环境下激活
public DruidDataSource testDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(testUrl);
druidDataSource.setUsername(useName);
druidDataSource.setPassword(password);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return druidDataSource;
}
}
dateSource.properties 内容为:
username=root
password=123456
dev.hosts.url=jdbc:mysql://localhost:3306/my_demo
test.hosts.url=jdbc:mysql://localhost:3306/my_test
-
启动容器的时候,需要配置 JVM 启动参数,指定激活的是哪个环境
-Dspring.profiles.active=dev -
除了设置 JVM 启动参数,我们也可以直接在代码中进行设置:
public class SpringStartTest { public static void main(String[] args) { // 注意要用 AnnotationConfigApplicationContext AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); // 获取系统运行环境 ConfigurableEnvironment environment = applicationContext.getEnvironment(); //设置启动的环境为dev environment.setActiveProfiles("dev"); // 设置读取的配置文件 applicationContext.register(DateSourceConfig.class); // 刷新容器 applicationContext.refresh(); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); } } }
@profile 也可以设置在类上,这里不做演示
十四、AOP 注解相关
spring AOP:指在程序运行期间动态的将某段代码切入到指定方法的指定位置 spring 的通知方法有:
- 前置通知 @Before :在目标方法运行之前执行
- 后置通知 @After:在目标方法结束之后运行,无论方法是正常结束还是异常结束
- 返回通知 @AfterReturning:在目标方法正常返回之后运行
- 异常通知 @AfterThrowing:在目标方法出现异常之后运行
- 环绕通知 @Around:手动推进目标方法运行 joinPoint.procced()
下面有个小例子简单演示下:
- 导入对应的依赖 spring-aspects
- 首先编写一个切面类 AopAspects
@Aspect // 申明这是一个切面类
@Component
@Order(value = 2) // 设置我们切面的优先级为2,因为spring 事务就是使用aop做的,所以我们的优先级要小于事务的aop
public class AopAspects {
/**
* 切点表达式,此处我们直接使用一个自定义的注解 @AopTest,这里也可以使用切点表达式写
*/
@Pointcut("@annotation(com.liuqiuyi.demo.aop.AopTest)")
public void pointCut() {
}
@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + " 方法开始执行,入参为:" + Arrays.toString(joinPoint.getArgs()));
}
@After("pointCut()")
public void afterMethod(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + " 方法执行完成");
}
/**
* 注意这里 JoinPoint 一定要放在参数第一个
* result 表示我们使用这个参数接收返回值
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void afterReturningMethod(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName() + " 方法执行完成,返回的结果为:" + result);
}
@AfterThrowing(value = "pointCut()", throwing = "e")
public void afterThrowingMethod(JoinPoint joinPoint, Exception e) {
System.out.println(joinPoint.getSignature().getName() + " 方法执行发生异常,异常信息为:" + e.getMessage());
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + " 方法开始执行,入参为:" + Arrays.toString(joinPoint.getArgs()));
Object proceed = null;
try {
proceed = joinPoint.proceed();
} catch (Throwable throwable) {
System.out.println(joinPoint.getSignature().getName() + " 方法执行出现异常,异常信息为:" + throwable);
}
System.out.println(joinPoint.getSignature().getName() + " 方法开始执行,出参为:" + proceed);
return proceed;
}
}
其中,自定义的注解类为 AopTest
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AopTest {
}
- 新建一个配置类 AopConfig,注意一定要加上 @EnableAspectJAutoProxy 注解
@Configuration
@EnableAspectJAutoProxy // 如果使用AOP,一定要加上这个配置,表示开启aop的自动代理
@ComponentScan(value = "com.liuqiuyi.demo.aop")
public class AopConfig {
}
- 编写一个目标类和目标方法
@Component
public class CalculateTest {
@AopTest
public int div(int a, int b) {
return a / b;
}
}
- 测试方法:
public class AopMainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfig.class);
// 注意不要直接new一个目标类,需要从ioc容器中获取
CalculateTest bean = applicationContext.getBean(CalculateTest.class);
bean.div(10, 0);
}
}
总结下来三步:
- 将目标类和切面类都加入到 ioc 容器中,并且告诉 spring 哪个是切面类(通过@Aspect注解标明)
- 在切面类的方法上,标明通知方式,申明切点表达式,告诉 spring 什么时候在哪运行
- 开启基于注解的AOP模式(在配置类上加上 @EnableAspectJAutoProxy 注解 )
十五、spring 注解式事务实现
- 导入相关的依赖:数据源、数据库驱动、spring-jdbc 模块
- 配置数据源、jdbcTemplate,开启基于注解的事务管理功能 @EnableTransactionManagement
@Configuration
@EnableTransactionManagement // 开启基于注解的事务管理功能
@ComponentScan(value = "com.liuqiuyi.demo.aop")
public class AopConfig {
@Bean
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306/my_demo");
druidDataSource.setUsername("root");
druidDataSource.setPassword("123456");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return druidDataSource;
}
/**
* 配置事务管理器
*/
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
- 在需要事务控制的方法上添加 @Transactional 注解