Spring笔记

Spring

1、Spring

Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:
控制反转)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring
MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多
著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .

image-20201204140548864

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

2、IOC(控制反转)

2.1 IOC本质

一个接口被多个类实现 ,如果把代码直接写死要是用户要请求另外别的类话就需要改代码,这显然是不现实的。

因此,我们在需要new一个对象的地方不去new它,而是交给用户,由用户选择

IOC本质:控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,我们在获取对象时,都是采用 new 的方式。是主动的。现在我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的,这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

作用:
削减计算机程序的耦合(解除我们代码中的依赖关系)。
实现:
工厂方法模式+反射

2.2 使用

1.导入依赖,maven会导入他需要的其他依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

2.编写代码,如持久层和业务层
3.创建xml文件或使用注解管理对象(导入beans名称空间下的约束)

<?xml version="1.0" encoding="UTF-8"?>
<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
       https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

导入beans约束头文件后,使用bean标签或注解去让spring创建和管理我们的对象
4.测试:

实体类:

public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}

编写beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <bean id="hello" class="cn.si.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>

</beans>
  • bean标签:

    • id为你要获取的名字
    • class为其绑定的类
  • property标签:

    • name是类中的属性
    • value是你要赋给此属性的值
    • ref:赋一个上面写的bean

例:

<bean id="user" class="cn.si.dao.UserDaoImpl"/>
<bean id="mySQL" class="cn.si.dao.UserDaoMySQLImpl"/>

<bean id="userService" class="cn.si.service.UserServiceImpl">
    <property name="userDao" ref="mySQL"/>
</bean>

测试:

public class MyTest {
    public static void main(String[] args) {
         //解析beans.xml文件 , 生成管理相应的Bean对象
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //getBean : 参数即为spring配置文件中bean的id .
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

使用spring后,我们不用再去new你所需要的对象,而是把这个过程交给spring,再去Spring中拿出来

可以传入多个参数

ApplicationContext context =newClassPathXmlApplicationContext("services.xml","daos.xml");

方法的实现需要靠set进行注入(依赖注入)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHUZW8GF-1608725368279)(Spring笔记+.assets/image-20201127132918130.png)]

总结

  • 对象由Spring创建的

  • 属性是由Spring容器设置的

  • 控制:对象由Spring来创建的

  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收

3、IOC创建对象方式

image-20201127140416070

image-20201127140459274

这里出现一个叶子才是spring导入成功

1.使用无参构造器创建

public class User {
    private String name;

    public User(){
        System.out.println("调用了无参构造器!");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<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
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="cn.si.pojo.User">
        <property name="name" value="小厮"/>
    </bean>

</beans>
import cn.si.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        System.out.println(user.toString());
    }
}

若不写构造器,程序里默认存在无参构造,在ApplicationContext生成时会把beans.xml所有的所有的类都生成。

2.使用有参构造器创建

通过参数类型

但如果两个参数同一个类型,此方法就无法使用

<bean id="user" class="cn.si.pojo.User">
    <constructor-arg type="java.lang.String" value="小厮"/>
</bean>
通过下标
<bean id="user" class="cn.si.pojo.User">
    <constructor-arg index="0" value="xiaosi"/>
</bean>
根据参数名字设置(推荐使用)
<bean id="user" class="cn.si.pojo.User">
    <constructor-arg name="name" value="xiaosi2"/>
</bean>

4、Spring配置

1.别名

<alias name="user" alias="us2"/>
<alias name="user" alias="us3"/>

alias 设置别名 , 为bean设置别名 , 可以设置多个别名,,设置后本名别名都能获取当前类,不过基本没什么用,因为bean的name可以取代它。

2、bean的配置

<!--bean就是java对象,由Spring创建和管理-->

<!--
   id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
   如果配置id,又配置了name,那么name是别名
   name可以设置多个别名,可以用逗号,分号,空格隔开
   如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;

class是bean的全限定名=包名+类名
-->
<bean id="userT" class="cn.si.pojo.UserT" name="t t1,t4;ty">
    <property name="name" value="小厮"/>
</bean>

3、import

团队开发使用,个人写beans.xml,最后再一起整合成applicationContext.xml,而且不用担心.xml里面书写了相同的bean标签的问题,会自动合并。

<import resource="{path}/beans.xml"/>

image-20201127144646075

<?xml version="1.0" encoding="UTF-8"?>
<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
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="beans.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>

</beans>

