Spring面试题

发布于:2024-07-01 ⋅ 阅读:(10) ⋅ 点赞:(0)

注解

@Component与@Bean的区别

1. @Component 注解
@Component 注解是Spring中用于定义一个普通组件类的注解。你可以将它用在任何希望由Spring容器管理的类上

Spring容器启动时,它会自动扫描整个类路径,寻找所有带有 @Component 注解的类,并将其实例化为Bean对象。这些Bean对象随后就可以被注入到其他组件中,实现依赖注入的功能。

此外,@Component 注解还有三个衍生注解: @Controller、 @Service 和 @Repository,它们分别用于标识控制器、业务层和数据访问层的组件。这些注解在功能上与 @Component 相同,但提供了更具体的语义,有助于我们对应用程序的不同部分进行更好的划分和组织。

2. @Bean注解
与 @Component 不同, @Bean 注解是用于在Java配置类中定义一个Bean对象的方法
通常,我们会将 @Configuration 注解加在一个类上,表示这是一个配置类。在这个配置类中,我们可以使用 @Bean 注解来标记方法,这些方法将返回一个Bean对象。
Spring容器会根据这些配置来创建相应的Bean,并在整个应用程序中提供这些Bean的访问。
要注意的是,通过 @Bean 注解定义的Bean对象是通过调用被标记方法的方式创建的。这意味着我们可以在方法中进行额外的初始化操作,或者在返回Bean对象之前对其进行修改。这种灵活性使得@Bean注解在某些场景下比 @Componen t注解更加有用。

如果你只是需要一个简单的Bean对象,并且不需要进行额外的初始化或修改操作,那么@Component注解可能是一个更好的选择。因为它更简洁,且不需要手动配置。然而,如果你需要在创建Bean对象时进行额外的操作,或者需要更灵活地控制Bean的创建过程,那么@Bean注解将是一个更好的选择。

Bean注解的使用示例:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

上面的代码相当于下面的 xml 配置

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

下面这个例子是通过 @Component 无法实现的。

@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

@Autowired 和 @Resource 的区别是什么?

  1. @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
  2. @Autowired默认的注入方式为byType(根据类型进行匹配),@Resource默认注入方式为 byName(根据名称进行匹配)。当一个接口存在多个实现类的情况下,@Autowired 和@Resource都需要通过名称才能正确匹配到对应的 Bean。Autowired 可以通过 @Qualifier 注解来显式指定名称,@Resource可以通过 name 属性来显式指定名称。
  3. @Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。

使用方式:

举个例子,SmsService 接口有两个实现类: SmsServiceImpl1和 SmsServiceImpl2,且它们都已经被 Spring 容器所管理。

// 报错,byName 和 byType 都无法匹配到 bean
@Autowired
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正确注入  SmsServiceImpl1 对象对应的 bean
// smsServiceImpl1 就是我们上面所说的名称
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

public @interface Resource {
    String name() default "";
    Class<?> type() default Object.class;
}
// 报错,byName 和 byType 都无法匹配到 bean
@Resource
private SmsService smsService;
// 正确注入 SmsServiceImpl1 对象对应的 bean
@Resource
private SmsService smsServiceImpl1;
// 正确注入 SmsServiceImpl1 对象对应的 bean(比较推荐这种方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

Bean

Bean 是线程安全的吗?

prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个 bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。不过,大部分 Bean 实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。
对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:

  1. 在 Bean 中尽量避免定义可变的成员变量。
  2. 在类中定义一个 ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)

Bean的作用域

Spring 中 Bean 的作用域通常有下面几种:

  1. singleton : IoC 容器中只有唯一的 bean 实例。Spring 中的 bean 默认都是单例的,是对单例设计模式的应用。
  2. prototype : 每次获取都会创建一个新的 bean 实例。也就是说,连续 getBean() 两次,得到的是不同的 Bean 实例。
  3. request (仅 Web 应用可用): 每一次 HTTP 请求都会产生一个新的 bean(请求 bean),该 bean 仅在当前 HTTP request 内有效。
  4. session (仅 Web 应用可用) : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean(会话 bean),该 bean 仅在当前 HTTP session 内有效。

如何配置 bean 的作用域呢?

xml 方式:

<bean id="..." class="..." scope="singleton"></bean>

