Spring5

前置知识

什么是 Spring

Spring 框架是 Java 应用最广的框架,它的成功来源于理念,而不是技术本身,它的理念包括 IoC (Inversion of Control,控制反转) 和 AOP(Aspect Oriented Programming,面向切面编程)。理念:每个bean与bean之间的关系统一交给 Spring IoC 容器管理。

Spring体系结构

  1. Spring Core:主要组件是BeanFactory,创建JavaBean的工厂,使用控制反转(IOC) 模式 将应用程序的配置和依赖性规范与实际的应用程序代码分开。

  2. Spring AOP:集成了面向切面的编程功能(AOP把一个业务流程分成几部分,例如权限检查、业务处理、日志记录,每个部分单独处理,然后把它们组装成完整的业务流程。每个部分被称为切面),可以将声明性事物管理集成到应用程序中。

  3. Spring Cntext:一个核心配置文件,为Spring框架提供上下文信息。

  4. Spring Do:Spring操作数据库的模块。

  5. Spring ORM:Spring集成了各种orm(object relationship mapping 对象关系映射)框架的模块,集成mybatis

  6. Spring Web集成各种优秀的web层框架的模块(Struts、Springmvc)

  7. Spring web MVC:Spring web层框架

思考一个问题 为什么启动SpringBoot项目的时候需要加上Configuration、@ComponentScan

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

看一下 @SpringBootApplication 注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

看一下 @SpringBootConfiguration 注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

@SpringBootApplication 包含 @ComponentScan 和 @SpringBootConfiguration。@SpringBootConfiguration 包含 @Configuration。

@Configuration 是为了注册bean,@CompnentScan 是为了扫描要注册的bean。

快速构建Spring环境

Maven依赖信息

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.0.RELEASE</version>
</dependency>

xml方式环境搭建

配置文件:src\main\resources\applicationContext.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
      ">
    <!-- 容器id class 使用java的反射机制初始化 -->
    <bean id="userEntity" class="com.pjw.entity.UserEntity">
        <!-- 使用set方法进行注入 -->
        <property name="userId" value="10"/>
        <property name="userName" value="pjw"/>
    </bean>
</beans>

UserEntity.java

public class UserEntity {
    private Integer userId;
    private String userName;

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        // 1.读取spring配置文件,创建IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2.从SpringIoc容器获取userEntity
        UserEntity userEntity = (UserEntity) applicationContext.getBean("userEntity");
        System.out.println(userEntity);
    }
}

Spring 中注入的 bean id 重复的话会怎么样

IoC 容器启动的时候会报错

注解方式环境搭建

UserEntity.java

public class UserEntity {
    private Integer userId;
    private String userName;

    public UserEntity(Integer userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                '}';
    }
}

MySpringConfig.java MySpringConfig 这个类也会被注册成为 bean

// @Configuration 等同于配置的spring配置文件
@Configuration
public class MySpringConfig {

    // bean id 为方法名称
    @Bean
    public UserEntity userEntity() {
        return new UserEntity(10, "pjw");
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MySpringConfig.class);
        UserEntity userEntity = applicationContext.getBean("userEntity", UserEntity.class);
        System.out.println(userEntity);
    }
}

@ComponentScan用法

bean id默认为小写开头的类名,可以在@Component注解里指定 bean id

  1. @ComponentScan 会把指定包路径上包含 @Component 注解的类注册成 bean
@Configuration
@ComponentScan("com.pjw")
public class MySpringConfig { }
  1. 指定包路径上包含指定注解的类注册成 bean
@Configuration
@ComponentScan(value = "com.pjw", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)}, useDefaultFilters = false)
public class MySpringConfig {
    @Bean
    public UserEntity userEntity() {
        return new UserEntity(10, "pjw");
    }
}
  1. 指定包路径上排除指定注解,包含 @Component 注解的类注册成 bean
@Configuration
@ComponentScan(value = "com.pjw", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)}, useDefaultFilters = true)
public class MySpringConfig {
    @Bean
    public UserEntity userEntity() {
        return new UserEntity(10, "pjw");
    }
}

FilterType 有五种类型

  • ANNOTATION:注解类型

  • ASSIGNABLE_TYPE:ANNOTATION:指定的类型

  • ASPECTJ:按照Aspectj的表达式,基本上不会用到

  • REGEX:按照正则表达式

  • CUSTOM:自定义规则