5、依赖注入

  • 依赖注入(Dependency Injection,DI)。

    ​ 依赖 : 指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .

    ​ 注入 : 指Bean对象所依赖的资源 , 由容器来设置和装配 .

1.构造器注入

前面已写,标题3。

2.set注入(重点)

bean | ref | idref | list | set | map | props | value | null

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is .

编写测试类:

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}
public class User {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
	...
}
public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        System.out.println(user.toString());
    }
}

注入实现:

<bean id="add" class="cn.si.pojo.Address">
    <property name="address" value="福建"/>
</bean>
<bean id="user" class="cn.si.pojo.User">
    
    <!--常量注入,value-->
    <property name="name" value="小厮"/>

    <!--bean注入,ref-->
    <property name="address" ref="add"/>

    <!--数组注入-->
    <property name="books">
        <array>
            <value>三体</value>
            <value>球状闪电</value>
            <value>流浪地球</value>
        </array>
    </property>

    <!--List-->
    <property name="hobbys">
        <list>
            <value>爬山</value>
            <value>游泳</value>
            <value>唱小白船</value>
        </list>
    </property>

    <!--Map-->
    <property name="card">
        <map>
            <entry key="身份证" value="111111111111111111"/>
            <entry key="银行卡" value="111222222222222111"/>
        </map>
    </property>

    <!--Set-->
    <property name="games">
        <set>
            <value>LOL</value>
            <value>OW</value>
            <value>CF</value>
        </set>
    </property>

    <!--
        null
        <property name="wife" value="">
    -->
    <property name="wife">
        <null/>
    </property>

    <!--Properties-->
    <property name="info">
        <props>
            <prop key="学号">20200101</prop>
            <prop key="性别"></prop>
            <prop key="水卡">22222222</prop>
        </props>
    </property>
</bean>

运行结果:

//        User{name='小厮',
//        address=Address{address='福建'},
//        books=[三体, 球状闪电, 流浪地球],
//        hobbys=[爬山, 游泳, 唱小白船],
//        card={
//        身份证=111111111111111111,
//        银行卡=111222222222222111
//        },
//        games=[LOL, OW, CF],
//        wife='null',
//        info={学号=20200101, 性别=男, 水卡=22222222}}

3.扩展方式注入

p命名空间

导入xml约束

xmlns:p="http://www.springframework.org/schema/p"

c命名空间

导入xml约束

xmlns:c="http://www.springframework.org/schema/c"

测试:

<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

        <!--p命名空间注入,可以直接注入属性的值:property-->
        <bean id="user2" class="cn.si.pojo.User2" p:age="20" p:name="小厮"/>

        <!--c命名空间注入,通过构造器注入:constructor-arg-->
        <bean id="user3" class="cn.si.pojo.User2" c:age="18" c:name="xiaosi"/>
</beans>
public void Test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    //可以在getBean后面声明生成的类型
    User2 user2 = context.getBean("user3", User2.class);
    System.out.println(user2);
}

属性依然要设置set方法

4.Bean的作用域

image-20201127190143642

public void Test(){
    ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
    //可以在getBean后面声明生成的类型
    User2 user2 = context.getBean("user3", User2.class);
    User2 user3 = context.getBean("user3", User2.class);
    System.out.println(user2==user3);
}
  • 单例模式(Spring的默认机制)singleton:
<bean id="user3" class="cn.si.pojo.User2" c:age="18" c:name="xiaosi" scope="singleton"/>

输出为true

  • 原型模式:每次从容器中get的时候,都会产生一个新对象 prototype
<bean id="user3" class="cn.si.pojo.User2" c:age="18" c:name="xiaosi" scope="prototype"/>

输出为false

  • 其余的只能在web开发中使用

6、Bean的自动装配

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配。

这里我们主要讲第三种:自动化的装配bean。

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。

创建项目:

public class Dog {
    public void shout(){
        System.out.println("喵~");
    }
}

public class Cat {
    public void shout(){
        System.out.println("汪~");
    }
}

public class People {
    private String name;
    private Dog dog;
    private Cat cat;
}

autowire byName (按名称自动装配)

会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid,所以需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法值一致(这里Cat方法的bean id要是改成cat111,就会报错)

<bean id="cat" class="cn.si.pojo.Cat"/>
<bean id="dog" class="cn.si.pojo.Dog"/>
<bean id="people" class="cn.si.pojo.People" autowire="byName"/>

autowire byType (按类型自动装配)

会自动在容器上下文中查找,和自己对象属性类型相同的bean,并且这个bean需要和自动注入的属性的类型一致。