注解方式:@Bean

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

Bean 的生命周期了解么?

  1. 创建 Bean 的实例:Bean 容器首先会找到配置文件中的 Bean 定义,然后使用 Java 反射 API 来创建 Bean 的实例。
  2. Bean 属性赋值/填充:为 Bean 设置相关属性和依赖,例如@Autowired 等注解注入的对象、@Value 注入的值、setter方法或构造函数注入依赖和值、@Resource注入的各种资源。
  3. Bean 初始化:
    如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入 Bean 的名字。
    如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入 ClassLoader对象的实例。
    如果 Bean 实现了 BeanFactoryAware 接口,调用 setBeanFactory()方法,传入 BeanFactory对象的实例。
    与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
    如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessBeforeInitialization() 方法
    如果 Bean 实现了InitializingBean接口,执行afterPropertiesSet()方法。
    如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
    如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor 对象,执行postProcessAfterInitialization() 方法。
  4. 销毁 Bean:销毁并不是说要立马把 Bean 给销毁掉,而是把 Bean 的销毁方法先记录下来,将来需要销毁 Bean 或者销毁容器的时候,就调用这些方法去释放 Bean 所持有的资源。
    如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。
    如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的 Bean 销毁方法。
    也可以直接通过@PreDestroy 注解标记 Bean 销毁之前执行的方法。

如何记忆呢?

  • 整体上可以简单分为四步:实例化 —> 属性赋值 —> 初始化 —> 销毁。
  • 初始化这一步涉及到的步骤比较多,包含 Aware 接口的依赖注入、BeanPostProcessor 在初始化前后的处理以及 InitializingBean 和 init-method 的初始化操作。
  • 销毁这一步会注册相关销毁回调接口,最后通过DisposableBean 和 destory-method 进行销毁。

最后,再分享一张清晰的图解(图源:如何记忆 Spring Bean 的生命周期open in new window)。

在这里插入图片描述

1. Bean 实例化

一旦Spring容器获得Bean的定义,它会使用Java反射API来实例化Bean。Java反射API允许在运行时检查类、方法和字段,并在需要时动态创建对象实例。通常情况下,Spring会调用Bean类的默认构造函数来创建实例,但也支持使用工厂方法或者其他创建方式。

当Spring容器实例化Bean时,它可以根据配置使用不同的创建方式。以下是一个示例:

假设我们有一个简单的Java类 UserService,它有一个默认的无参构造函数和一个带参数的构造函数,如下所示:

public class UserService {
    private String serviceName;

    // 默认无参构造函数
    public UserService() {
        this.serviceName = "DefaultService";
    }

    // 带参数的构造函数
    public UserService(String serviceName) {
        this.serviceName = serviceName;
    }

    // 其他方法
    public void execute() {
        System.out.println("Executing service: " + serviceName);
    }
}

现在,我们可以通过XML配置文件来告诉Spring如何实例化 UserService 类的Bean。以下是一个简化的XML配置示例 beans.xml:

<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 -->
    <bean id="userServiceDefault" class="com.example.UserService" />

    <!-- 定义一个使用带参数构造函数实例化的Bean -->
    <bean id="userServiceCustom" class="com.example.UserService">
        <constructor-arg value="CustomService" />
    </bean>

</beans>
2. 属性赋值

通过一个简单的例子来说明如何使用Spring Framework进行属性赋值和依赖注入。

假设我们有一个简单的Java类和它对应的Spring配置,演示如何使用populateBean方法进行属性填充和依赖注入。

  1. 创建一个普通的Java类
    假设我们有一个简单的服务类 UserService,它依赖于 UserRepository。
public class UserService {

    private UserRepository userRepository;
    private String serviceName;

    // 构造函数注入
    public UserService(UserRepository userRepository, String serviceName) {
        this.userRepository = userRepository;
        this.serviceName = serviceName;
    }

    // 方法
    public void processUser(String username) {
        userRepository.save(username);
        System.out.println(serviceName + " saved user: " + username);
    }
}
  1. 创建 UserRepository 接口和实现类
public interface UserRepository {
    void save(String username);
}

@Component
public class UserRepositoryImpl implements UserRepository {
    @Override
    public void save(String username) {
        // 实现保存逻辑
        System.out.println("Saving user: " + username);
    }
}
  1. 配置 Spring Bean
    使用 XML 配置文件或者注解方式配置 Spring Bean,示例中使用注解方式配置:
@Configuration
@ComponentScan(basePackages = "com.example.services")
public class AppConfig {

    @Bean
    public UserService userService() {
        return new UserService(userRepository(), "User Processing Service");
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepositoryImpl();
    }
}
  1. 使用 populateBean 方法进行属性填充
    假设在某个Spring管理的容器中,我们希望手动进行属性填充,可以使用 populateBean 方法。以下是一个伪代码示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainApp {

    public static void main(String[] args) {
        // 创建一个 ApplicationContext
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 获取 DefaultListableBeanFactory,它是 BeanDefinition 的管理器
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        // 假设我们要手动填充属性给 UserService
        UserService userService = new UserService(null, null); // 创建一个空的 UserService 实例

        // 获取 UserService 对应的 BeanDefinition
        BeanDefinition userServiceBeanDefinition = beanFactory.getBeanDefinition("userService");

        // 使用 populateBean 方法填充属性
        // 假设第二个参数 mbd 是 UserService 对应的 BeanDefinition
        // 假设第三个参数 instanceWrapper 是 UserService 实例的一个包装器
        populateBean("userService", userServiceBeanDefinition, new InstanceWrapper(userService));

        // 现在 userService 应该已经填充了属性
        userService.processUser("John");

        // 关闭 Spring 应用上下文
        context.close();
    }

    // 伪代码,模拟 populateBean 方法
    private static void populateBean(String beanName, BeanDefinition mbd, InstanceWrapper instanceWrapper) {
        // 实际的 populateBean 方法会根据 mbd 的配置来设置实例的属性和依赖
        // 这里只是演示逻辑,实际情况下 Spring 负责这个方法的实现
        UserService userService = (UserService) instanceWrapper.getWrappedInstance();
        UserRepository userRepository = new UserRepositoryImpl(); // 模拟从容器中获取依赖
        String serviceName = "User Processing Service"; // 模拟从容器中获取属性值
        userService.setUserRepository(userRepository);
        userService.setServiceName(serviceName);
    }

    // InstanceWrapper 类的伪代码
    static class InstanceWrapper {
        private Object wrappedInstance;

        public InstanceWrapper(Object wrappedInstance) {
            this.wrappedInstance = wrappedInstance;
        }

        public Object getWrappedInstance() {
            return wrappedInstance;
        }
    }
}

解释示例:
UserService 类是一个简单的服务类,依赖于 UserRepository 和 serviceName 属性。
UserRepository 接口和 UserRepositoryImpl 类是具体的数据访问对象,实现了用户保存的逻辑。
AppConfig 类使用了 Spring 的 Java 配置方式,配置了 UserService 和 UserRepository 的 Bean。
在 MainApp 中,我们模拟了使用 populateBean 方法手动填充 UserService 的属性。实际上,Spring 在初始化时会自动完成这些操作,我们这里仅作示例。
在实际的应用中,Spring Framework会自动管理 Bean 的依赖关系和属性赋值,开发者一般不需要手动操作 populateBean 方法。

3. 初始化

在 Spring Framework 中,如果一个 Bean 实现了特定的 Aware 接口,Spring 容器在将这些 Bean 实例化并进行依赖注入时,会调用相应的 Aware 接口方法,以便将容器的信息传递给 Bean。这些 Aware 接口包括:

BeanNameAware 接口:
如果 Bean 实现了 BeanNameAware 接口,Spring 在将 Bean 实例化并设置 Bean 名称后,会调用其 setBeanName(String name) 方法,将 Bean 的名字作为参数传递给该方法。

import org.springframework.beans.factory.BeanNameAware;

public class MyBean implements BeanNameAware {
    
    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("Bean name is: " + name);
    }

    // Other methods
}

BeanClassLoaderAware 接口:
如果 Bean 实现了 BeanClassLoaderAware 接口,Spring 在实例化 Bean 并设置其类加载器后,会调用其 setBeanClassLoader(ClassLoader classLoader) 方法,将 ClassLoader 对象的实例作为参数传递给该方法。

import org.springframework.beans.factory.BeanClassLoaderAware;

public class MyBean implements BeanClassLoaderAware {
    
    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        System.out.println("Bean's ClassLoader is: " + classLoader);
    }

    // Other methods
}

