ssm-spring

官方文档

https://docs.spring.io/spring-framework/docs/5.3.9/reference/html/

狂神B站
https://www.bilibili.com/video/BV1WE411d7Dv

ssm-spring

一、spring简介

1.介绍

Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器(lightweight container)

Spring是面向切面编程(AOP)控制反转(IoC)的容器框架。

2.历史

2002推出spring雏形:interface21

2004.3.4,发布spring正式版1.0

3.优点总结

  1. 开源免费框架(容器)
  2. 轻量级非入侵式的
  3. 控制反转(IOC),面向切面编程(AOP)
  4. 支持事务处理,对框架整合的支持

缺陷:发展太久之后,违背理念,配置十分繁琐,“配置地狱”

4.使用须知

当前的spring=spring framework

导包spring-webmvc,会将相应的包一并下载

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

导包spring-jdbc--和mybatis整合要用的包

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.9</version>
</dependency>

5.spring组成

Spring框架主要由七部分组成,分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。

6.拓展内容

springboot:

  • 快速开发的脚手架(框架)
  • 快速开发单个微服务
  • 约定大于配置

springcloud:基于springboot实现

springboot学习前提:spring+springmvc

7.约定大于配置

遵循一些默认的约定:如maven的src/main/resources目录用来存放资源文件,遵循它,即为约定。

而配置是自定义文件夹,再进行文件位置的配置,导致繁琐。

遵循约定而不需配置---约定大于配置

二、IOC概念

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,一种思想

IOC容器作为第三方使依赖的对象之间解耦

控制反转:获得依赖的对象的过程被反转了,即A当需要B对象使,IOC会主动创建一个B对象注入到A需要的地方

依赖注入(DI(Dependency Injection)):就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中--实现IOC的一种方式

IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。

所有的类都会在spring容器中登记

所有的类的创建、销毁都由spring来控制

IOC原型:

在一个对象需要另一个对象时,通过接口的set来或取到该对象,把主动权交给调用对象方法的一方

private UserDao userDao;
//单需要userDao接口的某个实现类时,将实现类的对象传入set,实现注入--拓展性(当UserDao有更多的实现类时,不许要改动此处的代码),解耦(避免该类声明另一个对象)
public void setUserDao(UserDao userDao){
this.userDao=userDao
}

spring使用多种方式实现了IOC

spring容器在初始化时先读取配置文件applicationContext.xml,将对象的所有信息存入容器中,在需要使用的时候再从容器中实例化取出

三、AOP概念

Aspect Oriented Programming的缩写,AOP是OOP(面向对象编程)(Object Oriented Programming)的延续

可以通过预编译方式和运行其动态代理实现在不修改源代码的情况下给程序动态统一添加某种特定功能的一种技术。

主要功能:

日志记录,性能统计,安全控制,事务处理,异常处理等等。

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

四、HelloSpring

1.bean.xml(applicationContext.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    使用spring来创建我们的对象,这些要被创建的对象都为bean-->
    <bean id="User" class="com.kuang.pojo.User">
        <property name="id" value="18201199"></property>
        <property name="name" value="你大爷" ></property>
        <!--  故意没有配置password属性,结果为null-->
    </bean>
</beans>

2.实体类

package com.kuang.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
}

必须要含有无参构造方法,bean.xml中构造对象是由无参构造方法构造对象,再对对象设置值

3.测试方法

import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testSpring {
    public static void main(String[] args) {
        //获取上下文的spring对象
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //从spring容器中取出user实例化对象--用id取出
        User user = (User) context.getBean("User");

        System.out.println(user);
    }
}
  1. 将要被实例化的类注册在bean文件中

  2. 通过ApplicationContext读取bean.xml配置文件获取配置信息

  3. 再getBean("name")来获取该类的实例化对象

  4. 对象创建时间:ApplicationContext读取配置文时

spring容器本质上就是一个生产工厂,只需要注册信息,在使用的时候调用出来即可。