<bean class="cn.si.pojo.Cat"/>
<bean class="cn.si.pojo.Dog"/>
<bean id="people" class="cn.si.pojo.People" autowire="byType"/>

使用注解

@Autowired

导入约束 xmlns:context约束

配置注解的支持 context:annotation-config/

<?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
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
        <!--向容器注册注解-->
        <context:annotation-config/>
</beans>

使用:写在相应的属性的上面

public class People {
    private String name;
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;
}
<?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
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

        <!--向容器注册注解-->
        <context:annotation-config/>

        <bean id="cat" class="cn.si.pojo.Cat"/>
        <bean id="dog" class="cn.si.pojo.Dog"/>
        <bean id="people" class="cn.si.pojo.People"/>
</beans>

但是如果是这样的话,就会报错

<bean id="cat1" class="cn.si.pojo.Cat"/>
<bean id="cat2" class="cn.si.pojo.Cat"/>
<bean id="dog1" class="cn.si.pojo.Dog"/>
<bean id="dog2" class="cn.si.pojo.Dog"/>
<bean id="people" class="cn.si.pojo.People"/>

@Autowired说到底就是先按照byType查找,如果找不到或不唯一的话再根据byName去查找,这时候就要配合另一个标签

@Qualifier

public class People {
    private String name;
    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;
    @Autowired
    @Qualifier(value = "cat1")
    private Cat cat;
    }

使用@Qualifier给它配置bean id。

扩展

@Autowired后面可以跟一个布尔值,false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;

当等于false的话类似于@Nullable,可以为空

@Resource

public class People {
    private String name;
    @Resource
    private Dog dog;
    @Resource(name = "cat1")
    private Cat cat;
}

@Resource标签不是Spring的标签,javax.annotation下的,他是先根据byName查找,找不到再由 byType。

@Resource(name = “cat1”)此标签可通过name传bean id,name默认为空。

小结

@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

7、使用注解开发

在Spring4之后,想要使用注解形式,必须得要引入aop的包

image-20201127205132921

引入约束

<?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
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

</beans>

Bean的实现

@Component和@Bean的目的是一样的,都是注册bean到Spring容器中。

@Component VS @Bean

@Component 和 它的子类型(@Controller, @Service and @Repository)注释在类上。告诉Spring,我是一个bean,通过类路径扫描自动检测并注入到Spring容器中。

@Bean不能注释在类上,只能用于在配置类中显式声明单个bean。意思就是,我要获取这个bean的时候,spring要按照这种方式去获取这个bean。默认情况下@Bean注释的方法名作为对象的名字,也可以用name属性定义对象的名字。

@Bean要和@Configuration一起使用:

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

注意:@Configuration注解的配置类有如下要求:

  1. @Configuration不可以是final类型;
  2. @Configuration不可以是匿名类;
  3. 嵌套的configuration必须是静态类。
  • @Component的使用:

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

1、配置扫描哪些包下的注解

<!--向容器注册注解-->
<context:annotation-config/>
<!--指定注解扫描包-->
<context:component-scan base-package="cn.si.pojo"/>

2、在指定包下编写类,增加注解

@Component("user")//可以赋值更改beanid,不然为首字母小写的他的类名
@Component
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
   public String name;
}

3、测试

@Test
public void test(){
   ApplicationContext applicationContext =
       new ClassPathXmlApplicationContext("beans.xml");
   User user = (User) applicationContext.getBean("user");
   System.out.println(user.name);
}
  • @Bean使用:

  • //这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component
    //@Configurable代表这是一个配置类,就类似于beans.xml   <beans></beans>
    @Configurable
    @ComponentScan("cn.si")  //扫描
    @Import(Config.class)    //引入别的配置文件
    public class SiConfig {
    
        通过方法注册一个bean,这里的返回值就Bean的类型(bean标签中的class属性),方法名就是bean的id!
        @Bean
        public User getUser(){
            return new User();
        }
    }
    

属性注入

使用注解注入属性

1、可以不用提供set方法,直接在直接名上添加@value(“值”),如果提供了set方法,在set方法上添加@value(“值”)

@Component
public class User {
    // 相当于配置文件中 <property name="name" value="xisosi"/>
    @Value("小厮")
    private String name;

    public String getName() {
        return name;
    }
//	 @Value("小厮")也可放在set上面
    public void setName(String name) {
        this.name = name;
    }
}

衍生注解

我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

自动装配注解

@Autowired
@Resource

作用域