获取所有 IoC 容器中的 bean

String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}

bean 的作用域

Spring 中 bean 默认是单例

可以使用 @Scope 来设置 bean 的作用域

@Scope 值 描述
singleton 全局有且仅有一个实例。
prototype 每次获取Bean的时候都会有一个新的实例。
request 针对每次请求都会产生一个新的Bean对象,并且该Bean对象仅在当前Http请求内有效。
session 针对每次请求都会产生一个新的Bean对象,并且该Bean仅在当前Http session内有效。

bean 是什么时候创建的

单例的时候默认是在容器初始化的时候创建的。

可以使用 @Lazy 注解来设置什么时候创建。@Lazy 注解值为 true 的时候是第一次获取的时候创建,false 的话容器初始化的时候创建。

@Lazy 默认值为 true。

多例是每次获取的时候创建的。

bean 的初始化指的是对象已经创建并且里面的所有 set 方法都已经执行完毕了。

销毁 bean

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MySpringConfig.class);
        // 把 bean 的 map 集合清空
        applicationContext.close();
    }
}

bean 初始化完成之后、销毁之后执行的方法

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
    private Integer userId;
    private String userName;

    private void myInitMethod() {
        System.out.println("myInitMethod");
    }

    private void myDestroyMethod() {
        System.out.println("myDestroyMethod");
    }
}
@Configuration
public class MySpringConfig {
    @Bean(initMethod = "myInitMethod",destroyMethod = "myDestroyMethod")
    public UserEntity userEntity(){
        return new UserEntity(19,"pjw");
    }
}

或者

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity implements InitializingBean, DisposableBean {
    private Integer userId;
    private String userName;


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化完成");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("销毁完成");
    }
}
@Configuration
public class MySpringConfig {
    @Bean
    public UserEntity userEntity(){
        return new UserEntity(19,"pjw");
    }
}

或者

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
    private Integer userId;
    private String userName;

    @PostConstruct
    public void chushihuawancheng() throws Exception {
        System.out.println("初始化完成");
    }

    @PreDestroy
    public void xiaohuiwancheng() throws Exception {
        System.out.println("销毁完成");
    }
}
@Configuration
public class MySpringConfig {
    @Bean
    public UserEntity userEntity(){
        return new UserEntity(19,"pjw");
    }
}

通过 ApplicationContextAware 获取 ApplicationContext

@Component
public class MyApplication implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        UserEntity userEntity = (UserEntity)applicationContext.getBean("userEntity");
        System.out.println(userEntity);
    }
}

生命周期中 aware 依赖

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware {
    private Integer userId;
    private String userName;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println(classLoader);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println(beanFactory);
    }

    @Override
    public void setBeanName(String name) {
        System.out.println(name);
    }
}

BeanPostProcessor 接口

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    // 每个 bean 执行自定义 init 方法之前处理
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean);
        System.out.println(beanName);
        return null;
    }

    // 每个 bean 执行自定义 init 方法之后处理
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean);
        System.out.println(beanName);
        retur
            n null;
    }
}

@Condition 根据条件判断注册 bean

public class MyCondition implements Condition{

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // 获取当前环境配置
        Environment environment = conditionContext.getEnvironment();
        String osName = environment.getProperty("os.name");
        if (osName.equals("Windows 7")) {
            // 返回true 就是能够创建该bean
            return false;
        }
        return false;
    }
}
@Configuration
public class MySpringConfig {
    @Bean
    @Conditional(MyCondition.class)
    public UserEntity userEntity() {
        return new UserEntity(10, "pjw");
    }
}

基于 @Import 注解注册 bean

bean id 为类的全路径名称

@Bean和@Import都是引入外部的Jar包

@Configuration
@Import(UserEntity.class)
public class MySpringConfig {
}

@EnableXXX 功能性注解实现原理

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({PayEntity.class, MyPayEntity.class})
public @interface EnablePayEntity {
    // 只要启动的时候 加入该EnablePayEntity  就会将PayEntity,MyPayEntity实体类注入到spring ioc容器
    // Enable注解的话 底层 实际上在调用 @Import({PayEntity.class, MyPayEntity.class})
}
@Configuration
@EnablePayEntity
public class MyConfig {
}

