Spring5

Spring概述

框架:

  • 高度抽取可重用代码的一种设计;高度的通用性
  • 多个可重用模块的集合,形成一个某个领域的整体解决方案

Spring:

​ 容器框架,可以管理所有组件

  1. Spring是轻量级的开源的JavaEE应用程序框架

  2. Spring有两个核心部分:

  • IOC:控制反转,把创建对象过程交给Spring进行管理
  • AOP:面向切面,不修改源代码进行功能增强
  1. Spring特点
  • 方便解耦,简化开发
  • AOP编程支持
  • 方便程序测试
  • 可以方便和其他框架整合
  • 方便进行事物操作
  • 降低API开发难度(对JDBC等进行封装)

Spring划分图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGXBUqXe-1630320609495)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210813102905000.png)]

Test:Spring的单元测试模块

Core Container:核心容器(IOC);黑色部分代表这部分的功能由哪些jar包组成,要使用这部分完整的功能,这些jar包都需要导入。

AOP+Aspects:面向切面编程

Data Access:数据访问

web:Spring开发web应用模块

IOC容器

IOC简介

  1. 什么是IOC?

    Inversion Of Control

    • 控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理
      • 资源的主动创建变为被动获取,以前是自己new对象,现在所有的对象交给容器创建管理
    • 使用IOC的目的:为了降低耦合(工厂模式)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5oS8AAv-1630320609497)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210810111801590.png)]

  2. IOC底层原理

    IOC底层原理主要包括三部分:xml解析、工厂模式、反射

    IOC过程:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt1Gnfz7-1630320609498)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210810114850186.png)]

DI==(Dependency Injection)依赖注入==:容器能知道哪个组件(类)运行的时候,需要另一个类(组件);容器通过反射的形式,将容器中准备好的BookService对象注入(利用反射给属性赋值)到BookServlet中。

ApplicationContext代表IOC容器

IOC接口(BeanFactory)

  1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  2. Spring提供IOC容器实现两种方式(两个接口)
    • BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员使用
      • 加载配置文件的时候不会创建对象,在获取(使用)对象的时候才创建
    • ApplicationContext:BeanFactory接口的子接口,提供更多功能,由开发人员使用
      • 加载配置文件的时候,就会把在配置文件对象创建。

IOC操作Bean管理(xml)

什么是Bean管理

  • Spring创建对象
  • Spring注入属性
  1. 基于xml方式创建对象:
<bean id="user" class="com.spring5.User"></bean>

​ ①在Spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建

​ ②bean标签有很多属性,常用属性有:

​ id属性:唯一标识

​ class属性:类全路径

