Spring知识总结

1.IOC:反转控制

1)DI:依赖注入
2)IOC在spring容器的实现:
a).通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化
b)Spring提供了IOC容器的两种实现方式.
BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。

ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。

3)ApplicationContext有两个主要实现类:
a) ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件

b) FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件

4)bean:
同一个类型的bean在XML文件中配置了多个,则获取时会抛出异常,所以同一个类型的bean在容器中必须是唯一的。

a)通过setXXX()方式赋值:

XML配置

  <!-- 使用bean元素定义一个由IOC容器创建的对象 -->
    <!-- class属性指定用于创建bean的全类名 -->
    <!-- id属性指定用于引用bean实例的标识 -->
    <bean id="student" class="first.bean.Student">
        <!-- 使用property子元素为bean的属性赋值 -->
        <property name="studentId" value="1001"/>
        <property name="stuName" value="Tom2015"/>
        <property name="age" value="20"/>
    </bean>

代码实现:

 //1创建IOC容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
        //2根据id值得到bean实例
        Student student  = (Student) classPathXmlApplicationContext.getBean("student");

        System.out.println(student);

b)通过构造的方式赋值
XML

<bean id="student1" class="first.bean.Student">
        <constructor-arg value="007" index="0" type="java.lang.Integer"></constructor-arg>
        <constructor-arg value="名字" index="1" type="java.lang.String"></constructor-arg>
        <constructor-arg value="18" index="2" type="java.lang.Integer"></constructor-arg>
    </bean>

    <bean id="student11" class="first.bean.Student">
        <constructor-arg  name="studentId" value="007" ></constructor-arg>
        <constructor-arg value="名字" name="stuName"></constructor-arg>
        <constructor-arg value="18" name="age"></constructor-arg>
    </bean>

代码实现:

 @Test
    public void test(){

        //1创建IOC容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
        //2根据id值得到bean实例
        Student student  = (Student) classPathXmlApplicationContext.getBean("student1");

        System.out.println(student);

        //2根据id值得到bean实例
        Student student11  = (Student) classPathXmlApplicationContext.getBean("student11");

        System.out.println(student11);

    }

c)bean给属性赋null值,ref标签赋值以及属性里面有对象,集合,map,properties等属性;

xml

 <bean id="student2" class="first.bean.Student">
        <!-- 使用property子元素为bean的属性赋值 -->
        <property name="stuName">
            <null></null>
        </property>
        <!--list集合里面存一个对象-->
        <!--对象实体-->
        <property name="dog">
            <bean class="first.bean.Dog">
                <property name="name" value="大黄"></property>
            </bean>
        </property>

        <property name="bookList">
            <list>
                <bean class="first.bean.Session">
                    <constructor-arg name="classId" value="001"></constructor-arg>
                    <constructor-arg name="className" value="计算机"></constructor-arg>
                </bean>
                <bean class="first.bean.Session">
                    <constructor-arg name="classId" value="002"></constructor-arg>
                    <constructor-arg name="className" value="高数"></constructor-arg>
                </bean>
            </list>
        </property>

        <!--map-->
        <property name="map">
            <map>
                <entry>
                    <key>
                        <value>
                            map01
                        </value>
                    </key>
                    <bean class="first.bean.Dog">
                        <property name="name" value="my dog"></property>
                    </bean>
                </entry>

                <entry>
                    <key>
                        <value>
                            map02
                        </value>
                    </key>
                    <ref bean="secondog"></ref>
                </entry>

            </map>
        </property>


        <!--properties-->
        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">root</prop>
                
            </props>
        </property>








    </bean>


    <bean id="secondog" class="first.bean.Dog">
        <property name="name" value="大黄"></property>
    </bean>

代码:

 @Test
    public void test01(){

        //1创建IOC容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("helloWorld.xml");
        //2根据id值得到bean实例
        Student student  = (Student) classPathXmlApplicationContext.getBean("student2");

        System.out.println(student);


    }

输出:
在这里插入图片描述
d)级联赋值:为对象的对象的属性赋值
xml

<!--级联赋值-->
    <bean id="student3" class="first.bean.Student">
        <property name="dog" ref="secondog">
        </property>
        <property name="dog.name" value="新的大黄"></property>
    </bean>

e)通过继承实现bean配置信息的重用
(指的是配置信息的继承,并非类的继承)

加了一个parent的属性;
当加了一个abstract的属性的时候,就意味着只能继承,不能被调用获取数据

<bean id="student4" class="first.bean.Student" parent="student2">
        <property name="dog" ref="secondog">
        </property>
        <property name="dog.name" value="新的大黄吗"></property>
    </bean>