基于 ImportSelector 接口注册 bean

public class MyImportSelector implements ImportSelector {
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.pjw.MemberEntity", "com.pjw.MsEntity"};
    }
}
@Configuration
@Import(MyImportSelector.class)
public class MyConfig {
}

基于 ImportBeanDefinitionRegistrar 接口注册 bean

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        // 手动注册到ioc容器中
        // spring容器中 bean 的信息是用 BeanDefinition 来描述
        BeanDefinition beanDefinition = new RootBeanDefinition(SmsEntity.class);
        // smsEntity 是 bean id
        beanDefinitionRegistry.registerBeanDefinition("smsEntity", beanDefinition);
    }
}
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class MyConfig {
}

基于 FactoryBean 接口注册 bean

// 要注册的类为 MyEntity
public class MyFactoryBean implements FactoryBean<MyEntity> {
    @Override
    public MyEntity getObject() throws Exception {
        return new MyEntity();
    }

    @Override
    public Class<?> getObjectType() {
        return MyEntity.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
@Configuration
@Import(MyFactoryBean.class)
public class MyConfig {
}

@Autowired 和 @Resource

@Autowired 和 @Resource 找 bean 的时候根据条件找到的bean必须是唯一的

@Autowired为Spring提供的注解,只按照类型查找。

@Resource为J2EE提供的注解。在该注解中:

  1. 如果同时指定了name和type,找name和type同时匹配的,找不到则抛出异常。

  2. 如果指定了name,找name匹配的,找不到则抛出异常。

  3. 如果指定了type,找type匹配的,找不到或是找到多个,都会抛出异常。

  4. 如果既没有指定name,又没有指定type,先找name匹配的,此时name是字段属性名;如果没有匹配,找type匹配的。

找name是匹配@Resource里的name属性值和bean的id。

@Primary 和 @Qualifier

public interface Animal {
    void sayHi();
}
public class Cat implements Animal{
    @Override
    public void sayHi() {
        System.out.println("cat say hi");
    }
}
public class Dog  implements Animal{
    @Override
    public void sayHi() {
        System.out.println("dog say hi");
    }
}
@RestController
public class MyController {
    @Autowired
    private Animal animal;
}

假设一个接口有两个实现类,使用 @Autowired 的话会报错。@Autowired 是按照类型查找的。

这时候有两个方案:

  1. 在 @Autowired 下面或者上面使用 @Qualifier 注解来指定 bean id

  2. 在 Dog 类 或者 Cat 类上面写 @Primary,表示优先使用这个 bean

java 子父类中的构造函数

在对子类对象进行初始化时,父类的构造函数也会运行,

那是因为子类的构造函数默认的第一行有一条隐式的语句: super();

super():会访问父类中的空参数构造函数。

而且子类中所有的构造函数默认的第一行都是:super();

为什么子类一定要访问父类中的构造函数?

因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。

所以子类在对象初始化时,要先访问一下父类中的构造函数。

如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。

super语句一定定义在子类构造函数的第一行。

子类中所有的构造函数,默认都会访问父类中空参数的构造函数。

因为子类每一个构造函数内的第一行都有一句隐式的super();

当父类中没有空参数的构造函数时,子类必须手动通过super或者this语句形式来指定要访问父类中的构造函数。

子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。子类中至少会有一个构造函数会访问父类中的构造函数

Spring Bean 的生命周期

BeanFactory 和 ApplicationContext

Spring 有两个核心接口:BeanFactory 和 ApplicationContext,其中 ApplicationContext 是 BeanFactory 的子接口。他们都可代表 Spring 容器,Spring 容器是生成 Bean 实例的工厂,并且管理容器中的 Bean。

BeanFactory 负责读取 bean 配置文档,管理 bean 的加载,实例化,维护 bean 之间的依赖关系,负责 bean 的生命周期。

ApplicationContext 除了提供上述 BeanFactory 所能提供的功能之外,还提供了更完整的框架功能:

a:国际化支持

b:资源访问

c:事件传递:通过实现 ApplicationContextAware 接口

常用的获取 ApplicationContext 的方法:

a:FileSystemXmlApplicationContext:从文件系统或 url 指定的 xml 配置文件创建,参数为配置文件名或文件名数组

b:ClassPathXmlApplicationContext:从 classpath 的 xml 配置文件创建,可以从 jar 包中读取配置文件

c:WebApplicationContextUtils:从 web 应用的根目录读取配置文件,需要先在 web.xml 中配置,可以配置监听器或者 servlet 来时间

d:AnnotationConfigApplicationContext:是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置。

bean 生命周期

这里有图片

aop

什么是 spring aop

spring的aop可以在方法的前后实现增强。aop可以解决我们在程序上的代码冗余问题。

spring aop有哪些应用场景

日志、事务、安全控制、计算方法耗时时间

spring aop的原理

前置通知、后置通知、环绕通知、运行通知、异常通知

aop 的原理

java动态代理模式

maven依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.3</version>
    <scope>compile</scope>
</dependency>

aop 五种通知的实现方式

@EnableAspectJAutoProxy
@Configuration
public class MySpringConfig {

}
@Aspect
@Component
public class LogAop {

    // 定义切入点:Pointcut 开始方法拦截入口
    @Pointcut("execution(* com.pjw..*.*(..))")
    public void logAop() {

    }

    @Before("logAop()")
    public void doBefore(){
        System.out.println("前置通知 >>> 调用方法之前拦截");
    }


    @After("logAop()")
    public void doAfter(){
        System.out.println("后置通知 >>> 调用方法之后拦截");
    }

    // 后置通知 执行之后 执行运行通知
    @AfterReturning("logAop()")
    public void doAfterReturning(){
        System.out.println("运行通知执行");
    }

    // 异常通知的时候运行通知不会执行,后置通知 执行之后 执行异常通知
    @AfterThrowing("logAop()")
    public void doAfterThrowing(){
        System.out.println("异常通知执行");
    }

    @Around("logAop()")
    public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知在方法之前执行");
        joinPoint.proceed(); // 执行我们目标方法
        System.out.println("环绕通知在方法之后执行");
    }
}
@Component
public class OrderService {
    public void addOrder(){
        System.out.println(111);
    }
}
结果:
环绕通知在方法之前执行
前置通知 >>> 调用方法之前拦截
111
环绕通知在方法之后执行
后置通知 >>> 调用方法之后拦截
运行通知执行

手动实现AOP责任链

// 能够把通知链形成关联
public interface MethodInvocation {
    void process() throws InvocationTargetException, IllegalAccessException;
}

public class DefaultMethodInvacation implements MethodInvocation {
    private List<MethodInterceptor> listMethodInterceptor; // 存放所有的通知
    private Object target;// 目标对象
    private Method method;// 目标方法
    private Object args[];// 目标参数
    // 最终使用反射机制执行目标方法
    private int index;// 记录当前链调用的位置