​ ③创建对象时候,默认也是执行无参数构造方法。

  1. 基于xml方式注入属性

    ①DI:依赖注入,就是注入属性

    在Spring配置文件配置对象创建,配置属性注入

    <!--    set方法注入属性-->
        <bean id="book" class="com.spring5.Book">
    <!--        使用property完成属性注入
                name:属性名称,Bean里面必须有setxxx方法
                value:向属性注入的值
    -->
            <property name="bookName" value="从入门到入土"></property>
            <property name="bookAuthor" value="康师傅"></property>
        </bean>
    
    <!--        有参构造注入属性
                name:有参构造的参数名称
    -->
        <bean id="order" class="com.spring5.Order">
            <constructor-arg name="orderName" value="Y7000"></constructor-arg>
            <constructor-arg name="address" value="China"></constructor-arg>
        </bean>
    
  2. 注入属性-----null值和特殊符号

    ​ null值的注入:

    <property name="address" >
        <null/>
    </property>
    

    ​ 特殊符号的注入:

    属性值包含特殊字符:
            1.把<>进行转义,&lt;&gt
            2.把带特殊符号内容写到CDATA
    -->
            <property name="address">
                <value><![CDATA[
                   <<深空彼岸>>
                ]]></value>
            </property>
    
  3. 注入属性-----外部Bean

    Service层调用Dao层:

    <!--service和dao对象创建-->
        <bean id="userService" class="com.spring5.service.UserService">
    <!--        注入userDao对象-->
            <property name="userDao" ref="userDaoImpl"></property>
        </bean>
        
        <bean id="userDaoImpl" class="com.spring5.dao.UserDaoImpl"></bean>
    
  4. 注入属性-----内部Bean和级联赋值

    内部bean:

    <!--   内部bean-->
        <bean id="emp" class="com.spring5.bean.Emp">
    <!--        设置两个普通属性-->
            <property name="eName" value="lucy"></property>
            <property name="gender" value=""></property>
    <!--        设置对象类型属性-->
            <property name="dept">
                <bean id="dept" class="com.spring5.bean.Dept">
                    <property name="dName" value="安保部"></property>
                </bean>
            </property>
        </bean>
    

    级联赋值:

    <bean id="emp" class="com.spring5.bean.Emp">
            <!--        设置两个普通属性-->
            <property name="eName" value="lucy"></property>
            <property name="gender" value=""></property>
    <!--        级联赋值-->
            <property name="dept" ref="dept"></property>
        <property name="dept.dName" value="法务部"></property>
        </bean>
        <bean class="com.spring5.bean.Dept" id="dept">
            <property name="dName" value="财务部"></property>
        </bean>
    
  5. 注入属性-----集合

    <!--    集合类型注入 -->
        <bean id="student" class="com.spring5.collection.Student">
    <!--        数组类型属性注入-->
            <property name="courses">
                <array>
                    <value>java</value>
                    <value>数据库</value>
                    <value>C++</value>
                </array>
            </property>
    <!--        list类型属性注入-->
            <property name="list">
                <list>
                    <value>张三</value>
                    <value>秦始皇</value>
                </list>
            </property>
    <!--        map类型注入-->
            <property name="map">
                <map>
                    <entry key="001" value="java"></entry>
                    <entry key="002" value="javaScript"></entry>
                </map>
            </property>
    <!--        set类型注入-->
            <property name="set">
                <set>
                    <value>MySQL</value>
                    <value>Redis</value>
                </set>
            </property>
        </bean>
    
    <!--        设置集合的对象类型值-->
    <!--        list集合,对象类型-->
    <bean id="student" class="com.spring5.collection.Student">
            <property name="courseList">
                <list>
                    <ref bean="course1"></ref>
                    <ref bean="course2"></ref>
                </list>
            </property>
        </bean>
    <!--    创建多个course对象-->
        <bean id="course1" class="com.spring5.collection.Course">
            <property name="courseName" value="Spring框架"></property>
        </bean>
        <bean id="course2" class="com.spring5.collection.Course">
            <property name="courseName" value="MyBatis框架"></property>
        </bean>
    
  6. 属性注入-----properties

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X8fOUZ3K-1630320609501)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210813182147591.png)]

  1. FactoryBean
  • Spring有两种类型bean,一个普通bean,另一种工厂bean(FactoryBean)
  • 普通bean:在配置文件中,定义bean类型就是返回类型
  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样
    • 第一步,创建一个类作为工厂bean,实现接口FactoryBean
    • 第二步,实现接口里面的方法,在实现的方法中定义返回的bean类型

Bean的作用域

  1. 在Spring里面,可以设置创建的bean实例是单实例还是多实例。
  2. Spring里面,默认情况下,bean是单实例对象。
  3. scope属性值用于设置单实例还是多实例:
    • prototype:多实例
    • singleton:单例(默认)
  4. 设置scope为prototype,不是在加载spring配置文件时候创建对象,在调用getBean方法的时候创建
  5. 设置scope为singleton,加载spring配置文件的时候会创建对象

Bean的生命周期

  1. 生命周期

    ​ 从对象创建到销毁的过程

  2. bean生命周期

    • 通过构造器创建bean实例(无参数构造)
    • 为bean的属性设置值和对其它bean引用(调用set方法)
    • postProcessBeforeInitialization()
    • 调用bean的初始化方法(需要进行配置初始化方法)
    • postProcessAfterInitialization()
    • bean被使用
    • 容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)

IOC操作Bean管理(xml自动装配)

  1. 什么是自动装配?

    根据指定装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入

    <!--    实现自动装配
            byName根据属性名注入,bean的id值与类属性名称一样
            byType根据属性注入
    -->
        <bean id="emp" class="com.spring5.autowire.Emp" autowire="byName">
    <!--        <property name="dept" ref="dept"></property>-->
        </bean>
        <bean id="dept" class="com.spring5.autowire.Dept"></bean>
    