f)bean的创建顺序默认就是配置文件的配置顺序
,可以通过depends-on改变创建顺序
意思是等bean容器secondog,student3执行完成再执行student1

<bean id="student1" class="first.bean.Student" depends-on="secondog,student3">
        <constructor-arg value="007" index="0" type="java.lang.Integer"></constructor-arg>
        <constructor-arg value="名字" index="1" type="java.lang.String"></constructor-arg>
        <constructor-arg value="18" index="2" type="java.lang.Integer"></constructor-arg>
    </bean>

f) bean的作用域,指定bean是否单实例,默认是单实例
scope属性:有四种值:
prototype:多实例:当获取bean实例时候才会被创建,且每一次获取将得到一个新的实例。
singleton:单实例,单实例在容器启动完成就已经创建好对象,保存在容器中,构造器只调一次。
request:在web环境同一次请求创建一个bean实例(通常没用)
session:在web环境同一次会话创建一个bean实例(通常没用)

g)配置通过静态工厂方法创建bean,实例工厂创建bean、FactoryBean

bean的创建默认就是框架通过反射new出来的bean实例,
工厂模式:工厂帮我们创建对象
静态工厂:工厂本身不用创建对象,通过静态方法调用,对象=工厂类.工厂方法名();
实例工厂:工厂本身需要创建对象,工厂类 对象 = new 工厂类,工程类.getAirPlane();

静态工厂XML:
这里并非要创建AirPlaneStaitcFactory实例,所以指定factory-method属性表名是个工厂,返回的不是工厂的对象而是该方法的对象

 <!--这里并非要创建AirPlaneStaitcFactory实例,所以指定factory-method属性表名是个工厂,返回的不是工厂的对象而是该方法的对象-->
    <bean id="staitcAirPlane" class="first.bean.AirPlaneStaitcFactory" factory-method="getAirPlane">
        <constructor-arg name="name" value="歼20">
        </constructor-arg>
    </bean>

java代码:

package first.bean;

public class AirPlaneStaitcFactory {
    public static AirPlane getAirPlane(String name){
        AirPlane airPlane = new AirPlane();
        airPlane.setName(name);
        airPlane.setDriverName("机长名字");
        airPlane.setLength("100m");

        return airPlane;

    }
}

实例工厂:
xml:
ii)先创建InstancePlaneFactory bean实例
ii)配置我们要创建的AirPlane使用哪个工厂
factory-bean指定哪个工厂实例
factory-method使用哪个工厂方法

<!--先创建实例工厂-->
    <bean id="InstancePlaneFactory" class="first.bean.AirPlaneInstanceFactory">
    </bean>

    <bean id="InstancePlane" class="first.bean.AirPlane"
    factory-bean="InstancePlaneFactory"
    factory-method="getAirPlane">
        <constructor-arg name="name" value="实例飞机"></constructor-arg>
    </bean>

java

 public void test2(){

        //1创建IOC容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean.xml");

        AirPlaneInstanceFactory airPlaneInstanceFactory = (AirPlaneInstanceFactory)classPathXmlApplicationContext.getBean("InstancePlaneFactory");
        AirPlane AirPlane = airPlaneInstanceFactory.getAirPlane("实例机长");

        System.out.println(AirPlane);

    }

FactoryBean*(是spring规定的接口)
这个接口的实现类都是工厂类
xml:

<bean id="myFactoryBeanImple" class="first.bean.MyFactoryBeanImple">
    </bean>

实现类:
getObject相当于工厂方法;
getObjectType返回工厂类型
isSingleton:是否单例;false不是单例

package first.bean;

import org.springframework.beans.factory.FactoryBean;

public class MyFactoryBeanImple  implements FactoryBean<AirPlane>{
    /**\
     * 返回创建的对象
     * @return
     * @throws Exception
     */
    @Override
    public AirPlane getObject() throws Exception {
        System.out.println("进来getObject方法。。。");
        AirPlane airPlane = new AirPlane();
        airPlane.setName("自带工厂方法的名字");
        return airPlane;
    }

    /**
     * 返回对象的类型
     * spring会自带调用,来确定创建对象的类型
     * @return
     */
    @Override
    public Class<?> getObjectType() {
        System.out.println("进来getObjectType方法" +
                "。。。");
        return null;
    }

    /**
     * false为单例
     * @return
     */
    @Override
    public boolean isSingleton() {
        return true;
    }
}


测试结果:
因为该工厂是spring认识的工厂,当容器一被创建的时候,
当isSingleton()方法返回false的时候,为多例,
当isSingleton()方法返回值为true时候,为单例子,;
不管是单实例还是多实例,只有bean被调用才创建。