    public DefaultMethodInvacation(List<MethodInterceptor> listMethodInterceptor, Object target, Method method, Object[] args) {
        this.listMethodInterceptor = listMethodInterceptor;
        this.target = target;
        this.method = method;
        this.args = args;
    }

    public DefaultMethodInvacation(List<MethodInterceptor> listMethodInterceptor) {
        this.listMethodInterceptor = listMethodInterceptor;
    }

    // 调用链形成
    public void process() throws InvocationTargetException, IllegalAccessException {
        if (index == listMethodInterceptor.size()) {
            method.invoke(target, args); //  执行目标
            return;
        }
        MethodInterceptor methodInterceptor = listMethodInterceptor.get(index++);
        methodInterceptor.invoke(this);
    }
}
public interface MethodInterceptor {
    //定义共同通知骨架
    void invoke(MethodInvocation methodInvocation) throws InvocationTargetException, IllegalAccessException;
}

public class BeforMethodInterceptor implements MethodInterceptor {
    public void invoke(MethodInvocation methodInvocation) throws InvocationTargetException, IllegalAccessException {
        System.out.println(">>>>前置通知<<<<");
        // 执行我们的目标方法
        methodInvocation.process();// 递归调用
    }
}

public class AfterMethodInterceptor implements MethodInterceptor {
    public void invoke(MethodInvocation methodInvocation) throws InvocationTargetException, IllegalAccessException {
        // 执行我们的前置通知
        methodInvocation.process();
        System.out.println(">>>后置通知<<<");
    }
}

public class AroundMethodInterceptor implements MethodInterceptor {
    public void invoke(MethodInvocation methodInvocation) throws InvocationTargetException, IllegalAccessException {
        System.out.println("环绕通知在目标方法之前执行..");
        methodInvocation.process();
        System.out.println("环绕通知在目标方法之后执行..");
    }
}
public class UserService {
    public void login(String userName, Integer age) {
        System.out.println("userName:" + userName + ",age:" + age);
    }
}
public class Test001 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 1.装配所有的通知存放到集合中
        ArrayList<MethodInterceptor> methodInterceptors = new ArrayList<MethodInterceptor>();
        methodInterceptors.add(new BeforMethodInterceptor());
        methodInterceptors.add(new AfterMethodInterceptor());
        methodInterceptors.add(new AroundMethodInterceptor());
        // 2.创建我们的目标方法
        UserService userService = new UserService();
        Method loginMethod = UserService.class.getMethod("login", String.class, Integer.class);
        Object[] objects = {"mayikt", 12};
        new DefaultMethodInvacation(methodInterceptors, userService, loginMethod, objects).process();
        // SpringAop底层 所有的通知最终使用递归算法调用+责任脸设计模式
    }
}