IOC操作Bean管理(基于注解)

使用注解的目的:简化xml配置,通过给bean上添加某些注解,可以快速将bean加入到ioc容器中

Spring针对Bean管理中创建对象提供注解

  • @Component
  • @Service
  • @Controller
  • @Repositroy:dao层

上面的四个注解,功能是一样的,都可以用来创建Bean对象


基于注解实现对象的创建
  • 第一步,引入依赖

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ewNvjFA-1630320609502)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210811174402503.png)]
  • 第二步:开启组件扫描

    <!--    开启组件扫描
            如果扫描多个包:
            1.多个包使用都好隔开
            2.扫描包上层目录
    -->
        <context:component-scan base-package="com.java.Dao,com.java.service"></context:component-scan>
    <!--    <context:component-scan base-package="com.java"></context:component-scan>-->
    
    <!--
        context:include-filter:设置扫描哪些内容
    -->
        <context:component-scan base-package="com.java" use-default-filters="false">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        </context:component-scan>
    <!--
            默认全部扫描(use-default-filters=true)
           context:exclude-filter:设置哪些内容不扫描
    -->
        <context:component-scan base-package="com.java">
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        </context:component-scan>
    

基于注解方式实现属性注入
  1. @AutoWired:根据属性类型进行自动装配

    • 把service和dao对象创建,在service和dao类上面添加创建对象的注解
    • 在service注入dao对象,在service类添加dao属性,在属性上面使用注解
  2. @Qualifier:根据属性名称进行注入

    @Qualifier注解和@AutoWired注解要一起使用

    @Service(value = "userService")
    public class UserService {
        @Autowired//根据类型进行注入,不需要加set方法
        @Qualifier(value ="userDaoImpl1")//有多个实现类的话,可以用value加以区分
        private UserDao userDao;
    
  3. @Resource:属性类型和属性名称都可(jdk的标准,扩展性强)

    @Resource//根据类型注入
        private UserDao userDao;
    
    @Resource(name = "userDaoImpl1")//根据名称注入
    	private UserDao userDao;
    
  4. @Value:注入普通类型


完全注解开发

创建配置类,替代xml配置文件

@Configuration//作为配置类,代替xml配置文件
@ComponentScan(basePackages = {"com.java"})
public class SpringConfig {
}

//测试类
@Test
    public void test3(){
        //加载配置类
        ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

泛型依赖注入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R8IZOeGj-1630320609503)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210813230136254.png)]


IOC容器总结

  1. 容器到底是什么,其实就是一个map
  2. 这个map保存所有创建好的bean,并提供外界获取的功能

IOC源码初体验

第一次这个源码看的实在是有亿点点难受,前面还好,越到后面越像看天书一样,源码只贴出一部分。

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    super(parent);
    this.setConfigLocations(configLocations);
    if (refresh) {
        //所有单实例bean创建完成
        this.refresh();
    }

}

refresh方法细节:

public void refresh() throws BeansException, IllegalStateException {
    synchronized(this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        this.prepareRefresh();
        //Spring解析xml配置文件将要创建的所有bean的配置信息保存起来
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            //支持国际化功能
            this.initMessageSource();
            this.initApplicationEventMulticaster();
            //留给子类的方法
            this.onRefresh();
            this.registerListeners();
            //初始化所有单实例bean的方法
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var10) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
            }

            this.destroyBeans();
            this.cancelRefresh(var10);
            throw var10;
        } finally {
            this.resetCommonCaches();
            contextRefresh.end();
        }

    }
}
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no BeanFactoryPostProcessor
   // (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
    //初始化所有单实例bean
   beanFactory.preInstantiateSingletons();
}
public void preInstantiateSingletons() throws BeansException {
   if (logger.isTraceEnabled()) {
      logger.trace("Pre-instantiating singletons in " + this);
   }

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   //拿到所有要创建的bean的名字,保存到beanNames中
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
    //增强for循环,按顺序创建bean
   for (String beanName : beanNames) {
       //根据bean的id获取到bena的定义信息(解析xml的时候得到的)
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
       //判断bean是单实例的,不是抽象的,并且不是懒加载
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         //是否式实现了FactoryBean接口的bean
          if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               FactoryBean<?> factory = (FactoryBean<?>) bean;
               boolean isEagerInit;
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                  isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
               }
               else {
                  isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
               }
               if (isEagerInit) {
                   //创建bean的细节
                  getBean(beanName);
               }
            }
         }
          //不是工厂bean
         else {
            getBean(beanName);
         }
      }
   }

   // Trigger post-initialization callback for all applicable beans...
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
               .tag("beanName", beanName);
         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
               smartSingleton.afterSingletonsInstantiated();
               return null;
            }, getAccessControlContext());
         }
         else {
            smartSingleton.afterSingletonsInstantiated();
         }
         smartInitialize.end();
      }
   }
}
public Object getBean(String name) throws BeansException {
    //所有的getBean都是调用对应的doGetBean
   return doGetBean(name, null, null, false);
}