BeanFactoryAware 接口:
如果 Bean 实现了 BeanFactoryAware 接口,Spring 在实例化 Bean 并将其注册到 BeanFactory 后,会调用其 setBeanFactory(BeanFactory beanFactory) 方法,将 BeanFactory 的实例作为参数传递给该方法。

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;

public class MyBean implements BeanFactoryAware {
    
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        System.out.println("BeanFactory instance: " + beanFactory);
    }

    // Other methods
}

这些 Aware 接口方法允许 Bean 在实例化后获取 Spring 容器的一些信息,例如 Bean 的名称、类加载器或者容器本身,从而可以根据这些信息执行特定的操作或者配置。

4. Bean 销毁

在 Spring Framework 中,Bean 的销毁过程可以通过以下几个例子来说明:

使用 @PreDestroy 注解:

import javax.annotation.PreDestroy;
public class MyBean {
    @PreDestroy
    public void preDestroy() {
        // 执行销毁前的操作,比如释放资源
        System.out.println("Bean is being destroyed. Performing cleanup...");
    }
}

在这个例子中,preDestroy() 方法使用了 @PreDestroy 注解,Spring 容器在销毁该 Bean 之前会自动调用这个方法。你可以在这个方法中执行任何你需要在 Bean 销毁前进行的清理操作,例如释放资源或关闭连接。

配置文件中指定 destroy-method:

<bean id="myBean" class="com.example.MyBean" destroy-method="cleanup">
    <!-- 其他配置 -->
</bean>
public class MyBean {

    public void cleanup() {
        // 执行销毁前的操作
        System.out.println("Bean is being destroyed. Performing cleanup...");
    }
}

在这个例子中,配置文件指定了 destroy-method 为 cleanup,表示在 Spring 容器销毁 myBean Bean 时,会调用 cleanup() 方法执行清理操作。

实现 DisposableBean 接口:

import org.springframework.beans.factory.DisposableBean;
public class MyBean implements DisposableBean {

    @Override
    public void destroy() throws Exception {
        // 执行销毁前的操作
        System.out.println("Bean is being destroyed. Performing cleanup...");
    }
}

在这个例子中,MyBean 类实现了 DisposableBean 接口,必须实现 destroy() 方法。Spring 容器在销毁该 Bean 时会调用 destroy() 方法执行清理逻辑。

这些例子展示了在 Spring 中如何通过不同的方式来定义和执行 Bean 的销毁过程。每种方式都有其特定的用途和适用场景,你可以根据具体需求选择最合适的方式来管理 Bean 的生命周期。

结合代码理解Bean的生命周期

// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
    throws BeanCreationException {

    // 1. 实例化
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    
    Object exposedObject = bean;
    try {
        // 2. 属性赋值
        populateBean(beanName, mbd, instanceWrapper);
        // 3. 初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

    // 4. 销毁-注册回调接口
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }

    return exposedObject;
}
1. Bean 实例化
2. Bean 属性赋值
3. Bean 初始化
// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 3. 检查 Aware 相关接口并设置相关依赖,允许bean意识到其所在的环境。
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    // 4. BeanPostProcessor 前置处理
    // 在初始化bean (wrappedBean) 之前,先应用注册的所有BeanPostProcessor实例。调用applyBeanPostProcessorsBeforeInitialization方法来对bean进行定制化处理。
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    // 5. 若实现 InitializingBean 接口,调用 afterPropertiesSet() 方法
    // 6. 若配置自定义的 init-method方法,则执行
    // 这部分负责bean的初始化:如果bean实现了InitializingBean接口,会调用afterPropertiesSet()方法来完成初始化。
    // 如果配置了自定义的初始化方法(例如XML配置中的init-method或者使用@PostConstruct注解的方法),会通过invokeInitMethods方法来执行。
    // 如果在初始化过程中发生异常,会捕获并封装成BeanCreationException,提供详细的错误信息。
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    // 7. BeanPostProceesor 后置处理
    // 在完成初始化后,再次应用BeanPostProcessor实例 (applyBeanPostProcessorsAfterInitialization)来对bean进行最终的定制化处理。
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
4. Bean 销毁-注册回调接口
// DisposableBeanAdapter.java
public void destroy() {
    // 9. 若实现 DisposableBean 接口,则执行 destory()方法
    if (this.invokeDisposableBean) {
        try {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((DisposableBean) this.bean).destroy();
                    return null;
                }, this.acc);
            }
            else {
                ((DisposableBean) this.bean).destroy();
            }
        }
    }
    
	// 10. 若配置自定义的 detory-method 方法,则执行
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
        if (methodToInvoke != null) {
            invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
        }
    }
}