静态的实例化对象的值有什么用?如何动态的配置需要实例化对象的值?

五、spring配置

1.alias(别名)

给一个已经存在的bean的id取一个别名--可以同别名获取对象,也可以用本名

<alias name="User" alias="resu"></alias>

2.bean

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    使用spring来创建我们的对象,这些要被创建的对象都为bean-->
    <bean id="User" class="com.kuang.pojo.User" name="resu,u2">
        <constructor-arg name="id" value="18201119"/>
        <constructor-arg name="name" value="你大爷"/>
        <constructor-arg name="password" value="123456"/>
    </bean>
</beans>

id:唯一标识符

class:包路径+类名

name:别名,可以同时取多个别名,用,或者空格或者;等分割(alias一对一,alias还有什么用???)

constructor-arg:构造参数

property:属性,属性property为基本属性类型时,使用value,为对象类型时且bean.xml中已经注册,可以使用ref直接指定

3.import

团队开发,将多个配置文件导入合并为一个

<import resource="bean.xml"></import>

六、IOC实现-依赖注入(DI)

依赖注入(DI(Dependency Injection)):就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中--实现IOC的一种方式

1.基于构造器的依赖注入

使用有参构造函数:constructor-arg标签是构造函数的值

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    使用spring来创建我们的对象,这些要被创建的对象都为bean-->
    <bean id="User" class="com.kuang.pojo.User">
        <constructor-arg name="id" value="18201119"/>
        <constructor-arg name="name" value="你大爷"/>
        <constructor-arg name="password" value="123456"/>
    </bean>
</beans>

除了使用name指定值,还可以使用index下标指定,index=0指构造参数的第一个参数,还可以使用type数据类型来进行赋值(多个同类型的怎么办?--无法使用该方法进行赋值)

 <constructor-arg index="0" value="18201119"/>
 <constructor-arg type="java.lang.String" value="18201119"/>

2.基于setter的依赖注入(重点)

依赖:对象的创建依赖与容器

注入:对象的所有属性由容器注入

各种复杂属性的注入方式:

测试对象:

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}

bean注入:

    <bean id="student" class="com.kuang.pojo.Student">
<!--        1.普通值注入,value-->
        <property name="name" value="邝飞"></property>
<!--        2.bean注入,ref-->
        <property name="address" ref="address"></property>
<!--        3.字符数组注入,array和value-->
        <property name="books">
            <array>
                <value>《红楼梦》</value>
                <value>《西游记》</value>
                <value>《三国演义》</value>
                <value>《水浒传》</value>
            </array>
        </property>
<!--        4.List注入,array和value-->
        <property name="hobbies">
            <list>
                <value>吃饭</value>
                <value>睡觉</value>
                <value>打豆豆</value>
            </list>
        </property>
<!--        5.map注入,map和entry-->
        <property name="card">
            <map>
                <entry key="身份证" value="123456"></entry>
                <entry key="驾驶证" value="321654"></entry>
            </map>
        </property>
<!--        6.set注入,set和value-->
        <property name="games">
            <set>
                <value>王者农药</value>
                <value>吃鸡</value>
            </set>
        </property>
<!--        7.空值注入,不进行注入也是null-->
        <property name="wife">
            <null></null>
        </property>
<!--        8.Properties配置文件注入-->
        <property name="info">
            <props>
                <prop key="学号">18201113</prop>
                <prop key="名字">邝飞</prop>
                <prop key="密码">1234566</prop>
            </props>
        </property>
    </bean>
    <bean id="address" class="com.kuang.pojo.Address">
        <property name="address" value="江西"></property>
    </bean>

3.拓展方式

p命名空间:代替setter方式的注入

beans中导入

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

p的作用是代替掉property

<bean id="student" class="com.kuang.pojo.Student" p:name="邝飞">

c命名空间:代替构造方法方式的注入

用法一致

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

c的作用是代替掉构造方法标签constructor-arg

<bean id="student" class="com.kuang.pojo.Student" c:name="邝飞">

