Spring注解驱动
前提知识:
bean是对象,对象不一定是bean。- 只有经ioc容器接管的对象才能称为
bean。 - ioc容器是一个存放bean的容器,可以想象为一个Map集合。
- ioc容器中管理的
bean都是默认单实例的。
1. 使用注解和使用配置文件对比。
1.1 使用配置文件
- 注入
bean,扫描bean,为bean的属性赋值。
- 指定
bean的id和类型。设置属性值。 - 需要写一个
bean.xml配置文件。
<bean id="people" class="com.rm.pojo.People">
<property name="name" value="李四"/>
<property name="age" value="16"/>
</bean>
- 扫描项目中的所有组件,使用
<context:component-scan>标签标注扫描路径。 - 只要标注了
@Reposity、@Service、@Controller、@Component注解的类都会被扫描到,并注入到ioc容器。
<context:component-scan base-package="com.rm"></context:component-scan>
- 使用
ClassPathXmlApplicationContext获取一个ioc容器,再通过getBean方法获取容器中的bean
- 使用的多态知识,父类引用指向子类对象。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
Person person = (Person) applicationContext.getBean("person");
1.2 使用注解
1.注入bean,为bean设置值,扫描所有组件。
- 新建一个
Myconfig类,添加@configuration注解,表明该类是个配置类。该类也将注入到ioc容器中。 @bean注解标在一个方法上面,该方法返回值类型就是bean的类型,方法名就是bean的id。@ComponentScan注解用来扫描所有包,可以根据需求指定哪些组件需要扫描,哪些组件需要排除。
@Configuration
@ComponentScan("com.rm")
public class Myconfig{
@bean
public Person person(){
return new Person("张三",15);
}
}
- 获取bean
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Myconfig.class);
Person person = (Person) applicationContext.getBean("person");
2. IOC
IOC和AOP是Spring两大核心思想。
- IOC的本质是将对象的创建,赋值,初始化,调用.....交给Spring统一管理。
- AOP的本质是切面编程,允许程序在运行期间获取每个类的方法,并对这些方法进行一些增强处理。
2.1 组件注册(将对象交给ioc容器)
Spring应用启动时,会将所有需要交给IOC容器管理的bean统一注册给IOC。IOC容器来统一管理这些bean,需要使用bean时。可以直接从容器中获取。(默认是单实例bean,只存在一个(id和type唯一))。
bean在容器中有很多属性,比如id,类型(属于什么class),生命周期等。
- 对于单实例bean来说,通类型的bean可以存在多个(实现类,继承等)。但是id只能存在一个。
- 获取bean时,根据id和类型来唯一确定一个bean。如果存在相同的,则报错。
bean经过创建---赋值---初始化一系列生命周期后注入到ioc容器中。
2.1.1 @Configuration
该注解用来代替原来的beans.xml配置文件,声明该类是一个配置类。同时将该配置类注入到容器。该配置类bean的id是类名首字母小写。类型是class类型。
@ComponentScan注解用来扫描组件包。替代原来的<context:component-scan>标签。@bean注解用来在ioc容器中注入bean。返回值类型就是bean类型。方法名就是bean的id(默认)。
@Configuration
@ComponentScan("com.rm")
public class Myconfig{
@bean
public Person person(){
return new Person("张三",15);
}
}
2.1.2 @ComponentScan
该注解通过扫描文件夹,批量将文件夹内的组件注入到ioc容器中。可以自定义条件只注入哪些组件或者排除哪些组件。
- value:指定要扫描的包
basePackages和value互为别名,不应同时出现。如同时出现,值必须相同。互为别名的属性必须定义默认值- https://blog.csdn.net/wolfcode_cn/article/details/80654730
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}