aop与事务

手动的事务实现方式

@Component
public class TransactionalUtils {
    // 获取当前事务管理器
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        System.out.println("获取当前的事务>>>>>");
        return transaction;
    }

    // 提交事务
    public void commit(TransactionStatus transactionStatus) {
        System.out.println("提交当前的事务>>>>>");
        dataSourceTransactionManager.commit(transactionStatus);
    }

    // 回滚
    public void rollback(TransactionStatus transactionStatus) {
        System.out.println("回滚当前的事务>>>>>");
        dataSourceTransactionManager.rollback(transactionStatus);
    }
}

@Service
public class OrderService {
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private TransactionalUtils transactionalUtils;

    public int addOrderInfo(int j) {
        TransactionStatus begin = transactionalUtils.begin();
        try {
            int i = orderInfoMapper.addOrderInfo();
            int result = 1 / j;
            transactionalUtils.commit(begin);
        } catch (Exception e) {
            e.printStackTrace();
            transactionalUtils.rollback(begin);
        }
        return 1;
    }
}

基于aop封装手动事务

@Aspect
@Component
@Scope("prototype")
public class TransactionalAop {
    @Autowired
    private TransactionalUtils transactionalUtils;

    // 定义切入点
    @Pointcut("execution (* com.mayikt.service..*.*(..))")
    public void transactionalAop() {
    }

    // 环绕通知
    @Around("transactionalAop()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取目标对象
        Class<?> classTarget = joinPoint.getTarget().getClass();
        // 获取目标对象类型
        Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 获取目标对象方法
        Method objMethod = classTarget.getMethod(methodName, par);
        // 判断该目标方法上是否有加上自定义事务注解
        ExtTransactional extTransactional = objMethod.getDeclaredAnnotation(ExtTransactional.class);
        if (extTransactional == null) {
            return joinPoint.proceed();// 执行目标方法
        }
        TransactionStatus begin = transactionalUtils.begin();
        try {
            System.out.println(">>>>环绕通知之前执行...>>>>>>");
            Object proceed = joinPoint.proceed();// 执行目标方案
            System.out.println(">>>>环绕通知之后执行...>>>>>>");
            transactionalUtils.commit(begin);
            return proceed;
        } catch (Exception e) {
            // 目标方法抛出异常的情况下 回滚当前事务
            transactionalUtils.rollback(begin);
            return 0;
        }
        // 思考如何回滚呢? 抛出异常
    }
}

@Service
public class OrderService {
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private TransactionalUtils transactionalUtils;

    @Transactional
    public int addOrderInfo(int j) {
        try {
            int i = orderInfoMapper.addOrderInfo();
            int result = 1 / j;
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            // 手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            // 或者自定义抛出异常
            return 0;
        }
    }
    // 注意的问题 如果在service 层 抛出异常的情况下 最好使用  TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    // 事务原理:当方法执行的时候如果抛出异常的情况 就会回滚当前的事务 如果没有抛出异常 就提交事务
}