AOP

什么是AOP
如何使用AOP
手写事务管理器学习AOP的使用
0. 不使用AOP的思想

applicationContext.xml

<beans>
	<bean id="UserService" class="service.impl.UserServiceImpl"></bean>
</beans>

事务管理器
TranscationManagerHandler

public class TransactionManagerHandler {
	public void begin(){
		System.out.println("开启事务");
	}
	
	public void commit(){
		System.out.println("提交事务");
	}
	
	public void rollback(){
		System.out.println("回滚事务");
	}
	
	public void closeSession(){
		System.out.println("关闭连接");
	}
}

如果不用Aop的思想,想使用事务管理器的代码如下,try catch代码和我们的业务没有关系,耦合性严重。
引入Aop的概念:aop能够保证我们给某个方法添加事务无需改动方法内的代码。而且可以只写一次,之后进行配置便可以指定给哪个方法加事务管理器

UserServiceImpl

public class UserServiceImpl implements UserService {
	private TransactionManagerHandler tx = new TransactionManagerHandler();
	public void insert(){
		try {
			tx.begin();
			System.out.println("调用dao层insert()");
		}
		catch (Exception e){
			tx.rollback();
		}
		finally {
			tx.closeSession();
		}
	}
}
1.使用配置类AOP的思想代码如下:

UserServiceImpl

public class UserServiceImpl implements UserService {
	public void insert(){
		System.out.println("调用dao层insert()");
	}
}

applicationContext.xml

<beans>
	<bean id="UserService" class="service.impl.UserServiceImpl"></bean>
	<bean id="TranctionManager" class="service.TranctionManagerHandler"></bean>
	
	<aop:config>
		<aop:aspect ref="TranctionManager">
			<aop:pointcut id="pt" expression="execution(public void service.impl.UserServiceImpl.insert())"></aop:pointcut>
			<aop:before method="begin" pointcut-ref="pt"/>
			<aop:after-returning method="commit" pointcut-ref="pt" />
			<aop:after-throwing methhod="rollback" pointcut-ref="pt"/>
		</aop:aspect>
	</aop:config>
</beans>
  1. 切面: 拦截处理类,这里就是事务管理器类 <apo:aspect
  2. 切入点:拦截规则 就绪、后置、异常、最终 <PointCut
  3. 连接点:具体被拦截到的方法,这里就是insert方法
  4. 通知:在不同场景下拦截的功能 <before,after
  5. 织入:由spring完成,切面

另一种解释:
目标(Target):被通知的对象
代理(Proxy):项目表对象应用通知之后创建的代理对象
连接点(JoinPoint):目标对象的所属类中,定义的所有方法均为连接点
切入点(PointCut):被切面拦截/增强的连接点(切入点一定是连接点,连接点不一定是切入点)
通知(Advice):增强的逻辑/代码,也即拦截到目标对象

2.注解开发实现AOP

使用注解开发AOP就可以把xml删除掉了

事务管理器
TranscationManagerHandler

@Controller
@Aspect
public class TransactionManagerHandler {
	
	// 切入点
	@Pointcut("execution(public void service.impl.UserServiceImpl.insert()")
	public void pointcut(){}
	
	@Before("pointcut()")
	public void begin(){System.out.println("开启事务");}
	
	@AfterReturning("pointcut()")
	public void commit(){System.out.println("提交事务");}
	
	@AfterThrowing("pointcut()")
	public void rollback(){System.out.println("回滚事务");}
	
	@After("pointcut()")
	public void closeSession(){System.out.println("关闭连接");}
}

SpringConfig

@Configuration
@ComponentScan("service")
@EnableAspectJAutoProxy
public class SpringConfig{
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
	private TransactionManagerHandler tx = new TransactionManagerHandler();
	public void insert(){
		System.out.println("调用dao层insert()");
	}
}
AOP的底层原理