excludeFilters排除组件规则,值是一个
@filter注解集合
例:排除所有含Controller和Service注解的组件
@ComponentScan(value="cn.rm",@excludeFilters={@filter(type=FilterType.ANNOTATION,classes={Controller.class,Service.class})})
或 type默认就是FilterType.ANNOTATION
@ComponentScan(basePackages="cn.rm",@excludeFilters={@filter(classes={Controller.class,Service.class})})
includeFilters只注入组件规则,值是一个
@filter注解集合
例:只注入含Reposity注解的组件。主要关闭默认组件注入规则。
@ComponentScan(value="cn.rm",@includeFilters={@filter(type=FilterType.ANNOTATION,classes={Reposity.class})},useDefaultFilters=false)
或 type默认就是FilterType.ANNOTATION
@ComponentScan(basePackages="cn.rm",@includeFilters={@filter(classes={Reposity.class})},useDefaultFilters=false)
自定义FilterType过滤规则
FilterType.ANNOTATION 按照注解进行过滤
@ComponentScan(value="cn.rm",@excludeFilters={@filter(type=FilterType.ANNOTATION,classes={Controller.class})})
FilterType.ASSIGNABLE_TYPE 按照给定的类型进行过滤
UserService.class类型都会被过滤,包括子类或实现类。
@ComponentScan(value="cn.rm",@excludeFilters={@filter(type=FilterType.ANNOTATION,classes={UserService.class})})
2.1.3 @Scope
该注解用来表示注入到ioc的bean的作用域是什么,默认是SCOPE_SINGLETON (单实例)的。ioc容器中的bean默认都是单实例的。无论获取多少次,取到的bean都是同一个。
SCOPE_PROTOTYPE 多实例 (ioc容器创建时不会生成实例。``获取该实例时``,才会创建。每次获取都会创建一个新的。)
SCOPE_SINGLETON 单实例 (ioc容器创建的时候就会生成该实例并放在容器内)
SCOPE_REQUEST web项目用到(同一次请求创建一个实例)
SCOPE_SESSION 同一个session创建一个实例
2.1.4 @Lazy
该注解只针对单实例bean有效。
- 默认:单实例默认在容器创建的时候创建对象。
- 使用@Lazy:容器启动不创建对象,第一次使用(获取)bean时创建对象,并实例化(为属性赋值)。
@Lazy
@Scope("singleton")
@Bean
public User user() {
return new User();
}
2.1.5 @Conditional(重要)
该注解作用:按照条件给容器中注册bean。spring源码大量使用。
- 配置在方法上,满足条件该bean才会被注册。
- 配置在类上,满足条件,该配置类内的所有bean都会被注册。
测试:存在
Bill Gates和Linus两个对象。
- 判断当前系统是Windows时注入
Bill Gates,判断当前系统是Linux时注入Linus。
MainConfig
@Configuration
public class MainConfig {
//参数是匹配规则,返回true说明匹配,注入该bean。
@Conditional({WindowsConditional.class})
@Bean("bill")
public User user1(){
return new User("Bill Gates");
}
@Conditional({LinuxConditional.class})
@Bean("linus")
public User user2(){
return new User("Linus");
}
}
WindowsConditional
public class WindowsConditional implements Condition {
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;
}
}
LinuxConditional
public class LinuxConditional 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;
}
}
测试类
MyTest
public class MyTest {
public static void main(String[] args) {
//如果完全使用配置类方式,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
//查看容器中注册的所有组件
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}

2.1.6 @Import(重要)
源码中也大量使用该注解
- 通过@import注解快速注入一个组件。(调用无参构造器创建一个对象)
- 组件id默认
全类名,组件类型即class类型。
@Configuration
@Import(Red.class)
public class RedConfig {
}
------------------------------
public class Red {
//默认存在无参构造器
}
------------------------------
@Test
public void Test1(){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(RedConfig.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
//打印所有组件的名字,即id
System.out.println(name);
}
context.close();
}
------------------------------
结果
redConfig
cn.rm.pojo.Red
2.1.6.1 ImportSelector接口
编写实现类实现该接口,重写selectImports方法。方法的返回值(全路径类名)即是要注入到ioc容器的bean。可以是多个。注入到ioc容器的bean的id即是全路径名。类型即是该class类。
使用
- 编写一个实现类实现该接口,重写方法,返回需要导入的组件全类名
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"cn.rm.pojo.User"};
}
}
- 将该实现类注入到组件
@Configuration
@Import(MyImportSelector.class)
public class MainConfig {
}
--------------------------------
//组件id:cn.rm.pojo.User
2.1.6.2 ImportBeanDefinitionRegistrar接口(重要)
public interface ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
使用该接口 方法的两个参数:
- AnnotationMetadata:当前类的注解信息
- BeanDefinitionRegistry:BeanDefinition注册类
- 调用BeanDefinition.registerBeanDefinition方法将需要注册的bean根据条件手工注入。
使用该接口
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//判断容器中是否有bean的id为mainConfig的组件,有就执行下一步
boolean b = registry.containsBeanDefinition("mainConfig");
if (b){
//指定bean的定义信息(bean类型,bean作用域等...)
BeanDefinition beanDefinition = new RootBeanDefinition(User.class);
//指定bean的id和类型并注入到容器
registry.registerBeanDefinition("user1",beanDefinition);
}
}
}
将该实现类注入到ioc容器
- 此时容器是存在mainConfig的组件,所以判断生效。注入id为user1,类型为User.class的组件。
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class MainConfig {
}
--------------------------------------------------
//user1
2.1.7 FactoryBean接口
通过创建一个bean工厂,在这个工厂里面生成需要的bean。
public class UserFactoryBean implements FactoryBean {
//生产bean
public Object getObject(){
return new User();
}
//bean类型
public Class<?> getObjectType() {
return User.class;
}
//true:单例。false:多例
public boolean isSingleton() {
return true;
}
}
- 将这个工厂bean注入到容器中。
@Configuration
public class MyConfig(){
@bean
public UserFactoryBean userFactoryBean(){
return new UserFactoryBean();
}
}
- 查看容器中所有的bean。
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
//查看容器中注册的所有组件
String[] names = context.getBeanDefinitionNames();
//mainConfig
//userFactoryBean
- 查看id为
userFactoryBean的组件class类型。实例类型是工厂生产的bean类型。
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Object user = context.getBean("userFactoryBean");
System.out.println("==="+user.getClass());
----------------------------------------------------------------------------------------
//===class cn.rm.pojo.User
如果不想获取生产的bean。想要获取工厂bean本身。
Object userFactoryBean = context.getBean("&userFactoryBean");
System.out.println("---"+userFactoryBean.getClass());
----------------------------------------------------------------
//---class cn.rm.pojo.UserFactoryBean
2.2 bean的生命周期
bean的生命周期由ioc容器来管理:bean创建----赋值----初始化前----初始化----初始化后----销毁前----销毁的过程
- bean创建
- 单实例:在容器启动的时候(new AnnotationConfigApplicationContext())调用无参构造器创建对象。
- 多实例和懒加载实例:在每次获取时创建对象。
2.初始化bean
- 对象创建完成,并赋值后,调用初始化方法。
- 销毁bean
- 单实例和懒加载单实例:容器关闭的时候调用销毁方法
- 多实例:容器不会管理这个bean,只是你需要的时候给你创建,容器不会调用销毁方法。
2.2.1 bean的初始化和销毁
我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候会自动调用自定义的初始化或销毁过程。
新建一个类,自定义初始化和销毁方法
public class Car {
public Car() {
System.out.println("car...construct...");
}
public void init(){
System.out.println("car....init...");
}
public void destroy(){
System.out.println("car...destroy...");
}
}
定义一个配置类,注入Car组件,通过@Bean注解指定初始化和销毁方法
@Configuration
public class MyCarConfig {
@Bean(name = "car1",initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
}
测试

2.2.1.1 指定初始化和销毁方法
1.使用@bean注入组件时
- 指定initMethod 和destroyMethod 确定bean中哪些方法是初始化方法和销毁方法。
public void init(){
System.out.println("car....init...");
}
public void destroy(){
System.out.println("car...destroy...");
}
---------------------------------------------
使用@bean注入组件时,指定initMethod 和destroyMethod 确定bean中哪些方法是初始化方法和销毁方法。
@Bean(name = "car",initMethod = "init",destroyMethod = "destroy")
public Car car(){
return new Car();
}
2.通过实现接口,重写接口方法。
- 实现
InitializingBean接口,重写afterPropertiesSet方法,该方法在bean创建好并赋值之后执行。(定义初始化逻辑) - 实现
DisposableBean接口,重写destroy方法,该方法在单实例bean销毁后执行。
3.使用JSR250规范定义的两个注解
- 注解标注在bean所在类的方法上。
- @PostConstruct:该注解标注的方法在bean创建完成并赋值之后调用。来执行初始化方法。
- @PreDestroy:在容器销毁之前调用该注解标注的方法,用来通知清理工作。
@PostConstruct
public void init(){
System.out.println("car....init...");
}
@PreDestroy
public void destroy(){
System.out.println("car...preDestroy...");
}
---------------------------------------------
@Bean
public Car car(){
return new Car();
}
2.2.2 后置处理器BeanPostProcessor
在bean初始化前后进行一些工作。
实现
BeanPostProcessor接口
- postProcessBeforeInitialization:在bean初始化之前进行一些工作。
- postProcessAfterInitialization:在bean初始化之后进行一些工作。
@Component
public class MyBeanPostProcess implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器处理前--->"+beanName+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后置处理器处理后--->"+beanName+"---"+bean);
return bean;
}
}
---------------------------------------------
// 先调用构造器创建对象,然后赋值(见下一节原理),然后执行初始化前方法,初始化,初始化后方法。容器关闭的时候调用销毁方法
car...construct...
后置处理器处理前--->car---cn.rm.pojo.Car@4d339552
car....init...
后置处理器处理后--->car---cn.rm.pojo.Car@4d339552
2.2.2.1 后置处理器BeanPostProcessor原理