4.bean作用域(生命周期)

Scope Description
singleton (单例) (默认)将每个 Spring IoC 容器的单个 bean 定义范围限定为单个对象实例。
prototype (原型) 将单个 bean 定义的作用域限定为任意数量的对象实例。
request 将单个 bean 定义的范围限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有一个在单个 bean 定义后面创建的 bean 实例。仅在可感知网络的 Spring ApplicationContext中有效。
session 将单个 bean 定义的范围限定为 HTTP Session的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
application 将单个 bean 定义的范围限定为ServletContext的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。
websocket 将单个 bean 定义的范围限定为WebSocket的生命周期。仅在可感知网络的 Spring ApplicationContext上下文中有效。

在bean的scope设置属性

1.单例模式:每次请求的实例对象是同一个

<!--默认-->
<bean id="student" class="com.kuang.pojo.Student" scope="singleton"/>

2.原型模式:每次请求的实例对象是新创建的

<bean id="student" class="com.kuang.pojo.Student" scope="prototype"/>

3.session,application,websocket仅在web开发中使用

七、Bean的自动装配

spring满足bean依赖的一种方式

在上下文中自动寻找,并自动给bean装配属性

三种装配方式:

  • xml中显式配置
  • java中显式配置
  • 隐式自动装配

1.xml中显式配置

在bean中设置autowire属性--官方版本太新--忽略了此方法

autowire:

byName

  • 会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanid !
  • 要保证所有bean的id唯一,并且和set后的值一致
<bean id="student" class="com.kuang.pojo.Student" autowire="byName">

byType

  • 会自动在容器上下文中查找,和自己对象属性类型相同的bean(class) !
  • 要保证bean的class唯一,并且bean要和注入的属性类型一样
<bean id="student" class="com.kuang.pojo.Student" autowire="byType">

2.注解自动装配

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

    <context:annotation-config/>

</beans>

2.在对应的属性或者构造方法或者set方法加上@Autowired注解

@Autowired//不推荐变量注解注入//推荐使用set或者构造方法注入
private Address address;

如果将@Autowired注解放在属性或者构造方法上,则无需再些set方法(?不理解)

如果设置@Autowired的required值为false,则该对象可以为空

@Autowired(required=false)

@Nullable注解在方法输入前配置,则该对象可以为空

public void setAddress(@Nullable Address address);

当多个bean名称重复,clsaa相同,无法自动识别对应的对象时:

在@Autowired注解加上@Qualifier()可以通过id指定一个具体的bean

@Autowired
@Qualifier(value="address")//通过bean的id指定具体的bean
private Address address;

3.@Resource---可以代替@Autowired进行使用

@Resource
private Address address;

要求名字或class一致

@Resource(name="adress")//指定具体的bean
private Address address;

@Autowired常用,@Resource功能强大一点,不需要其他注解的补充

两者区别:

  • 都是用于自动装配,都可以放在属性上
  • @Autowired通过byType方式实现,必须要求这个对象存在
  • @Resource默认使用byName方式,其次使用byType方式实现,两个都找不到报错

八、注解开发

通过注解代替重复简单的配置内容

spring4之后,要导入aop的包才可以进行注解开发

使用注解需要在beans.xml(applicationContext)中加如contex的约束和注解支持:

<?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.xsd">

    <!--扫描指定的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.kuang"/>
    <!--注解自动装配-->
    <context:annotation-config/>
</beans>

1.@component

这个注解在要在appllicationContext.xml进行bean注册的类上加上,等价于类的bean配置

@component
public class User {
    private int id;
    private String name;
    private String password;
}
<bean id="student" class="com.kuang.pojo.Student" ></bean>

@component的衍生注解:衍生注解功能一致,针对web中会进行dao,service,controller分层

  1. dao:@Repository
  2. service:@Service
  3. controller:@Controller

2.@value

属性或者在set方法上加上这个注解配置属性的值。

@component
public class User {
    @value("18201113")
    private int id;
    @value("邝飞")
    private String name;
    @value("123456")
    private String password;
}