基于Spring注解整合JDBC

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.46</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
</dependencies>
public class JdbcTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        OrderService orderService = applicationContext.getBean("orderServiceImpl", OrderService.class);
        orderService.addOrder();
        // 为什么使用事务之后 为什么有把aop给开启呢? 本身就是基于AOP实现的
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }
    }
}

@Configuration
@ComponentScan("com.mayikt")
@EnableTransactionManagement
public class MyConfig {
    // 学习xml ssh spring-jdbc.xml

    //注入到ioc容器中 beanid =dataSource class=DataSource类的完整路径地址
    // 配置我们的数据源
    @Bean
    public DataSource dataSource() {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setUser("root");
        mysqlDataSource.setPassword("root");
        mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8");
        mysqlDataSource.setDatabaseName("test");
        return mysqlDataSource;
    }
    // @EnableTransactionManagement 实际上帮助我们开启了aop

    /**
     * 注入JdbcTemplate
     *
     * @return
     */
    @Bean
    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(dataSource());
    }

    // 多数据源如何实现
    //@EnableTransactionManagement 开启事务注解

    @Bean
    public PlatformTransactionManager platformTransactionManager(){
         return  new DataSourceTransactionManager(dataSource());
    }
    // 完全高仿springboot框架
}

@Repository
public class OrderDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void addOrder() {
        jdbcTemplate.update("insert into order_info values(null,'mayikt','zhangsan','1111')");
    }

}

public interface OrderService {
    void addOrder();
}

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderDao orderDao;

    @Transactional
    public void addOrder() {
        try {
            orderDao.addOrder();
            int i = 1 / 0; // 如果报错的情况下肯定是会插入到数据库中
        } catch (Exception e) {

        }
    }
    // springboot-jdbc
}

循环依赖

循环依赖概念

依赖注入:赋值

@Service
//@Scope("prototype")
public class AService {
    //
    @Autowired
    public BService bService;
    // 为什么Aservice在创建的时候 为什么Bservice比ASERVICE 先创建

    public AService() {
        System.out.println("AService被Java的反射技术创建");
    }

    public void setbService(BService bService) {
        this.bService = bService;
    }

}

@Service
//@Scope("prototype")
public class BService {
    @Autowired
    public AService aService;

    public void setaService(AService aService) {
        this.aService = aService;
    }
}

Spring 中的循环依赖问题在单例的情况下,Spring 是已经帮助我们解决好了。多例情况下,是无法解决循环依赖问题。

解决多例中的循环依赖

public class SpringApp {
    public static void main(String[] args) {
//        1. ioc容器在创建的时候所有的单例对象是不是会被创建
       AnnotationConfigApplicationContext applicationContext =
               new AnnotationConfigApplicationContext(MyConfig.class);
//        // 对例子情况 当你在调用的时候才获取
//        AService aSerivce = applicationContext.getBean("AService", AService.class);
        BService bSerivce = applicationContext.getBean("BService", BService.class);
//        aSerivce.setbService(bSerivce);
//        bSerivce.setaService(aSerivce);
        // 循环引用异常 找不到对象
        /**
         * 思考问题? 如果我们的项目对象必须要是多例? 而且必须要循环引用  明确的指定引用那个对象
         */
//        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
//        for (int i = 0; i < beanDefinitionNames.length; i++) {
//            System.out.println(beanDefinitionNames[i]);
//        }
        /**
         * spring中单例的情况下 实际上已经帮助我们解决好了这样循环依赖问题
         *
         * 分析Spring单例 循环依赖的问题底层是如何解决的?
         * 单例对象在什么时候被创建。 IOC容器被加载的时候创建  多例子的情况下 是在调用的时候创建
         */
        // 缓存我们的婴儿对象
        HashMap<String, Object> stringObjectHashMap = new HashMap<String, Object>();
        stringObjectHashMap.put("aService", new AService());
        stringObjectHashMap.put("bService", new BService());
        // 3.从三级缓存中查询到赋值
        AService aService = (AService) stringObjectHashMap.get("aService");
        BService bService = (BService) stringObjectHashMap.get("bService");
        aService.bService = bService;
        // Java的引用传递
        bService.aService = aService;
    }
}

循环依赖三级缓存

posted @ 2021-12-20 23:27  pakhm  阅读(64)  评论(0)    收藏  举报