上面这部分的代码没贴出来,一方面是代码实在有点多,另一方面是实在看不下去了,第一次看给我吐了…

实在看不下去了,这是人能写出来的代码么… 第一次看的我就像看天书一样。

暂时就理解到这里了,理解的或许有不少错误,希望大佬们指正。


小结

  • ApplicationContext是BeanFactory的子接口;
  • BeanFactory:bean工厂接口,负责创建bean实例,容器里面保存的bean实例其实是一个map;
    • BeanFactory也是最底层的接口
  • ApplicationContext:是容器接口,更多的负责容器功能的实现(可以基于BeanFactory创建好的对象之上完成更强大的功能);
    • 容器可以从map中获取这个bean
  • ApplicationContext是留给程序员使用的ioc容器接口,ApplicationContext是BeanFactory的子接口之一。

Spring的单元测试

package com.spring.test;

import com.spring.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * 使用Spring的单元测试
 * 1. 导包
 * 2.@ContextConfiguration(locations="")使用它来指定Spring的配置文件的位置
 * 3.@RunWith指定使用哪种驱动进行单元测试,默认就是junit
 *      @RunWith(SpringJUnit4ClassRunner.class)
 *      表示使用Spring的单元测试模块来执行标了@Test的测试方法
 *      以前@Test注解只是由junit执行
 *
 *   好处:我们不用context.getBean()获取组件了,直接Autowired组件,Spring为我们自动装配
 */
@ContextConfiguration(locations = "classpath:bean1.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTest {
//    ApplicationContext context=null;
    @Autowired
    UserService userService;
    @Test
    public void test(){
        System.out.println(userService);
        int i = userService.fundCount();
        System.out.println(i);
    }
}

AOP(面向切面编程)

AOP:(Aspect Oriented Programming)面向切面编程

OOP:(Object Oriented Programming)面向对象编程

​ 面向切面编程:基于OOP基础之上新的编程思想,指在程序运行期间,将某段代码动态的切入到指定方法指定位置进行运行的这种编程方式

​ 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发效率。

​ 利用AOP可以不同通过修改源代码的方式,在主干功能里面添加新功能

AOP底层原理

  1. AOP底层使用动态代理

    第一种,有接口情况,使用JDK动态代理

    ​ 创建接口实现类的代理类,增强类的方法

    第二种,没有接口情况,使用CGLIB动态代理

    ​ 创建子类的代理对象

JDK动态代理

interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I can fly";
    }

    @Override
    public void eat(String food) {
        System.out.println("喜欢吃"+food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("通用方法一");
    }
    public void method2(){
        System.out.println("通用方法二");
    }
}
//生产代理类的工厂
class ProxyFactory{
    public static Object getProxyFactory(Object object){//object:被代理类的对象
        //调用此方法返回一个代理类的对象
        MyInvocationHandler handler=new MyInvocationHandler();
        handler.bind(object);
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{

    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj){
        this.obj=obj;
    }

    //当我们通过代理类的对象调用方法a时,就会自动调用如下的方法invoke
    //将被代理类要执行的方法a的功能声明再invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        HumanUtil util=new HumanUtil();
        util.method1();

        Object returnValue = method.invoke(obj, args);

        util.method2();

        return returnValue;
    }
}
/*
实现动态代理:
    1.根据加载到内存中的被代理类,动态的创建一个代理类及其对象
    2.通过代理类的对象调用方法时,动态的去调用被代理类中的同名方法
 */
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyFactory(superMan);

        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("火锅");
        System.out.println("*****");

        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyFactory = (ClothFactory) ProxyFactory.getProxyFactory(nikeClothFactory);
        proxyFactory.produceCloth();
    }
}