@value相当于bean中的property标签

3.@Autowired@Resource

自动装配注解,详情看七、bean的自动装配---这两个注解针对对象中的对象属性使用

4.@Scope

作用域注解--配置这个bean的作用域为单例,原型,或者是web

详情看六、4bean的作用域

5.总结

xml更加万能,适用于任何场景。

注解不是自己的类使用不了,维护相对复杂。

最佳实现:

xml用来管理bean

注解只用于完成属性的注入

九、使用java配置spring

用纯java代替spring的applicationContext.xml配置文件,用一个java类来配置bean

javaconfig是spring的一个子功能,spring之后,它成为了核心功能----更灵活。

java配置类本身也在spring的容器中

springboot中很多java配置方式

1.手动装配

myConfig.java

通过注解进行注册

@Configuration:设定该类为配置类

package com.kuang.config;

import com.kuang.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class myConfig {
    @Bean
    public User getUser(){
        return new User();
    }
}

User.java

只注入一个id值

package com.kuang.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @Value("1820113333")
    private int id;
    private String name;
    private String password;
}

2.自动扫描

myConfig.java

如果在配置类中加入@ComponentScan("com.kuang.pojo")扫描注解,并在实体类中加上@Component注解即可进行bean扫描

package com.kuang.config;

import com.kuang.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.kuang.pojo")
public class myConfig {
}

User.java

package com.kuang.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
    @Value("1820113333")
    private int id;
    private String name;
    private String password;
}

testSpring

获取myConfig类文件,通过方法名获取对象

ApplicationContext context = new AnnotationConfigApplicationContext(myConfig.class);
User user=context.getBean("getUser",User.class);
System.out.println(user);

@import注解可以使不同的config文件相互结合---等同于xml的import标签导入其他xml文件

十、AOP底层-代理模式

代理模式:(运行时才创建具体对象)

  • 静态代理
  • 动态代理

1.静态代理

用一个代理类代替具体类进行调用,而代理类方法调用具体的类方法(实现同一个方法接口即可)

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

2.动态代理

动态代理的代理类是动态生成

动态代理两类:

  • 基于接口的动态代理--jdk的动态代理----使用这个
  • 基于类的动态代理--cglib
  • java字节码实现动态代理:javasist

了解两个类:

Proxy(代理)-类

静态方法Proxy.newProxyInstance();用于动态创建代理类。Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

调用Proxy的静态方法newProxyInstance得到实例化的代理类对象

方法参数:

ClassLoader loader:类加载器

Class<?>[] interfaces:接口参数,要代理的接口h

InvocationHandler h:InvocationHandler 接口实现类

InvocationHandler-接口

实现方法invoke();

InvocationHandler是由代理实例的调用处理程序实现的接口。

每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法

ProxyInvocationHandler实现InvocationHandler接口的invoke方法

方法参数:

Object proxy:代理类实例化对象

Method method:代理类实例化对象使用的方法

Object[] args:各种参数

实现

ProxyInvocationHandler.java

package com.kuang.test;

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

public class ProxyInvocationHandler implements InvocationHandler {

    Object object;

    public void setObject(Object object) {
        this.object = object;
    }

    //生成代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader()
                                      ,object.getClass().getInterfaces(),this);
    }

    //处理代理实例对象,返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res=method.invoke(object,args);
        return res;
    }
}

Rent接口:

package com.kuang.test;

public interface Rent {
    public void rent();
}

host被代理类:

package com.kuang.test;

public class Host implements Rent {

    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

main:

package com.kuang.test;

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        //方法接口,实例真实角色
        Rent host=new Host();
        //声明InvocationHandler
        ProxyInvocationHandler proxyInvocationHandler=new ProxyInvocationHandler();
        //将被代理的接口实例置入InvocationHandler
        proxyInvocationHandler.setObject(host);
        //生成代理实例
        Rent hostProxy= (Rent) proxyInvocationHandler.getProxy();
        //执行代理方法->执行方法时,调用InvocationHandler的invok方法->调用Host的反射获得的方法
        hostProxy.rent();
    }

}