//1.加载配置文件,创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
---------------------------------------------------------------------------------------------------------
//2.刷新容器
refresh();
---------------------------------------------------------------------------------------------------------
//3.实例化所有剩余的(非惰性初始化)单例(创建bean)
finishBeanFactoryInitialization(beanFactory);
//3.1实例化前
beanFactory.preInstantiateSingletons();
//3.2判断容器中是否存在该bean
getBean(beanName);
return doGetBean(name, null, null, false);
//3.3创建该bean的实例。
sharedInstance = getSingleton(beanName, () -> {...}
//3.4返回在给定名称下注册的(原始)单例对象,如果尚未注册,则创建并注册一个新对象。 @return 注册的单例对象
singletonObject = singletonFactory.getObject();
---------------------------------------------------------------------------------------------------------
//3.5 首次加载,不存在该bean对象,于是创建bean
return createBean(beanName, mbd, args);
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
---------------------------------------------------------------------------------------------------------
//4.给bean赋值
populateBean(beanName, mbd, instanceWrapper);
---------------------------------------------------------------------------------------------------------
//5.初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
---------------------------------------------------------------------------------------------------------
初始化bean方法(initializeBean)体内容
if (mbd == null || !mbd.isSynthetic()) {
//执行初始化之前方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//执行初始化方法,如@PostConstruct标注的方法。或@Bean表明的初始化方法。
invokeInitMethods(beanName, wrappedBean, mbd);
}
if (mbd == null || !mbd.isSynthetic()) {
//执行初始化之后方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
执行初始化前方法(applyBeanPostProcessorsBeforeInitialization)方法体
- 遍历获取所有bean后置处理器(只要实现了BeanPostProcessor接口都是),挨个执行postProcessBeforeInitialization方法
- 一旦返回null,跳出循环。
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
2.2.2.2 Spring底层使用后置处理器BeanPostProcessor
例:ApplicationContextAware接口
该接口功能:可以在组件中注入ioc容器。功能是ApplicationContextAwareProcessor提供的。
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
/**
* 通过给定的ioc容器创建一个ApplicationContextAware后置处理器
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
//将ioc容器赋值给该类的属性applicationContext,方便其他方法调用。
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
//
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof ApplicationContextAware)){
return bean;
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
@Component
public class MyTestApp implements ApplicationContextAware {
//想要在该类中其他方法使用ioc容器,只需要将setApplicationContext方法的参数保存在applicationContext属性即可。
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println("=------>"+name);
}
}
}
2.2.3 Bean的属性赋值
2.2.3.1 @Value
- 基本数值
- SPEL表达式 #{}
@Value("张三")
private String name;
@Value("#{20-2}")
private Integer age;
2.2.3.2 @PropertySource
使用该注解读取外部配置文件的key/value,保存到运行的环境变量。加载完外部的配置文件后使用${}取值
person.name=张三
@PropertySource("classpath:/person.properties")
@Configuration
public class MyConfig{
@Bean
public Person person(){
return new Person();
}
}
--------------------------------------------------
public class Person{
@Value("${person.name}")
private String name;
}
2.2.4 Bean的自动装配
Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值。
AutowiredAnnotationBeanPostProcessor:后置处理器,解析完成自动装配功能。
2.2.4.1 @AutoWired等
- 默认有限按照组件类型从容器中找该class类型的组件。
- 如果只有一个:使用该组件,将找到的组件赋值给该属性。
- 如果有多个:使用属性名作为id进行查找。如果查到一个使用该组件。查到多个,报错。
- 如果容器中不存在,查不到该注解时不想让报错。@AutoWired(required=false)。找不到就赋值null。
public Class MyServiceImpl{
@AutoWired
private MyDao myDao;
}
@Qualifer注解
- 使用该注解的value值作为id去ioc容器查找。而不是用默认的属性名去查找。
public Class MyServiceImpl{
@AutoWired
@Qualifer("dao111")
private MyDao myDao;
}
2.2.4.2 @Resource等
- @Resource(JSR250规范)默认按照属性名作为组件id去查找。可以通过@Resource(name="")指定组件id名。
- @Inject(JSR330规范)需要导入javax.inject的包。功能和 @AutoWired一样。
- 这两个是java规范的注解
public Class MyServiceImpl{
@Resource
private MyDao myDao;
}
#### 2.2.4.3 方法、构造器自动装配
> 自动装配注解标注在方法上。
- 标注在方法上,Spring容器创建该对象时,就会调用该方法,完成该方法参数的赋值。
- 方法使用的``参数``,``自定义类型``从ioc容器中获取。
```java
private Car car;
@AutoWired
public void setCar(Car car){
this.car =car;
}
自动装配标注在构造器上
- 默认注入到ioc容器中的组件,调用的都是无参构造器创建对象。之后再进行初始化及赋值操作。
- 构造器要用的组件,都是从容器中获取的。
//只存在有参构造时生效。存在无参构造时,使用的是无参构造创建的对象。
@AutoWired
public Boss(Car car){
this.car=car;
}
标注在参数上
- 也是从容器中获取
2.2.4.4 Aware自动装配原理
自定义组件想要使用Spring容器底层的一些组件(如:ApplicationCopntext、BeanFactory等)
- 需要自定义组件所属的类实现xxxAware接口。在创建该对象时,会调用接口规定的方法,注入底层组件。
继承Aware接口的接口

自定义组件响应获取ioc容器,实现ApplicationContextAware接口。
- 在创建Car对象时,会调用setApplicationContext方法,获取到ioc容器。
- 将ioc容器赋值给该类的属性,其他方法即可使用ioc容器。
public class Car implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Car() {
System.out.println("car...construct...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public void printBeanNames(){
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
3. AOP
什么是AOP
- 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
- 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
- AOP底层使用的是动态代理模式。
有两种情况的动态代理。
- 需要增强的类有接口,使用JDK动态代理。
- 创建接口实现类代理对象,增强类中的方法。

- 没有接口,使用cglib动态代理。
- 创建子类的代理对象,增强类中的方法。

3.1 AOP中的一些术语
连接点
- 类里面哪些方法可以被增强,这些方法称为连接点。

切入点
- 实际被真正增强的方法,叫做切入点。
- 比如上面四个方法都能被增强,这四个方法都称为连接点。但实际只有
add()方法被增强了,那add()方法被称为切入点。
通知(增强)
- 实际增强的逻辑部分称为通知(增强)。
- 通知有多种类型。
前置通知、后置通知、环绕通知、异常通知、最终通知(类似于try..catch中的finally)。
切面
- 切面是一个动作,把通知应用到切入点的过程称为切面。
切入点表达式
- 作用:用来确定对哪个类里面的哪个方法进行增强。
- execution([权限修饰符][返回类型][类全路径][方法名][参数列表])
对UserServiceImpl中的add方法进行增强
- 返回类型可以不写,修饰权限符
*表示所有权限符。(..)表示参数列表。
execution(* com.rm.service.UserServiceImpl.add(..))
对UserServiceImpl中的所有方法进行增强
execution(* com.rm.service.UserServiceImpl.*(..))
对com.rm.service包中的所有类的所有方法进行增强
execution(* com.rm.service.*.*(..))
3.2 相同切入点抽取(重要)
- 将相同的切入点通过
@Pointcut抽取。 - 再使用该切入点,加上
方法名即可。
@Pointcut("execution(* cn.rm.service.UserServiceImpl.*(..))")
public void pointDemo(){
}
@Before("pointDemo()")
public void before() {
System.out.println("前置通知====");
}
3.3 使用@Aspect注解实现aop
创建接口和实现类
实际业务方法
public class DivServiceImpl implements DivService {
public int div(int i,int j) {
System.out.println("方法被执行了.....");
return i/j;
}
}
创建增强类
对业务方法进行增强
- 可以获取方法名称,参数名,返回值。异常信息。
@Aspect
public class DivPointCut {
//相同切入点抽取
@Pointcut("execution(* cn.rm.service.DivServiceImpl.*(..))")
public void pointDemo(){
}
@Before("pointDemo()")
public void before(JoinPoint point) {
String methodName = point.getSignature().getName();
List<Object> list = Arrays.asList(point.getArgs());
System.out.println("前置通知执行,方法名:"+methodName+"参数列表:"+list);
}
@After("pointDemo()")
public void after(JoinPoint point) {
String methodName = point.getSignature().getName();
List<Object> list = Arrays.asList(point.getArgs());
System.out.println("后置通知执行,方法名:"+methodName+"参数列表:"+list);
}
@AfterReturning(value = "pointDemo()",returning="result")
public void afterReturning(JoinPoint point,Object result) {
String methodName = point.getSignature().getName();
List<Object> list = Arrays.asList(point.getArgs());
System.out.println("返回通知执行,方法名:"+methodName+"参数列表:"+list+"返回值:"+result);
}
@Around(value = "pointDemo()")
//若方法返回值是void。afterReturning获取到的result也是null。因为先执行的环绕通知。
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕执行前====");
Object o = point.proceed();
System.out.println("环绕执行后====");
System.out.println("方法执行结果"+o);
return o;
}
@AfterThrowing(value = "pointDemo()",throwing = "exception")
public void afterThrowing(JoinPoint point,Exception exception) {
System.out.println(point.getSignature().getName()+"方法遇到异常执行====异常信息:"+exception.getMessage());
}
}
注入ioc容器
@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
@Bean
public DivService divService(){
return new DivServiceImpl();
}
@Bean
public DivPointCut divPointCut(){
return new DivPointCut();
}
}
测试(正常情况)
public class MyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
DivService bean = (DivService) context.getBean("divService");
bean.div(2,1);
context.close();
}
---------------------------------------------------------------------------------------------------------------
环绕执行前====
前置通知执行,方法名:div参数列表:[2, 1]
方法被执行了.....
环绕执行后====
方法执行结果2
后置通知执行,方法名:div参数列表:[2, 1]
返回通知执行,方法名:div参数列表:[2, 1]返回值:2
测试(异常)
public class MyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
DivService bean = (DivService) context.getBean("divService");
bean.div(2,0);
context.close();
}
}
---------------------------------------------------------------------------------------------------------------
环绕执行前====
前置通知执行,方法名:div参数列表:[2, 0]
方法被执行了.....
后置通知执行,方法名:div参数列表:[2, 0]
div方法遇到异常执行====异常信息:/ by zero
总结:
使用AOP分三步
- 将业务逻辑和切面类注入到容器,告诉spring哪个是切面类(@Aspect)
- 在切面类上的每个方法上标注注解通知,告诉spring何时运行(切入点表达式)
- 开启基于注解的AOP模式(@EnableAspectJAutoProxy)
3.4 增强优先级
- 如果有两个类都对一个类中的一个切入点进行增强,可以通过
@Order注解设置优先级。 - 数值越小,优先级越高。
@Aspect
@Order(1)
public class AnnPointCut1 {
}
@Aspect
@Order(2)
public class AnnPointCut2 {
}
3.5 AOP原理
。。。。。。。。。。。。。
4. 事务
4.1 事务特性
ACID原则
- 原子性:一个事务中的多个语句要么都成功,要么都失败。
- 一致性:事务执行前后,数据总量不变。
- 隔离性:多个角色操作同一个记录。多个事务直接相互隔离,不会产生影响。
- 持久性:事务一经提交,持久化到数据库。
4.2 事务操作
声明式事务(使用),编程式事务(不推荐)。
声明式事务:基于注解(使用),基于配置文件(不推荐)。
- 在spring中进行声明式事务管理,底层使用的就是AOP。
使用事务需要导入数据库相关依赖
- 声明式事务使用的还是数据库的事务。所以操作的数据库引擎需要支持事务才能用(INNODB)。
@Transactional注解
- 该注解标注的方法表示该方法是一个事务方法。
- 在SpringBoot中只需要加该注解即可实现事务功能。
- 只要是
RuntimeException和RuntimeException下面的子类抛出的异常 @Transactional都可以回滚的。 - 如果需要支持回滚Exception异常可以用@Transactional(rollbackFor = Exception.class)
注解失效场景
- 注解标注的方法不是public修饰的
- 异常被try...catch捕获了,没有抛出。
- 该方法所在的类没有注入到ioc容器。
业务逻辑层
public class userService{
@AutoWired
private UserMapper userMapper
@Transactional
public void insertUser(User user){
return userMapper.insertUser(user);
}
}
4.3 事务原理
。。。。。。。。。

浙公网安备 33010602011771号