@Component
@Scope("singleton")
public class User {
    @Value("小厮")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

使用@Scope,里面的属性跟上面Bean作用域一样。

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

小结

xml与注解

​ xml适用于任何场合,维护简单

​ 注解不是自己的类的东西无法使用,维护相对复杂

最佳使用:使用xml管理bean,注解只负责完成属性的注入。

记得开启注解支持和扫描

<!--向容器注册注解-->
<context:annotation-config/>
<!--指定注解扫描包-->
<context:component-scan base-package="cn.si"/>

8、使用java的方式配置Spring(不使用.xml)

建一个实体类,使用@Component注解(实现bean的注入)

package cn.si.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//将这个类标注为Spring的一个组件,放到容器中!
@Component
public class User {
    private String name;


    public String getName() {
        return name;
    }
    @Value("小厮")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

编写一个配置类

package cn.si.config;

import cn.si.pojo.User;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component
//@Configurable代表这是一个配置类,就类似于beans.xml   <beans></beans>
@Configurable
@ComponentScan("cn.si")  //扫描
@Import(Config.class)    //引入别的配置文件
public class SiConfig {

    通过方法注册一个bean,这里的返回值就Bean的类型(bean标签中的class属性),方法名就是bean的id!
    @Bean
    public User getUser(){
        return new User();
    }
}

测试类:

public class MyTest {
    @Test
    public void test(){
      ApplicationContext context = new AnnotationConfigApplicationContext(SiConfig.class);
        User getUser = (User) context.getBean("getUser");
        System.out.println(getUser);
    }
}

因为使用的是java来配置Spring,所以这里使用AnnotationConfig来生成。这种纯java的配置方式,在SpringBoot中随处可见

9、代理模式

(可看代理模式笔记)

静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

实现:

抽象角色

public interface Rent {
    public void rent();
}

真实角色:

public class Host implements Rent{
    //房东要出租房子
    public void rent() {
        System.out.println("我有房子要出租");
    }
}

代理角色:

public class Proxy implements Rent {
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        host.rent();
        seeHouse();
        fare();
    }

    //看房
    public void seeHouse(){
        System.out.println("带房客看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

客户:

public class Client {
    public static void main(String[] args) {
        //房东租房
        Host host=new Host();
        //中介帮忙
        Proxy proxy=new Proxy(host);
        //客户找中介
        proxy.rent();
    }
}

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类和代理要一一对应,就是说加入你有一百个真实角色就要有一百个代理,代码量翻倍

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

静态代理的用法:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个项目");
    }

    public void delete() {
        System.out.println("删除了一个项目");
    }

    public void update() {
        System.out.println("修改了一个项目");
    }

    public void query() {
        System.out.println("查询了一个项目");
    }
}
public class MyUser {
    public static void main(String[] args) {
        UserServiceImpl user=new UserServiceImpl();
        user.add();
    }
}

如果这个时候要让我们在增删改查的时候输出日志,就可以使用代理模式(在公司中改原有代码是大忌,所以需要面向切面的编程(AOP))

写一个代理类:

public class UserServiceImplProxy implements UserService {

    private UserServiceImpl userImpl;

    public void setUserImpl(UserServiceImpl userImpl) {
        this.userImpl = userImpl;
    }

    public void add() {
        log("add");
        userImpl.add();
    }

    public void delete() {
        log("delete");
        userImpl.delete();
    }

    public void update() {
        log("update");
        userImpl.update();
    }

    public void query() {
        log("query");
        userImpl.query();
    }

    private void log(String Msg){
        System.out.println("[INFO]执行了"+Msg+"操作");
    }
}

测试:

public class MyUser {
    public static void main(String[] args) {
        UserServiceImpl user=new UserServiceImpl();
        UserServiceImplProxy proxy = new UserServiceImplProxy();
        proxy.setUserImpl(user);
        proxy.delete();
    }
}

动态代理

  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理–cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy

编写通用代理类(实现InvocationHandler接口)

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

public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    //生成得到 代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //本质:利用反射
        Object result = method.invoke(target, args);
        return result;
    }

}

image-20201128143054758

这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:

  • loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
  • interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
  • h:一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。

image-20201128143336375

Object invoke(Object proxy, 方法 method, Object[] args)//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

测试Rent:

public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host=new Host();

        //代理角色:不存在,生成
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(host);//设置要代理的对象

        //动态生成代理类
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }

}

测试UserService:

public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl user=new UserServiceImpl();

        //代理角色:不存在,生成
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setTarget(user);//设置要代理的对象

        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();
        proxy.delete();
    }

}