h)创建带有生命周期方法的bean
生命周期:bean的创建到销毁

ioc容器中注册bean:
ii)、单例bean,容器启动的时候创建好,容器关闭时候销毁;
ii)多例bean,获取的时候才创建;
可以为bean自定义一些生命周期方法,在创建和销毁就可以自动调用;
bean里面可以指定destroy-method(销毁),和init-method(创建)属性

在这里插入图片描述
i)bean的后置处理器BeanPostProcessor,在bean的初始化前后调用方法

xml:

<bean id="airPlane" class="first.bean.AirPlane" init-method="myInit" destroy-method="myDestroy">
        <property name="name" value="歼击机"></property>
    </bean>

    <bean id="processor" class="first.bean.MyBeanPostProcessor">

    </bean>

java:

 @Test
    public void test3(){

        //1创建IOC容器
        ConfigurableApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("bean2.xml");

        classPathXmlApplicationContext.close();
//        System.out.println(AirPlane);

    }

运行结果:

before和after方法分别在初始化之前和后初始化之后调用
在这里插入图片描述

执行顺序:
构造器—》后置处理器before—》初始化方法–》后置处理器after–》初始化完成

无论bean是否有初始化方法,后置处理器都会默认有,还有继续工作

j)引用外部属性文件
数据库连接池作为单实例是最好的,一个项目就一个连接池,连接池里面有很多连接,可以让spring帮忙创建

xml
方式一:直接在xml配置;
方式二:引入外部的配置文件
<context:property-placeholder location=“classpath:dbconfig.properties”/>
并加上命名空间:
xmlns:context="http://www.springframework.org/schema/context

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
,为了防止和spring的属性重复,通常在配置的时候,加个前缀。eg:jdbc.username

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="user" value="root"></property>
        <property name="password" value="hhh"></property>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.10.**:3306/xx?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>

    <bean id="processor" class="first.bean.MyBeanPostProcessor">

    </bean>

    <!--加载外部配置文件-->
    <context:property-placeholder location="classpath:dbconfig.properties"/>

    <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>


</beans>

java可以根据类型拿或者根据id去拿取;
根据类型拿到连接池,可以获得该类型的所有实现子类等等

 @Test
    public void test4(){

        //1创建IOC容器
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("application.xml");
        //拿到连接池
        DataSource dataSource = (DataSource)classPathXmlApplicationContext.getBean("dataSource");

        //根据类型拿到连接池,可以获得该类型的所有实现子类等等
       DataSource dataSource11 = (DataSource)classPathXmlApplicationContext.getBean(DataSource.class);

        DataSource dataSource2 = (DataSource)classPathXmlApplicationContext.getBean("dataSource2");



    }

k)基于xml的自动装配
autowire=“defaut/no”
三种规则自动装配:
在bean中加入autowire的属性;

例如:
在这里插入图片描述
有参数构造器:
在这里插入图片描述
L)SPEL
#{1*12}//可以运算
#{book.name}//可以引用其他bean的属性或者直接引用其他bean
#(T(全类名).(静态方法))//调用静态方法
#{book1.getBookName()}//调用非静态 对象.方法名

M)使用注解快速加入容器的几步:
ii)加上controller,service,repository,component
ii)加上spring,自动扫描组件,依赖context名称空间
ii)id默认是类名首字母小写,(记得需要导入aop包)

xml

 <context:component-scan base-package="first.controller">

    </context:component-scan>

java
(也可以修改默认名称)

@Controller
//@Controller("hh")
public class bookController {
    public void getBook(){
        System.out.println("getBook");
    }



}

N)@Autowired的原理:
ii)先按照类型去找,找到一个就赋值
ii)没找到抛出异常
ii)找到如果有多个就按照id去找
@Qualifier指定变量名称进行找

2.AOP

面向切面编程:将某段代码动态切入到方法指定位置运行的这种编程方式,面向切面编程;

1)前奏:使用jdk默认的动态代理的方式写入日志:

代理类:
代理对象传入实现接口,是为了让代理对象也实现与之相同的接口;使之能够调用相关的方法:Calculator proxy = CalculatorProxy.getProxy(calculator);
所以有个缺点:如果没有实现接口的话是没有办法使用jdk默认的动态代理;

