Spring面试题

server/2024/10/18 12:34:06/

文章目录

    • 注解
      • @Component与@Bean的区别
      • @Autowired 和 @Resource 的区别是什么?
      • 使用方式:
    • Bean
      • Bean 是线程安全的吗?
      • Bean的作用域
      • Bean 的生命周期了解么?
        • 1. Bean 实例化
        • 2. 属性赋值
        • 3. 初始化
        • 4. Bean 销毁
      • 结合代码理解Bean的生命周期
        • 1. Bean 实例化
        • 2. Bean 属性赋值
        • 3. Bean 初始化
        • 4. Bean 销毁-注册回调接口
      • AOP
        • 什么是AOP
        • 如何使用AOP
          • 手写事务管理器学习AOP的使用
            • 0. 不使用AOP的思想
          • 1.使用配置类AOP的思想代码如下:
          • 2.注解开发实现AOP
        • AOP的底层原理

注解

@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注解的使用示例:

java">@Configuration
public class AppConfig {@Beanpublic TransferService transferService() {return new TransferServiceImpl();}}

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

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

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

java">@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 容器所管理。

java">// 报错,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

java">@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,它有一个默认的无参构造函数和一个带参数的构造函数,如下所示:

java">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/beanshttp://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。
java">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 接口和实现类
java">public interface UserRepository {void save(String username);
}@Component
public class UserRepositoryImpl implements UserRepository {@Overridepublic void save(String username) {// 实现保存逻辑System.out.println("Saving user: " + username);}
}
  1. 配置 Spring Bean
    使用 XML 配置文件或者注解方式配置 Spring Bean,示例中使用注解方式配置:
java">@Configuration
@ComponentScan(basePackages = "com.example.services")
public class AppConfig {@Beanpublic UserService userService() {return new UserService(userRepository(), "User Processing Service");}@Beanpublic UserRepository userRepository() {return new UserRepositoryImpl();}
}
  1. 使用 populateBean 方法进行属性填充
    假设在某个Spring管理的容器中,我们希望手动进行属性填充,可以使用 populateBean 方法。以下是一个伪代码示例:
java">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) {// 创建一个 ApplicationContextAnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);// 获取 DefaultListableBeanFactory,它是 BeanDefinition 的管理器DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();// 假设我们要手动填充属性给 UserServiceUserService userService = new UserService(null, null); // 创建一个空的 UserService 实例// 获取 UserService 对应的 BeanDefinitionBeanDefinition 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 的名字作为参数传递给该方法。

java">import org.springframework.beans.factory.BeanNameAware;public class MyBean implements BeanNameAware {private String beanName;@Overridepublic 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 对象的实例作为参数传递给该方法。

java">import org.springframework.beans.factory.BeanClassLoaderAware;public class MyBean implements BeanClassLoaderAware {private ClassLoader classLoader;@Overridepublic 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 的实例作为参数传递给该方法。

java">import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;public class MyBean implements BeanFactoryAware {private BeanFactory beanFactory;@Overridepublic 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 注解:

java">import javax.annotation.PreDestroy;
public class MyBean {@PreDestroypublic 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>
java">public class MyBean {public void cleanup() {// 执行销毁前的操作System.out.println("Bean is being destroyed. Performing cleanup...");}
}

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

实现 DisposableBean 接口:

java">import org.springframework.beans.factory.DisposableBean;
public class MyBean implements DisposableBean {@Overridepublic void destroy() throws Exception {// 执行销毁前的操作System.out.println("Bean is being destroyed. Performing cleanup...");}
}

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

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

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

java">// 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 初始化
java">// 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 销毁-注册回调接口
java">// 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

java">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

java">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

java">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

java">@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

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

UserServiceImpl

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

http://www.ppmy.cn/server/53721.html

相关文章

DevExpress WPF中文教程:Grid - 如何排序、分组、过滤数据(设计时)?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

恢复策略(上)-撤销事务(UNDO)、重做事务(REDO)

一、引言 利用前面所建立的冗余数据&#xff0c;即日志和数据库备份&#xff0c;要将数据库从一个不一致的错误状态恢复到一个一致性状态&#xff0c;还需要相关的恢复策略&#xff0c;不同DBMS的事务处理机制所采用的缓冲区管理策略可能不同&#xff0c;发生故障后的数据库不…

Unity之Hololens2开发MRTK Profile详解

前言 配置 MRTK 的主要方式之一是使用基础包中的配置文件。 场景中的主要 MixedRealityToolkit 对象具有活动配置文件 - 一个 ScriptableObject。 顶级 MRTK 配置配置文件包含主核心系统的每个核心的子配置文件数据,每个主核心系统都旨在配置其相应子系统的行为。 此外,这些…

一二三应用开发平台应用开发示例(5)——列表视图、树视图、树表视图、树参照视图配置

列表视图 接下来进入列表视图配置&#xff0c;创建的操作方式跟前面相同&#xff0c;如下图所示&#xff1a; 保存后回到列表&#xff0c;点击行记录的配置按钮&#xff0c;进入如下配置页面 可以看到该配置界面相比新增、修改、查看那三个视图要复杂得多&#xff0c;配置项…

MySQL表解锁

查看锁信息 show full processlist 如果一个表被锁定了&#xff0c;会有一个 “Waiting for table metadata lock” 的提示&#xff0c;表明该表正在等待锁定。 解锁表 删除state上有值的事务 kill query 事务id 表解锁完成

微信小程序的跳转页面

在微信小程序中&#xff0c;要实现从当前页面返回到指定页面的功能&#xff0c;通常不直接使用“返回上一页”的逻辑&#xff0c;而是利用小程序的页面栈管理和navigateBack或者重新定向到目标页面的API。下面我将介绍两种主要的方法&#xff1a; 方法一&#xff1a;使用 navi…

求职刷题力扣 DAY38动态规划 part04

1. 1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能…

学习java第一百一十六天

Spring Framework有哪些不同的功能&#xff1f; 答&#xff1a; 轻量级-Spring 在代码量和透明度方面都很轻便。 IOC-控制反转AOP-面向切面编程可以将应用业务逻辑和系统服务分离&#xff0c;以实现高内聚。容器-Spring 负责创建和管理对象&#xff08;Bean&#xff09;的生命周…