可以在配置文件中添加所需要的方法:

image-20201128144052343

动态代理的好处

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口!
  • 核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!、

10、AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtuW40Sb-1608725368286)(Spring笔记+.assets/image-20201128134700301.png)]

在aop:aspect标签里使用aop:xxx标签
配置对应的通知类型:

  • aop:before前置通知
  • aop:after-returning后置通知
  • aop:after-throwing异常通知
  • aop:after最终通知
  • aop:around环绕通知(不与其他一起使用,独立使用)

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">

</beans>
第一种方式:

通过 Spring API 接口实现

实现过程:

编写接口:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

实现类:

public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个项目");
    }

    public void delete() {
        System.out.println("删除了一个项目");
    }

    public void update() {
        System.out.println("修改了一个项目");
    }

    public void query() {
        System.out.println("查询了一个项目");
    }
}

前置日志(实现MethodBeforeAdvice接口):

public class BeforeLog implements MethodBeforeAdvice {
    //method : 要执行的目标对象的方法
    //args : 被调用的方法的参数
    //target : 目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println( target.getClass().getName() + "的" + method.getName() + "方法被执行了");
    }
}

后置日志(实现AfterReturningAdvice接口):

public class AfterLog implements AfterReturningAdvice {
    //returnValue 返回值
    //method被调用的方法
    //args 被调用的方法的对象的参数
    //target 被调用的目标对象
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + target.getClass().getName()
                +"的"+method.getName()+"方法,"
                +"返回值:"+returnValue);
    }
}

编写applicationContext.xml,注册bean并实现aop

<!--注册bean-->
<bean id="userService" class="cn.si.service.UserServiceImpl"/>
<bean id="beforeLog" class="cn.si.log.BeforeLog"/>
<bean id="afterLog" class="cn.si.log.AfterLog"/>

<!--==================方式一:通过API接口=====================-->
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法-->
    <aop:pointcut id="pointcut" expression="execution(* cn.si.service.UserServiceImpl.*(..))"/>
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

execution:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?) 

括号中各个pattern分别表示:

  • 修饰符匹配(modifier-pattern?)
  • 返回值匹配(ret-type-pattern)可以为*表示任何返回值,全路径的类名等
  • 类路径匹配(declaring-type-pattern?)
  • 方法名匹配(name-pattern)可以指定方法名 或者 代表所有, set 代表以set开头的所有方法
  • 参数匹配((param-pattern))可以指定具体的参数类型,多个参数间用“,”隔开,各个参数也可以用“”来表示匹配任意类型的参数,如(String)表示匹配一个String参数的方法;(,String) 表示匹配有两个参数的方法,第一个参数可以是任意类型,而第二个参数是String类型;可以用(…)表示零个或多个任意参数
  • 异常类型匹配(throws-pattern?)
  • 其中后面跟着“?”的是可选项
//表示匹配所有方法  
1)execution(* *(..))  
//表示匹配com.savage.server.UserService中所有的公有方法  
2)execution(public * com. savage.service.UserService.*(..))  
//表示匹配com.savage.server包及其子包下的所有方法 
3)execution(* com.savage.server..*.*(..))
第二种方式

通过自定义类实现

public class DiyProxy {

    public void before(){
        System.out.println("============执行前============");
    }

    public void after(){
        System.out.println("==============执行后===============");
    }
}

 <!--==================方式二:通过自定义类=====================-->
    <bean id="diy" class="cn.si.diy.DiyProxy"/>

    <aop:config>
    <!--自定义切面,ref要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* cn.si.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
第三种方式

使用注解实现

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AnnotationPointcut {

    @Before("execution(* cn.si.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("调用前-----------");
    }

    @After("execution(* cn.si.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("调用后-----------");
    }

    @Around("execution(* cn.si.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

        //执行目标方法
        Object proceed = jp.proceed();

        System.out.println("环绕后");
    }
}

@Aspect:开启AOP,作用是把当前类标识为一个切面供容器读取

@Before(“execution(* cn.si.service.UserServiceImpl.*(…))”)标识一个前置增强方法标识一个前置增强方法

@After: final增强,不管是抛出异常或者正常退出都会执行

@Around:环绕增强,相当于MethodInterceptor

<!--==================方式三:通过注解=====================-->
<bean id="anno" class="cn.si.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

aop:aspectj-autoproxy/开启AOP注解支持

11、Spring整合Mybatis