十一、AOP实现

提供声明式事务,允许用户自定义切面

·横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等....

·切面(ASPECT)︰横切关注点被模块化的特殊对象。即,它是一个类。

·通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

·目标((Target)︰被通知对象。

·代理(Proxy)︰向目标对象应用通知之后创建的对象。

·切入点(PointCut):切面通知执行的“地点"的定义。·连接点(JointPoint) :与切入点匹配的执行点。

图解:通过代理对象来完成每个业务的前置后置操作

各种通知类型:

使用spring实现AOP:

提供声明式事务,允许用户自定义切面

导入aop的包

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
    <scope>runtime</scope>
</dependency>

1.使用spring原生接口

实现前置后置的日志功能:

log.java(前置日志)

package com.kuang.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {
    //前置日志

    //method要执行的目标对象的方法
    //args参数
    // target目标对象
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法名"+method.getName());
        System.out.println("对象名"+target.getClass().getName());
    }
}

AfterLog.java(后置日志)

package com.kuang.log;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    //后置日志

    //结果返回后
    //returnValue返回值
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法名"+method.getName());
        System.out.println("对象名"+target.getClass().getName());
        System.out.println("结果"+returnValue);
    }
}

userService接口

package com.kuang.service;

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

UserServiceImpl实现类:

package com.kuang.service;

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("add");
    }

    @Override
    public void delete() {
        System.out.println("delete");
    }

    @Override
    public void update() {
        System.out.println("update");
    }

    @Override
    public void select() {
        System.out.println("select");
    }
}

applicationContext.xml配置bean,配置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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--    注册bean-->
    <bean id="UserServiceImpl" class="com.kuang.service.UserServiceImpl"></bean>
    <bean id="log" class="com.kuang.log.Log"></bean>
    <bean id="afterLog" class="com.kuang.log.AfterLog"></bean>

<!--    方式一:使用spring 原生api-->
<!--    配置AOP:导入AOP约束,config配置-->
    <aop:config>
<!--        切入点:在哪里执行:expression表达式:execution(要执行的位置 *修饰词(public) *返回值 *类名 *方法名 *参数)-->
<!--        com.kuang.service.UserServiceImpl.*(..)(这个类下的所有方法,  ..代表任意参数)  -->
        <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!--        执行环绕增加-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
    </aop:config>
</beans>

进行测试:

package com.kuang;

import com.kuang.service.UserService;
import com.kuang.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {
    public static void main(String[] args) {
        //获取xml的bean配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取service实现类对象实例
        //动态代理的是接口--强制转换
        UserService userService = (UserService) context.getBean("UserServiceImpl");
        userService.add();
    }
}

结果:前置输出,add,后置输出

2.使用自定义类实现

自定义类

package com.kuang.diy;

public class DiyPointCut {
    //在方法前后加入日志

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

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

}

将方式一部分替换

<!--    方式二-->

    <bean id="diy" class="com.kuang.diy.DiyPointCut"></bean>

    <aop:config>
<!--        切面,ref引用类  -->
        <aop:aspect ref="diy">
<!--            切入点-->
            <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
<!--            指定前后执行方法-->
            <aop:before method="before" pointcut-ref="pointcut"></aop:before>
            <aop:after method="after" pointcut-ref="pointcut"></aop:after>
        </aop:aspect>
    </aop:config>

测试:

3.注解实现

注解实现的类

package com.kuang.diy;


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;

//使用注解方式实现AOP
@Aspect
public class AnnotationPointCut {
    //值是切入点
    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("注解前");
    }

    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("注解后");
    }

    //在环绕增中,可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕前");
        //获得签名
        System.out.println(proceedingJoinPoint.getSignature());
        //进行执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕后");

    }

}

xml配置