package AOP;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class CalculatorProxy {
    /**
     * 为传入的参数创建一个动态代理对象
     * @param calculator
     * @return
     */
    public static Calculator getProxy(Calculator calculator) {
        //方法执行器,帮我们目标对象执行目标方法
        InvocationHandler h = new InvocationHandler() {
            /**
             *
             * @param proxy:代理对象,给jdk使用,任何时候都不要动这个对象
             * @param method:当前要执行的目标对象的方法
             * @param args:这个方法调用时候,外界的参入的参数值
             * @return
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("目标方法执行之前。。。");
                //目标方法执行后的返回值
                Object invoke = method.invoke(calculator,args);
                System.out.println("目标方法执行之后。。。");
                return invoke;
            }
        };
        //实现了哪些接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
        //被代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //proxy为目标对象创建代理对象
        Object proxy = Proxy.newProxyInstance(loader,interfaces,h);
        return (Calculator) proxy;
    }
}

CalculatorImpl类

package AOP;

public class CalculatorImpl implements Calculator {

    @Override
    public double add(double a, double b) {
        System.out.println("加法运算结果:"+(a+b));
        return a+b;
    }
}

test类

package AOP;

import org.junit.Test;

public class AOPTest {

    @Test
    public void test(){
        Calculator calculator = new CalculatorImpl();
        calculator.add(1,1);
        Calculator proxy = CalculatorProxy.getProxy(calculator);
        proxy.add(3,1);
    }
}

2)spring的aop简单使用(aop底层是动态代理)(cglib)

//Cglib动态代理,实现MethodInterceptor接口
//重写拦截方法intercept

在这里插入图片描述

在这里插入图片描述

面向切面编程的专业术语:
ii)导包
ii)写配置:
将目标和切面类(封装了通知方法(在目标方法执行前后执行的方法))加入到ioc容器中;
切面类
加上注解:
@Aspect
@Component

package Spring_AOP;

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

@Aspect
@Component
public class LogUtils {
    /**
     * try{
     *     @before
     *     method.invoke(obj,agrs);
     *     @AfterReturning
     * }catch(e){
     *     @AfterThrowing
     * }finally{
     *     @After
     * }
     * before目标方法执行之前   前置通知
     * after目标方法结束之后   后置通知
     * AfterReturn 在目标方法返回之后  返回通知
     * @AfterThrowing在目标方法抛出异常之后  异常通知
     * @Around 环绕通知
     */
    //目标方法执行之前,写入切入点表达式
    //execution(返回权限符 返回值类型 方法签名)
    @Before("execution( public  double Spring_AOP.Calculator.add(double ,double ))")
    public static void logStart(){
        System.out.println("开始执行");
//        System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
    }
    //目标方法出现异常时候执行
    @AfterThrowing("execution( public  double Spring_AOP.Calculator.*(double ,double ))")
    public static void logException(){
        System.out.println("抛出异常之后");
//        System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

    //
    @AfterReturning("execution( public  double Spring_AOP.Calculator.*(double ,double ))")
    public static void logReturn(){
        System.out.println("正常执行之后");
//        System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

    @After("execution( public  double Spring_AOP.Calculator.add(double ,double ))")
    public static void logAfter(){
        System.out.println("方法结束之后");
//        System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
    }
}

目标类的实现类

package Spring_AOP;

import org.springframework.stereotype.Component;

@Component
public class CalculatorImpl implements Calculator {

    @Override
    public double add(double a, double b) {
        System.out.println("加法运算结果:"+(a+b));
        return a+b;
    }

    @Override
    public double ChuFa(double a, double b) {
        return a/b;
    }
}

还要告诉spring哪个是切面类@Aspect,何时执行
ii)在配置文件开启基于注解的aop功能

导入aop的空间名称以及注解扫描

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        ">


    <context:component-scan base-package="Spring_AOP">

    </context:component-scan>

    <!--开辟基于注解的aop功能,aop名称空间-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>





</beans>

通知:

/**
* try{
* @before
* method.invoke(obj,agrs);
* @AfterReturning
* }catch(e){
* @AfterThrowing
* }finally{
* @After
* }
* before目标方法执行之前 前置通知
* after目标方法结束之后 后置通知
* AfterReturn 在目标方法返回之后 返回通知
* @AfterThrowing在目标方法抛出异常之后 异常通知
* @Around 环绕通知
*/

//目标方法执行之前,写入切入点表达式
//execution(返回权限符 返回值类型 方法签名)
@Before(“execution( public double Spring_AOP.Calculator.add(double ,double ))”)

测试类

 @Test
    public void test2(){
        try{
            Calculator calculator = (Calculator)ioc.getBean(Calculator.class);
//            Calculator calculator = new CalculatorImpl();
//            calculator.add(1,1);
//            int i = 1/0;

            calculator.ChuFa(1,0);
//            int i = 1/0;
        }catch (Exception e){
            System.out.println(e.getMessage());
        }finally {

        }
    }

注:容器加在接口时候是不创建对象的(spring比较智能),所以通常是加载实现类上

3)切入点表达式的写法
ii)匹配一个或者多个字符
@Before(“execution( public double Spring_AOP.Calculator.*(double ,double ))”)
ii)匹配任意一个参数,第一个是int类型,第二个参数任意类型(匹配任意类型)