你所需要导入的所有包

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!--    mysql驱动    -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.17</version>
</dependency>
<!--    Mybatis    -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.16</version>
</dependency>

整合方式一:

SqlSessionTemplate完全代替SqlSession

可能会报错:

image-20201128205013362

image-20201128205047508

配置Spring整合mybatis(可专门建一个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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置数据源替换mybatis的数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!--配置SqlSessionFactory,关联MyBatis-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!--关联Mybatis-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:cn/si/Mapper/*.xml"/>
    </bean>

    <!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--利用构造器注入,因为没提供set注入的方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
</beans>

增加接口的实现类;私有化sqlSessionTemplate

import cn.si.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> getUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.getUser();
    }
}

注册实现类的bean,这里我另写了一个xml,引入上面配置好的mybatis整合:

<?xml version="1.0" encoding="UTF-8"?>
<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
       https://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="spring-mybatis.xml"/>

    <bean id="user2" class="cn.si.Mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
</beans>

测试:

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapperImpl user2 = context.getBean("user2", UserMapperImpl.class);
        List<User> userList = user2.getUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}

mybatis的核心配置文件基本被取代,如果你愿意,甚至可以不用mybatis的核心配置文件,spring可以把他全部整合:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration-->

<configuration>
    <typeAliases>
        <package name="cn.si.pojo"/>
    </typeAliases>
    
</configuration>
比较:

整合

<!--配置数据源替换mybatis的数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

未整合:

<dataSource type="POOLED">
    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</dataSource>

整合

<!--配置SqlSessionFactory,关联MyBatis-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--关联Mybatis-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:cn/si/Mapper/*.xml"/>
</bean>

未整合

 private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
<mappers>
    <mapper resource="cn/si/Mapper/UserMapper.xml"/>
</mappers>

整合

<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--利用构造器注入,因为没提供set注入的方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

未整合

public static SqlSession getSqlSession(){
    return sqlSessionFactory.openSession();
}

整合方式二:

通过继承SqlSessionDaoSupport实现,只需要传入sqlSessionFactory,可以省略方式一的:

<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--利用构造器注入,因为没提供set注入的方法-->
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

实现:

import cn.si.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {

    public List<User> getUser() {
        SqlSession session = getSqlSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        return mapper.getUser();
    }
}
<bean id="user3" class="cn.si.Mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试类一样。

12、事务

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据的完整性和一致性。

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

  1. 原子性(atomicity)

    • 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
  2. 一致性(consistency)

    • 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
  3. 隔离性(isolation)

    • 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
  4. 持久性(durability)

    • 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.si.Mapper.UserMapper">
    <select id="getUser" resultType="user">
        select * from user
    </select>

    <insert id="addUser" parameterType="user">
        insert into user(id,name,pwd) values(#{id},#{name},#{pwd})
    </insert>

    <delete id="deleteUser" parameterType="int">
        deletes from user where id=#{id}
    </delete>
</mapper>

这里delete写错了

public List<User> getUser() {
    User user = new User(4,"小明","123456");
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    mapper.addUser(user);
    mapper.deleteUser(4);
    return mapper.getUser();
}

实现类中调用它,虽然报sql错误,但是还是把新增的修改提交上去了,这种不应该存在。

使用事务:

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

JDBC事务:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

这里的name可以分add、update、query、delete等,query可以设置只读read-only=“true”

propagation(传播)默认为"REQUIRED"

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

织入AOP事务

<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* cn.si.Mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

配置事务后出错不会提交,完成。

测试类:

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper bean = (UserMapper) context.getBean("userImp");
        List<User> userList = bean.getUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
出现的错误

UserMapper bean = (UserMapper) context.getBean(“userImp”);

记住这里一定要new接口,不然会报错:

com.sun.proxy.$Proxy11 cannot be cast to
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(4);
return mapper.getUser();
}


实现类中调用它,虽然报sql错误,但是还是把新增的修改提交上去了,这种不应该存在。



使用事务:

```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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

JDBC事务:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

这里的name可以分add、update、query、delete等,query可以设置只读read-only=“true”

propagation(传播)默认为"REQUIRED"

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

织入AOP事务

<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* cn.si.Mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

配置事务后出错不会提交,完成。

测试类:

public class MyTest {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper bean = (UserMapper) context.getBean("userImp");
        List<User> userList = bean.getUser();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
出现的错误

UserMapper bean = (UserMapper) context.getBean(“userImp”);

记住这里一定要new接口,不然会报错:

com.sun.proxy.$Proxy11 cannot be cast to

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值