<!--    方式三-->
<!--    注册bean-->
    <bean id="AnnotationPointCut" class="com.kuang.diy.AnnotationPointCut"></bean>
<!--    开启aop注解支持-->
    <aop:aspectj-autoproxy/>

注意:环绕在before前执行,在after后执行,在外面一层执行

4.声明式事务

1.事务

将一组业务作为原子操作,确保数据一致性完整性

ACID原则:

原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。

一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

2.spring事务
  • 声明式事务:AOP的应用,不影响原有代码--交由容器管理
  • 编程式事务

事务--使一串操作中途出现错误的时候,全部否决,不会执行成一半的样子.

十二、整合mybatis

1.导入相关包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>test_mybatis</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>test_spring_mybatis</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!--mybatis持久化框架-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <!-- spring-webmvc的包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.9</version>
        </dependency>
        <!-- spring操作数据库的包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.9</version>
        </dependency>
        <!--aop的包-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
            <!--            <scope>runtime</scope>-->
        </dependency>
        <!--spring-mybatis整合的包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>
        <!--        lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

一、mybatis内容

  1. 编写实体类
  2. 编写核心配置文件
  3. 编写接口
  4. 编写Mapper.xml
  5. 测试

二、mybatis-spring

http://mybatis.org/spring/zh/index.html

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类。

方式一

1.进行配置

spring-mybatis配置文件:spring管理mybatis,替代mybatis-config文件大部分功能

SqlSessionTemplate是SqlSession的实现,线程安全

<?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
       http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--    配置mybatis的工具类,将SqlSession的创建托管给spring-->
<!--    datasource:使用spring的数据源替换mybatis的配置   c3p0 dbcp druid-->
<!--    使用spring提供的jdbc-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306?mybatis/useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
<!--    SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
<!--        绑定mybatis配置文件=将两个配置文件连接起来-->
        <property name="configLocation" value="mybatis-config.xml"></property>
<!--        注册Mapper=代替mybatis中的注册-->
        <property name="mapperLocations" value="classpath:com/kuang/dao/*.xml"></property>
    </bean>
<!--    SqlSession-->
<!--    SqlSessionTemplate(SqlSession模板)=SqlSession-->
    <bean id="SqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--        只能通过构造器注入sqlSessionFactory,没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
    </bean>
</beans>

sping总配置文件:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <import resource="spring-mybatis.xml"></import>

    <!--    注册实现类-->
    <bean id="UserMapperImpl" class="com.kuang.dao.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="SqlSessionTemplate"></property>
    </bean>
</beans>
2.编写实现类UserMapperImpl

实体类:

package com.kuang.pojo;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

接口UserMapper:

package com.kuang.dao;

import com.kuang.pojo.User;

import java.util.List;
import java.util.Map;

public interface UserMapper {//数据操作Dao

    public List<User> getUserList();//查全部
    public User getUserById(int id);//查单个
    public int addUser(User user);//增
    public int deleteUser(int id);//删
    public int updateUser(User user);//改
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.dao.UserMapper">
    <select id="getUserList" resultType="com.kuang.pojo.User">
        select *
        from mybatis.user
    </select>

    <select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
        select *
        from mybatis.user
        where id = #{id}
    </select>