execution( public double Spring_AOP.Calculator.(int ,))
ii)匹配任意多个参数,任意类型参数
execution( public double Spring_AOP.Calculator.(…))
ii)匹配任意多次路径
execution( public double Spring_AOP1…Spring_AOP12Calculator.
(…))

ii)权限位置不能代表任意,权限位置不写就表示任意;
execution( double Spring_AOP1…Spring_AOP12Calculator.
(…))
ii)全权限,任意返回类型,任意路径,任意参数
execution( * *(…))

4)在通知方法运行的时候,拿到目标方法的详细信息
只需要在通知方法的参数列表写上一个参数
JoinPoint joinPoint

 @Before("execution( public  double Spring_AOP.Calculator.add(double ,double ))")
    public static void logStart(JoinPoint joinPoint){
        System.out.println("开始执行");
        Object[] args = joinPoint.getArgs();
        System.out.println("方法参数:"+Arrays.toString(args));
//        System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

5)在通知方法运行的时候,拿到目标方法的详细信息,(拿到返回值,异常)

ii)返回值
当Object result改为double result的时候,就只能接收浮点类型

 @AfterReturning(value="execution( public  double Spring_AOP.Calculator.*(double ,double ))",returning = "result")
    public static void logReturn(JoinPoint joinPoint,Object result){
        System.out.println("正常执行之后");
        System.out.println("result:"+result);
//        System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

ii)异常信息

  //目标方法出现异常时候执行
    @AfterThrowing(value = "execution( public  double Spring_AOP.Calculator.*(double ,double ))",throwing ="exception" )
    public static void logException(JoinPoint joinPoint,Exception exception){
        System.out.println("抛出异常之后");
//        System.out.println("["+method.getName()+"]方法正常执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

6)也可以把表达式抽取出来,实现可重入,
@Pointcut(“execution( public double Spring_AOP.Calculator.add(double ,double ))”)

  @Pointcut("execution( public  double Spring_AOP.Calculator.add(double ,double ))")
    public void hahaaMyPoint(){

    }

    //目标方法执行之前,写入切入点表达式
    //execution(返回权限符 返回值类型 方法签名)
    @Before(value = "hahaaMyPoint()")
    public static void logStart(JoinPoint joinPoint){
        System.out.println("开始执行");
        Object[] args = joinPoint.getArgs();
        System.out.println("方法参数:"+Arrays.toString(args));
//        System.out.println("["+method.getName()+"]方法开始执行,用的参数列表["+ Arrays.toString(args)+"]");
    }

7)环绕通知:
执行顺序:
环绕通知优先于其他普通通知
环绕前置–》环绕后置—》方法返回–》环绕正常返回/方法异常–》环绕后置–》普通后置
环绕通知有个参数ProceedingJoinPoint ,环绕通知是“四合一”的通知
在这里插入图片描述

 @Around("hahaaMyPoint()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object[] args = proceedingJoinPoint.getArgs();

        Object proceed = null;

       try {
           System.out.println("myAround开始");
           //利用反射执行目标方法
           proceed = proceedingJoinPoint.proceed(args);
           System.out.println("myAround正常执行结束");

       }catch (Exception e){
           System.out.println("myAround抛出异常");
       }finally {
           System.out.println("myAround最后");
       }

       return proceed;

    }

注:当有多个切面的时候,默认按照字母文件的首字母的字母表顺序,也可以通过加上@Order(1)来改变顺序,数字越小,优先级越高

3基于xml的aop

1)加到ioc容器
在这里插入图片描述
2)需要名称空间,告诉哪个是切面类,以及相对应的通知方法

<bean  id="calculator2" class="Spring_AOP.CalculatorImpl"></bean>
    <bean id="logUtils" class="Spring_AOP.LogUtils"></bean>

    <aop:config>
        <!--指定切面-->
        <aop:aspect ref="logUtils">
            <aop:pointcut id="hahaaMyPoint2" expression="execution( public  double Spring_AOP.Calculator.add(double ,double ))"/>
            <!--相当于前置通知-->
            <aop:before method="logStart2" pointcut="execution( public  double Spring_AOP.Calculator.add(double ,double ))"/>

            <!--exception是来接受异常的-->
            <aop:after-throwing method="logException2" pointcut-ref="hahaaMyPoint2" throwing="exception"></aop:after-throwing>


        </aop:aspect>

    </aop:config>

4.JdbcTemplate