AOP术语

  1. 连接点

    ​ 类里面哪些方法可以被增强,这些方法成为连接点;每一个方法的每一个位置都是一个连接点

  2. 切入点

    ​ 实际被真正增强的方法,成为切入点

  3. 通知(加入的代码逻辑)

    通知有多种类型:

    • 前置通知
    • 后置通知
    • 环绕通知
    • 异常通知
    • 最终通知(finally)
  4. 切面

    是动作,把通知应用到切入点的过程

  5. 切入点表达式

  6. 切入点表达式的作用:在众多连接点里面选出切入点

  7. 语法结构:

    //execution([权限修饰符][返回类型][类全路径][方法名称][参数列表])
    //权限修饰符可以省略,返回类型用*代替,参数列表用..代替
    //举例一:对com.dao.UserDao类里面的add方法增强
    execution(*com.dao.UserDao.add(..));
    //举例二:对com.dao.UserDao类里面的所有方法增强
    execution(*com.dao.UserDao.*(..));
    

实现AOP

Spring框架一般都是基于AspectJ实现AOP操作

AspectJ不是Spring的组成部分,是独立的框架,一般把AspectJ和Spring一起使用,进行AOP操作

//增强类(切面1)
@Component
@Aspect//生成代理对象
@Order(1)
public class UserProxy {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.spring.aop.User.add(..))")
    public void point(){

    }
    //前置通知
    @Before(value ="point()")
    public void before(){
        System.out.println("before1");
    }
    //有异常照样执行
    @After(value = "point()")
    public void after(){
        System.out.println("after1");
    }

    //后置通知(返回通知),有异常不执行
    @AfterReturning(value = "point()")
    public void afterReturning(){
        System.out.println("afterReturning1");
    }

    //异常通知
    @AfterThrowing(value = "point()")
    public void afterThrowing(){
        System.out.println("afterThrowing..");
    }

    @Around(value = "point()")
    //环绕通知
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object proceed = null;
        try {
            System.out.println("环绕前置");
            //利用反射调用目标方法,就是method.invoke(obj,args)
            proceed = proceedingJoinPoint.proceed();
            System.out.println("环绕返回通知");
        } catch (Exception e) {
            System.out.println("环绕异常通知");
        } finally {
            System.out.println("环绕后置通知");
        }
        return proceed;
    }
}

@Component
@Aspect//生成代理对象(切面2)
@Order(2)//Order值越小,优先级越高
public class UserProxy1 {
    //相同切入点抽取
    @Pointcut(value = "execution(* com.spring.aop.User.add(..))")
    public void point() {
    }

    //前置通知
    @Before(value = "point()")
    public void before() {
        System.out.println("before2");
    }
    @After(value = "point()")
    public void after(){
        System.out.println("after2");
    }
    @AfterReturning(value = "point()")
    public void afterReturning(){
        System.out.println("afterReturning2");
    }
}

多切面运行顺序

两个切面的测试结果如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MPPDODI-1630320609504)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210814115831228.png)]

为什么是这样呢?画个图就一目了然了!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cgv7HCVy-1630320609504)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210814121702115.png)]

AOP应用场景

  • AOP加日志保存到数据库
  • AOP做权限验证
  • AOP做安全检查
  • AOP做事物控制

jdbcTemplate

  1. 什么是jdbcTemplate

    • Spring框架对JDBC进行了封装,使用jdbcTemplate更方便地实现对数据库的操作

    在xml中引入jdbc配置文件的时候,一般在配置文件中加上前缀,因为username是Spring中的关键字,不加会报错:

    jdbc.username=root
    jdbc.password=0000
    jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    
  2. jdbcTemplate的xml配置

    <!--    引用外部配置文件-->
        <context:property-placeholder location="dbconfig.properties"></context:property-placeholder>
    <!--    数据库连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
            <property name="url" value="${jdbc.url}"></property>
    <!--
            username是Spring中的一个关键字
    -->
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        </bean>
    
    <!--    配置jdbc模板对象,注入dataSource-->
    
    <!--    jdbcTemplate-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--        注入dataSource-->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    