    <insert id="addUser" parameterType="com.kuang.pojo.User">
        insert into mybatis.user (id, name, pwd)
        values (#{id}, #{name}, #{pwd})
    </insert>

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

    <update id="updateUser" parameterType="com.kuang.pojo.User">
        update mybatis.user
        set name = #{name},
            pwd=#{pwd}
        where id = #{id};
    </update>
</mapper>

UserMapperImpl:

package com.kuang.dao;

import com.kuang.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserMapperImpl implements UserMapper{

    private SqlSessionTemplate sqlSessionTemplate;
    private UserMapper mapper;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    public void setMapper(){
        mapper = sqlSessionTemplate.getMapper(UserMapper.class);
    }

    @Override
    public List<User> getUserList() {
        setMapper();
        return mapper.getUserList();
    }

    @Override
    public User getUserById(int id) {
        setMapper();
        return mapper.getUserById(id);
    }

    @Override
    public int addUser(User user) {
        setMapper();
        mapper.addUser(user);
        return 0;
    }

    @Override
    public int deleteUser(int id) {
        setMapper();
        mapper.deleteUser(id);
        return 0;
    }

    @Override
    public int updateUser(User user) {
        setMapper();
        mapper.updateUser(user);
        return 0;
    }
}

test:

import com.kuang.dao.UserMapperImpl;
import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class test {
    public static void main(String[] args) {
        //不再获取sqlSession来进行执行,spring进行内部集成了,只需要从spring中取出注册的实现类来完成操作即可
        //通过spring控制mybatis
        ApplicationContext applicationContext = new                          ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapperImpl userMapperImpl = (UserMapperImpl)      applicationContext.getBean("UserMapperImpl");
        List<User> list=userMapperImpl.getUserList();
        for (User user : list) {
            System.out.println(user);
        }
        System.out.println("ok");
    }
}

方式二=方式一的简化操作

简化了sqlSession的获取途径

实现类继承SqlSessionDaoSupport

1.配置中
<!--另一种方式,简化sqlSession获取-->
    <bean id="UserMapperImplSupport" class="com.kuang.dao.UserMapperImplSupport">
        <!--注入工厂-->
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
2.实现类
package com.kuang.dao;

import com.kuang.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImplSupport extends SqlSessionDaoSupport implements UserMapper{

    @Override
    public List<User> getUserList() {
        return getSqlSession().getMapper(UserMapper.class).getUserList();
    }

    @Override
    public User getUserById(int id) {
        return getSqlSession().getMapper(UserMapper.class).getUserById(id);
    }

    @Override
    public int addUser(User user) {
        getSqlSession().getMapper(UserMapper.class).addUser(user);
        return 0;
    }

    @Override
    public int deleteUser(int id) {
        getSqlSession().getMapper(UserMapper.class).deleteUser(id);
        return 0;
    }

    @Override
    public int updateUser(User user) {
        getSqlSession().getMapper(UserMapper.class).updateUser(user);
        return 0;
    }
}

test:

import com.kuang.dao.UserMapperImpl;
import com.kuang.dao.UserMapperImplSupport;
import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class test {
    public static void main(String[] args) {
        //不再获取sqlSession来进行执行,spring进行内部集成了,只需要从spring中取出注册的实现类来完成操作即可
        //通过spring控制mybatis
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//        UserMapperImpl userMapperImpl = (UserMapperImpl) applicationContext.getBean("UserMapperImpl");
        UserMapperImplSupport userMapperImplSupport = (UserMapperImplSupport) applicationContext.getBean("UserMapperImplSupport");
        List<User> list=userMapperImplSupport.getUserList();
        for (User user : list) {
            System.out.println(user);
        }
        System.out.println("ok");
    }
}

十三、事务实现

配置spring-mybatis.xml文件:在原有的基础上添加:---通过aop完成,不影响原来的操作

<!--
xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
-->

<!--    配置声明式事务,创建transactionManager对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <constructor-arg ref="dataSource" />
    </bean>

<!--    结合AOP实现事务的织入-->
<!--配置事务通知:-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--     给哪些方法配置事务-->
<!--    配置事务的传播特性:propagation:REQUIRED(默认值) -->
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete"/>
        <tx:method name="update"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*"/>
     </tx:attributes>
</tx:advice>

    <!--    配置事务切入点-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.kuang.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"></aop:advisor>
    </aop:config>

实现目的:当一组操作(增加,修改和删除一起)为事务,为了不出现使事务中途失败执行到一半的情况。

如:增加成功,修改失败--对于这一个事务,如果不撤回增加的数据,会使这个事务完成到一半,只进行了增加。

注意

springAOP和springMVC面试必问,IOC不一定

posted @ 2021-07-18 12:33  世界时停  阅读(93)  评论(0)    收藏  举报