xml配置数据源:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="user" value="root"></property>
        <property name="password" value="hhh"></property>
        <property name="jdbcUrl" value="jdbc:mysql://192.168.10.**:3306/xx?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

java代码:
在这里插入图片描述

5.声明式事务

四个关键特性:
原子性:要么都执行,要么都不执行
一致性;数据一致性,例如:我转一百,对方也将收到一百,不可能我转了1百对方没收到啥的;
隔离性;多事务并发执行互不干扰
持久性;对数据的修改永久保存下去。

事务切面===事务管理器

为某个方法添加事务管理器:
1)配置事务管理器让其进行事务控制,需要面向切面编程的包

2)开启基于注解的事务控制模式,依赖tx名称空间
3)给事务方法加注解

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">



    <context:component-scan base-package="TX">

    </context:component-scan>
    <!--加载外部配置文件-->
    <context:property-placeholder location="classpath:dbconfig.properties"/>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
    </bean>

    <!--事务控制-->
    <!--1.事务管理器-->
    <bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--2开启基于注解的事务控制模式,依赖tx名称空间-->
    <tx:annotation-driven transaction-manager="tm"/>
    <!--3.给事务方法加上注解-->

</beans>

BookDao

package TX;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;

@Repository
public class BookDao {

//    @Autowired
    @Resource(name = "jdbcTemplate")
    JdbcTemplate jdbcTemplate;

    @Test
    public void test(){
        System.out.println(jdbcTemplate);
        String sql ="select price from book where isbn = 'ISBN-001'";
        int price = jdbcTemplate.queryForObject(sql,Integer.class);
        System.out.println(price);
    }

    void updateStock(String isbn){
        String sql ="update book_stock set stock=stock-1 where isbn = ?";
        jdbcTemplate.update(sql,isbn);
    }

    void updateAccount(String username,int balance){
        String sql ="update account set balance=balance-? where username = ?";
        jdbcTemplate.update(sql,balance,username);
    }

    int getPrice(String isbN){
        String sql ="select price from book where isbn = 'ISBN-001'";
        int price = jdbcTemplate.queryForObject(sql,Integer.class);
        return price;
    }
}

BookService

package TX;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class BookService {
    @Autowired
    BookDao bookDao;

    @Transactional
    public void checkOut(String username,String isbn){
//        1减去库存
        bookDao.updateStock(isbn);

        int price = bookDao.getPrice(isbn);
        int i=1/0;
        //2减去余额
        bookDao.updateAccount(username,price);

    }

}

测试代码:

(采用Junit无法调用到注解的jdbcTemplate,这里采用spring自带的测试@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { “classpath:tx.xml” }))

package TX;

import Spring_AOP.Calculator;
import Spring_AOP.CalculatorImpl;
import Spring_AOP.CalculatorProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:tx.xml" })
//@Component
public class JDBCTemplateTest {
    //1创建IOC容器
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("tx.xml");
    JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);

    @Autowired
    private BookService bookService;

    @Autowired
    private BookDao bookDao;



    @Test
    public void test(){
//        System.out.println(jdbcTemplate);
//        String sql ="select price from book where isbn = 'ISBN-001'";
//        int price = jdbcTemplate.queryForObject(sql,Integer.class);
//        System.out.println(price);

//        1减去库存
       bookService.checkOut("Tom","ISBN-001");

//        int count= bookDao.getPrice("ISBN-001");
//        System.out.println("数量:"+count);


    }


}

在这里插入图片描述

6.事务细节

isolation:事务的隔离级别
propagation:事务的传播行为
timeOut:超时,自动终止并且回滚
readOnly:设置事务为只读事务,可以进行事务优化,为true
时候,加快查询速度,不用管事务一堆操作,默认是false

运行时异常:可以不处理,默认都回滚;
编译时异常:要么try-catch,要么在方法声明throw,默认不回滚

noRollbackFor:哪些异常可以不回滚(可以让默认回滚的异常不回滚)
noRollbackForName
rollbackFor:哪些不回滚的异常可以回滚
rollbackForName

1)隔离级别:
并发问题:
ii)脏读:(只有脏读是一定不能发生)
ii)不可重复读:(指的是更新的数据)
ii)幻读:(指的是插入的记录)
ii)
四种隔离级别:
(肯定是一个在读,一个在写才会出现这个问题)
在这里插入图片描述
四种隔离级别分别有什么问题:在这里插入图片描述
2)模拟并发问题:

ii)默认隔离级别
SELECT @@tx_isolation;
REPEATABLE-READ
ii)修改为读取未提交的
set session transaction isolation level read uncommitted;(当前会话)
在这里插入图片描述