jdbcTemplate的增删改查

@Component
public class UserDaoImpl implements UserDao {
    //注入jdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;

    //添加方法
    @Override
    public void add(User user) {
        String sql="insert into user values(?,?,?,?,?)";
        int update = jdbcTemplate.update(sql, user.getId(), user.getName(), user.getPassword(), user.getAddress(), user.getPhone());
        System.out.println(update);
    }

    //删除单行
    @Override
    public void delete(String id) {
        String sql="delete from user where id=?";
        int update = jdbcTemplate.update(sql,id);
        System.out.println(update);
    }

    //更新操作
    @Override
    public void update(User user) {
        String sql="update user set `name`=? ,password=? ,address=?, phone=? where id=?";
        int update = jdbcTemplate.update(sql, user.getName(), user.getPassword(), user.getAddress(), user.getPhone(), user.getId());
        System.out.println(update);
    }

    //查询总记录数
    @Override
    public int selectCount() {
        String sql="select count(*) from user";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }

//    RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
    @Override
    public User findUser(String id) {
        String sql="select * from user where id=?";
        User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), id);
        return user;
    }

    //查找所有对象
    @Override
    public List<User> findAllUser() {
        String sql="select * from user";
        List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<User>(User.class));

        return userList;
    }

    //批量添加
    @Override
    public void batchAddUser(List<Object[]> list) {
        String sql="insert into user values(?,?,?,?,?)";
        int[] update = jdbcTemplate.batchUpdate(sql, list);
        System.out.println(Arrays.toString(update));
    }

    //批量修改
    @Override
    public void batchUpdateUser(List<Object[]> list) {
        String sql="update user set `name`=? ,password=? ,address=?, phone=? where id=?";
        int[] update = jdbcTemplate.batchUpdate(sql, list);
        System.out.println(Arrays.toString(update));
    }

    //批量删除
    @Override
    public void batchDeleteUser(List<Object[]> list) {
        String sql="delete from user where id=?";
        int[] update = jdbcTemplate.batchUpdate(sql, list);
        System.out.println(Arrays.toString(update));
    }

声明式事物

​ 事物管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事物管理。

​ 使用事物管理器就可以在目标方法运行前后进行事务控制(事物切面)

​ 目前都使用DataSourceTransactionManager

快速配置一个事物

<!--    事物控制-->

<!--    1.配置事物管理器让其进行事物控制-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--        控制住数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
<!--    2.开启基于注解的事物控制模式:依赖tx名称空间-->
    <tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!--    3.给事物方法加注解@Transactional-->

事物细节

/*
isolation-Isolation:事物的隔离级别
propagation-Propagation:事物的传播行为
    传播行为来设置这个事物方法是不是和之前的大事物共享一个事物(使用同一条连接)
    如果是REQUIRED:事物的属性都是继承于大事物的
    而REQUIRED——NEW可以调整

noRollbackFor-Class[]:设置哪些异常事物不回滚(设置原来默认本来需要回滚的事物不回滚)
noRollbackForClassName-String[](String全类名)

rollbackFor-Class[]:设置哪些异常事物回滚(设置原本不回滚的异常回滚)
rollbackForClassName-String[]

异常分类:
    运行时异常(非检查异常):可以不用处理;默认都回滚
    编译时异常(检查异常):try-catch或在方法上声明throws;默认不回滚

事物的回滚:默认发生运行时异常都回滚,发生编译时异常不会回滚

readOnly-boolean:设置事物为只读事物,默认false
    如果没有对db的增删改操作,则设置为true可以加快查询速度
timeout-int:超时,超出指定执行时长后自动终止并回滚
    异常:TransactionTimedOutException
*/

事物的隔离级别

常见的事物异常:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeppFisK-1630320609505)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210816134511223.png)]

隔离级别:数据库系统必须具有隔离并发运行各个事物的能力,使他们不会互相影响,避免各种并发问题。一个事物与其他事务隔离程度称为隔离级别。SQL标准规定了以下几种事物隔离级别:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-siL1us0g-1630321315813)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210816135622077.png)]

说明:其中,MySQL默认的隔离级别是REPEATABL READ

事物的传播行为

img

说明:较为常用的是第一个和第二个。

REQUIRED与REQUIRED_NEW的区别:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值