ii)模拟脏读
在这里插入图片描述
ii)模拟不可重复读
在这里插入图片描述

ii)可重复读:repeatable read
在这里插入图片描述

并发修改数据的时候,底层将会进行排队
3)传播行为
如果多个事务进行嵌套运行,子事务是否要和大事务共用一个事务

在这里插入图片描述
eg:@transactional(propagation=Propagation.REQUIRED,timeout=3)
当使用Propagation.REQUIRED,所有的子事务将继承于大事务
(REQUIRED和REQUIRED_NEW最常见)

情况分析
ii)当大事务里面嵌套了两个小事务
当大事务抛出异常的时候,第一个事务REQUIRED_NEW可以成功执行,第二个REQUIRED执行失败
在这里插入图片描述

ii)当第一个子事务发生异常时候,虽然传播行为是REQUIRED_NEW,但是产生的异常会往外抛,导致大事务也发生异常,并且第二个子事务是REQUIRED,所以第二个子事务也执行不成功
在这里插入图片描述

ii)当第二个事务发生异常时候,并不影响第一个成功执行
在这里插入图片描述

7.基于xml的事务控制配置

1)配置事务管理器
2)卡配置事务方法
3)告诉spring哪些是事务方法(事务切面按照我们切入表达式切入事务方法)

xml配置

   <aop:config>
        <aop:pointcut id="point" expression="execution(* TX(..))"/>
        <!-- 将切入点表达式和事务属性配置关联到一起 -->
        <aop:advisor advice-ref="mtTx" pointcut-ref="point"></aop:advisor>
    </aop:config>

    <!-- 配置基于XML的声明式事务  -->
    <tx:advice  id="mtTx" transaction-manager="tm">
        <tx:attributes>
            <!-- 设置具体方法的事务属性 -->
            <tx:method name="find*" read-only="true"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="checkOut2"
                       isolation="READ_COMMITTED"
                       no-rollback-for="java.lang.ArithmeticException,java.lang.NullPointerException"
                       propagation="REQUIRED"
                       read-only="false"
                       timeout="10"/>
        </tx:attributes>

    </tx:advice>

8.源码分析

//1创建IOC容器
    ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("bean.xml");

当执行这语句之后,所有的bean将创建并初始化完成。
1) AbstractApplicationContext有一个Refresh()实现单实例bean的创建

public void refresh() throws BeansException, IllegalStateException {
    Object var1 = this.startupShutdownMonitor;
    synchronized(this.startupShutdownMonitor) {
        this.prepareRefresh();
//解析xml配置文件,将bean的配置信息保存起来
        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
        this.prepareBeanFactory(beanFactory);

        try {
            this.postProcessBeanFactory(beanFactory);
            this.invokeBeanFactoryPostProcessors(beanFactory);
            this.registerBeanPostProcessors(beanFactory);
//国际化功能
            this.initMessageSource();
            this.initApplicationEventMulticaster();
//空方法,留给子类
            this.onRefresh();
            this.registerListeners();
//初始化所有单实例bean
            this.finishBeanFactoryInitialization(beanFactory);
            this.finishRefresh();
        } catch (BeansException var9) {
            if (this.logger.isWarnEnabled()) {
                this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
            }

            this.destroyBeans();
            this.cancelRefresh(var9);
            throw var9;
        } finally {
            this.resetCommonCaches();
        }

    }
}


在这里插入图片描述
2)DefaultListableBeanFactory:preInstantiateSingletons()创建bean

public void preInstantiateSingletons() throws BeansException {
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Pre-instantiating singletons in " + this);
    }

//拿到所有需要创建的bean的名字
    List<String> beanNames = new ArrayList(this.beanDefinitionNames);
    Iterator var2 = beanNames.iterator();
//按照顺序创建bean
    while(true) {
        String beanName;
        Object bean;
        do {
            while(true) {
                RootBeanDefinition bd;
                do {
                    do {
                        do {
                            if (!var2.hasNext()) {
                                var2 = beanNames.iterator();

                                while(var2.hasNext()) {
                                    beanName = (String)var2.next();
                                    Object singletonInstance = this.getSingleton(beanName);
                                    if (singletonInstance instanceof SmartInitializingSingleton) {
                                        SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
                                        if (System.getSecurityManager() != null) {
                                            AccessController.doPrivileged(() -> {
                                                smartSingleton.afterSingletonsInstantiated();
                                                return null;
                                            }, this.getAccessControlContext());
                                        } else {
                                            smartSingleton.afterSingletonsInstantiated();
                                        }
                                    }
                                }

                                return;
                            }

                            beanName = (String)var2.next();
//根据beanId获取bean的定义信息
                            bd = this.getMergedLocalBeanDefinition(beanName);
                        } while(bd.isAbstract());
                    } while(!bd.isSingleton());
                } while(bd.isLazyInit());

                if (this.isFactoryBean(beanName)) {
                    bean = this.getBean("&" + beanName);
                    break;
                }

                this.getBean(beanName);
            }
        } while(!(bean instanceof FactoryBean));

        FactoryBean<?> factory = (FactoryBean)bean;
        boolean isEagerInit;
        if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
            SmartFactoryBean var10000 = (SmartFactoryBean)factory;
            ((SmartFactoryBean)factory).getClass();
            isEagerInit = (Boolean)AccessController.doPrivileged(var10000::isEagerInit, this.getAccessControlContext());
        } else {
            isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
        }

        if (isEagerInit) {
//创建对象
            this.getBean(beanName);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3)AbstractBeanFactory:getBean()

所有getBean调用的是AbstractBeanFactory
的dogetBean()

protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
    String beanName = this.transformedBeanName(name);
//试着从已经注册的所有单实例bean中看有没有这个bean,第一次创建bean是没有的
    Object sharedInstance = this.getSingleton(beanName);
    Object bean;
    if (sharedInstance != null && args == null) {
        if (this.logger.isTraceEnabled()) {
            if (this.isSingletonCurrentlyInCreation(beanName)) {
                this.logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
            } else {
                this.logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }

        bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, (RootBeanDefinition)null);
    } else {
        if (this.isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        BeanFactory parentBeanFactory = this.getParentBeanFactory();
        if (parentBeanFactory != null && !this.containsBeanDefinition(beanName)) {
            String nameToLookup = this.originalBeanName(name);
            if (parentBeanFactory instanceof AbstractBeanFactory) {
                return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
            }

            if (args != null) {
                return parentBeanFactory.getBean(nameToLookup, args);
            }

            if (requiredType != null) {
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }

            return parentBeanFactory.getBean(nameToLookup);
        }

        if (!typeCheckOnly) {
            this.markBeanAsCreated(beanName);
        }

        try {
            RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);
            this.checkMerge,dBeanDefinition(mbd, beanName, args);
//拿到所有依赖的bean,depends-on属性
            String[] dependsOn = mbd.getDependsOn();
            String[] var11;
            if (dependsOn != null) {
                var11 = dependsOn;
                int var12 = dependsOn.length;

                for(int var13 = 0; var13 < var12; ++var13) {
                    String dep = var11[var13];
                    if (this.isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }

                    this.registerDependentBean(dep, beanName);

                    try {
                        this.getBean(dep);
                    } catch (NoSuchBeanDefinitionException var24) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", var24);
                    }
                }
            }

            if (mbd.isSingleton()) {
                sharedInstance = this.getSingleton(beanName, () -> {
                    try {
                        return this.createBean(beanName, mbd, args);
                    } catch (BeansException var5) {
                        this.destroySingleton(beanName);
                        throw var5;
                    }
                });
                bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
            } else if (mbd.isPrototype()) {
                var11 = null;

                Object prototypeInstance;
                try {
                    this.beforePrototypeCreation(beanName);
                    prototypeInstance = this.createBean(beanName, mbd, args);
                } finally {
                    this.afterPrototypeCreation(beanName);
                }

                bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
            } else {
                String scopeName = mbd.getScope();
                Scope scope = (Scope)this.scopes.get(scopeName);
                if (scope == null) {
                    throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
                }

                try {
                    Object scopedInstance = scope.get(beanName, () -> {
                        this.beforePrototypeCreation(beanName);

                        Object var4;
                        try {
                            var4 = this.createBean(beanName, mbd, args);
                        } finally {
                            this.afterPrototypeCreation(beanName);
                        }

                        return var4;
                    });
                    bean = this.getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                } catch (IllegalStateException var23) {
                    throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton", var23);
                }
            }
        } catch (BeansException var26) {
            this.cleanupAfterBeanCreationFailure(beanName);
            throw var26;
        }
    }

    if (requiredType != null && !requiredType.isInstance(bean)) {
        try {
            T convertedBean = this.getTypeConverter().convertIfNecessary(bean, requiredType);
            if (convertedBean == null) {
                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
            } else {
                return convertedBean;
            }
        } catch (TypeMismatchException var25) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", var25);
            }

            throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    } else {
        return bean;
    }
}


在这里插入图片描述

在这里插入图片描述

4)getSingleton(beanName);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
        Map var4 = this.singletonObjects;
        synchronized(this.singletonObjects) {
//创建对象
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
//创建好的对象最终保存在earlySingletonObjects中
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }

    return singletonObject;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值