Spring基础

Spring

  • Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的免费开源框架(容器),支持事务的处理

  • 目的:解决企业应用开发的复杂性,整合了现有的技术框架,使现有的技术更容易使用

  • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能

  • Spring框架以interface21框架为基础,经过重新设计,不断丰富其内涵,于2004年3月24日发布了1.0正式版本。

  • SSH:Struct2 + Spring + Hibernate !

  • SSM:SpringMvc + Spring + Mybatis !

官方下载地址:https://repo.spring.io/ui/native/release/org/springframework/spring/

GitHub:https://github.com/spring-projects/spring-framework

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
    
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

现代化的Java开发,都是基于Spring的开发!

Spring Boot:一个快速开发的脚手架,可以快速开发单个微服务,约定大于配置

Spring Cloud:基于SpringBoot实现

学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用

弊端:发展太久之后,配置十分繁琐,人称:配置地狱

IOC

理论推导

控制反转IoC(Inversion of Control),是一种设计思想。IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XMl配置,也可以使用注解,新版本的Spring也可以零配置实现IoC

新建maven项目,在pom.xml文件中添加依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

image-20220221141613569

1.UserDao接口

public interface UserDao {
    void getUser();

}

2.UserDaoImpl实现类

public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("默认获取用户数据");
    }
}

3.UserService业务接口

public interface UserService {
    void getUsers();

}

4.UserServiceImpl业务实现类

public class UserServiceImpl implements UserService{

    @Override
    public void getUsers() {
        userDao.getUser();
    }
}

在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码,如果程序代码量十分大,修改一次的成本代价十分昂贵

使用一个set接口实现:

private UserDao userDao;

//利用set进行动态实现值的注入
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}

使用set注入后,程序不再具有主动性,而是被动的接受对象,大大降低系统的耦合性,可以更加专注在业务的实现上,这是IOC的原型。

Hello Spring

新建一个maven项目

1.实体类:

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 + '\'' +
                '}';
    }
}

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

    <bean id="hello" class="com.wang.pojo.Hello">
        <property name="str" value="Spring"/>
    </bean>

</beans>

3.测试

import com.wang.pojo.Hello;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {

//        获取Spring的上下文对象,使用xml加载必须加这句话
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

//        现在对象都在Spring的管理中,要使用时直接在里面取出来就行了
        Hello hello = (Hello) context.getBean("hello");

        System.out.println(hello.toString());
    }
}

运行结果:image-20220221154525491

Hello对象由Spring创建,Hello对象的属性由Spring容器设置,这个过程就叫控制反转。

控制:传统应用程序的对象由程序本身控制创建,使用Spring后,对象由Spring来创建

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

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

到了现在,我们彻底不用对程序进行改动,要实现不同操作,只需要对xml配置文件进行修改,对象由Spring来创建,管理,装配。

IOC创建对象的方式

1.使用无参构造创建对象,默认!

public User(){
    System.out.println("User的无参构造");
}

<!--    无参构造赋值-->
<bean id="user" class="com.wang.pojo.User">
    <property name="name" value="张三"/>
</bean>

2.使用有参构造创建对象

public User(String name) {
    this.name = name;
}
<!--    有参构造赋值-->
<!--    1.下标赋值-->
<bean id="user" class="com.wang.pojo.User">
    <constructor-arg index="0" value="李四"/>
</bean>
    
<!--    2.通过类型创建,不建议使用-->
<bean id="user" class="com.wang.pojo.User">
    <constructor-arg type="java.lang.String" value="王五"/>
</bean>
    
<!--    3.通过参数名-->
<bean id="user" class="com.wang.pojo.User">
  	<constructor-arg name="name" value="赵六"/>
</bean>

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了

Bean配置

<!--    使用Spring来创建对象,在Spring中这些都称为Bean
        bean =   相当于 new Hello();
        原来应该为: 类型 变量名 = new 类型();

        id :对象名    bean的唯一标识符
        class : new的对象    bean对象所对应的全限定名:包名 + 类型
        name : 也是别名,而且可以同时起多个别名
        property 相当于给对象中的属性设置一个值
-->
<bean id="hello" class="com.wang.pojo.Hello" name="hello2,hello3">
    <property name="str" value="Spring"/>
</bean>

<!--    别名:用于给变量起别名,即修改bean的id-->
<alias name="user" alias="newUser"/>

import

一般用于团队开发使用,它可以将多个配置文件导入合并为一个。

<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

依赖注入

DI

DI依赖注入本质是set注入!

  • 依赖:bean对象的创建依赖于容器
  • 注入:bean对象中的所有属性,由容器来注入
    <bean id="address" class="com.wang.pojo.Address">
        <property name="address" value="重庆"/>
    </bean>

    <bean id="student" class="com.wang.pojo.Student">
<!--        1.普通值注入,value-->
        <property name="name" value="特工007"/>
<!--        2.bean注入,ref-->
        <property name="address" ref="address"/>
<!--        3.数组-->
        <property name="books">
            <array>
                <value>三国演义</value>
                <value>水浒传</value>
                <value>红楼梦</value>
                <value>西游记</value>
            </array>
        </property>
<!--        4.List-->
        <property name="hobbys">
            <list>
                <value>唱歌</value>
                <value>跳舞</value>
            </list>
        </property>
<!--        5.Map-->
        <property name="card">
            <map>
                <entry key="身份证" value="111111200000000000"/>
                <entry key="银行卡" value="6220000000000000000"/>
            </map>
        </property>
<!--        Set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>BOB</value>
                <value>COC</value>
            </set>
        </property>
<!--        null-->
        <property name="wife">
            <null/>
        </property>
<!--        Properties-->
        <property name="info">
            <props>
                <prop key="学号">2016444444</prop>
                <prop key="性别">男</prop>
                <prop key="姓名">张三</prop>
            </props>
        </property>
    </bean>
    @Test
    public void test03(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        Student student = (Student) context.getBean("student");
        System.out.println(student.toString());
/*
Student{name='特工007',
 address=Address{address='重庆'},
 books=[三国演义, 水浒传, 红楼梦, 西游记],
 hobbys=[唱歌, 跳舞],
 card={身份证=111111200000000000, 银行卡=6220000000000000000},
 games=[LOL, BOB, COC],
 info={学号=2016444444, 性别=男, 姓名=张三},
 wife='null'}
 */
    }

空间注入

p命名空间注入:

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

<!--    p命名空间注入
        可以直接注入属性的值,不需要构造方法-->
        <bean id="user" class="com.wang.pojo.User" p:name="小明" p:age="21"/>

c命名空间注入:

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

<!--    c命名空间注入,通过构造器注入 construct-args-->
    <bean id="user" class="com.wang.pojo.User" c:name="小明" c:age="23"/>

注意点:p命名和c命名空间不能直接使用,需要导入xml约束!

Bean的作用域

1.单例模式(默认)

<bean id="user" class="com.wang.pojo.User" c:name="小红" c:age="23" scope="singleton"/>

2.原型模式:每次从容器中get都会产生一个新对象

<bean id="user" class="com.wang.pojo.User" c:name="小红" c:age="23" scope="prototype"/>

3.其余的request、session、application这些只能在web开发中使用到!

Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

Spring中的三种装配方式:

  • 在XML中进行显式配置
  • 在Java中进行显式配置
  • 隐式的bean发现机制和自动装配

ByName自动装配

<bean id="cat" class="com.wang.pojo.Cat"/>
<bean id="dog" class="com.wang.pojo.Dog"/>
<!--    byName:会自动在容器上下文中查找和自己对象set方法后面的值对应的beanid      -->

<bean id="people" class="com.wang.pojo.People" autowire="byName">
    <property name="name" value="李华"/>
</bean>

ByType自动装配

<bean class="com.wang.pojo.Cat"/>
<bean class="com.wang.pojo.Dog"/>
<!--    byName:会自动在容器上下文中查找和自己对象属性类型相同的bean      -->

<bean id="people" class="com.wang.pojo.People" autowire="byType">
    <property name="name" value="李华"/>
</bean>

小结:byname需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致;bytype需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。

注解实现自动装配

1.导入约束:context约束

2.配置注解支持

<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>

@Autowired:直接在属性上使用即可,可以忽略set方法。

科普:@Nullable 字段标记了这个注解,说明这个字段可以为Null

public @interface Autowired {
    boolean required() default true;
}

//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不能为空
@Autowired(required = false)

如果自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成时,我们可以使用

@Qualifier(value=“dog222”)去配合@Autowired指定一个唯一的bean对象注入

@Resource注解:

@Resource(name = "dog222")

@Resource和@Autiwired都是用来自动装配的,都可以放在属性字段上。不同:

  • @Autowired通过byname的方式实现
  • @Resource默认通过byname的方式实现,也可以通过bytype实现,还可以指定一个唯一的bean对象注入

使用注解开发

在Spring4之后,要使用注解开发,必须保证导入了aop包

image-20220223131529887

使用注解需要导入context约束,增加注解的支持!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
">
    <!--    指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.wang.pojo"/>
    <context:annotation-config/>

</beans>

1.Bean

@Component

@Component
//等价于<bean id="user" class="com.wang.pojo.User"/>

2.属性注入

@value

public class User {

    @Value("张三")
    public String name;

}

3.衍生的注解

@Component有几个衍生注解,在web开发中会按照mvc三层架构分层

dao @Repository

service @Service

controller @Controller

这四个注解功能都是一样的,都代表将某个类注册到Spring中,装配bean

4.自动装配

@Autowired:自动装配通过byname的方式实现
​ 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
@Nullable:标注了这个注解的字段可以为null
@Resource:默认通过byname的方式实现,也可以通过bytype实现

5.作用域

@Scope

@Scope("singleton")			//单例模式

小结:xml更加万能,适用于任何场合,维护简单方便;注解,不是自己的类使用不了,维护相对复杂

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

6.使用java的方式配置Spring

完全不使用Spring的xml配置,全权交给java来做

JavaConfig是Spring的一个子项目,在Spring4之后,成为了一个核心功能。

实体类:

package com.wang.pojo;

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

@Component      //这个类被Spring接管,注册到了容器中
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 com.wang.config;

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

@Configuration
//代表这是一个配置类,相当于beans.xml
//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个Component
@ComponentScan("com.wang.pojo")     //显示扫描
@Import(MyConfig2.class)    //将两个类引成一个类
public class MyConfig {

    @Bean       //注册一个bean,相当于bean标签
    public User getUser(){
        return new User();
    }
    //方法的名字相当于id属性,返回值相当于class属性

}

测试类:

import com.wang.config.MyConfig;
import com.wang.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {

    @Test
    public void test01(){

        //如果完全使用配置类方式,就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

        User user = (User) context.getBean("user");
        System.out.println(user.getName());
    }
}

这种纯java的配置方式,在SpringBoot中随处可见!

代理模式

代理模式:SpringAOP的底层!

代理模式的分类:

  • 静态代理
  • 动态代理

静态代理

抽象角色:一般会使用接口或者抽象类来解决

真实角色:被代理的角色

代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作

客户:访问代理对象的人

例1:

租房接口:

//租房接口
public interface Rent {

    public void rent();
}

房东:

//房东
public class Host implements Rent{

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

代理:

public class Proxy implements Rent{

    private Host host;

    public Proxy() {
    }

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

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

    //代理还能带你看房
    public void seeHouse(){
        System.out.println("中介带你看房");
    }

    //代理收中介费
    public void fare(){
        System.out.println("收中介费");
    }

    //签合同
    public void contract(){
        System.out.println("签租赁合同");
    }
}

租客:

public class Client {

    public static void main(String[] args) {
        //租房
        Host host = new Host();
        //直接找房东
//        host.rent();
        //找代理
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

例2:

接口:

public interface UserService {

    public void add();
    public void delete();
    public void update();
    public void query();
}

真实角色:

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询一个用户");
    }
}

代理角色:

public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    //新增一个日志方法
    public void log(String msg){
        System.out.println("[Debug]使用了"+msg+"方法");
    }

}

客户:

public class Client {

    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
//        userService.add();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}

image-20220224134008582

代理模式的好处:可以使真实角色的操作更加纯粹,公共业务交给代理角色,实现业务分工,公共业务发生扩展时方便集中管理。

缺点:一个真实角色就会产生一个代理角色,代码量会翻倍

改变原有的代码在公司中是大忌!!!

动态代理

动态代理和静态代理角色一样,动态代理底层原理是反射。

动态代理的代理类是动态生成的,不是我们直接写好的!

分类:基于接口的动态代理、基于类的动态代理

  • 基于接口——JDK动态代理
  • 基于类——cglib
  • java字节码实现(Javassist)

需要了解两个类:Proxy(代理)、invocationhandler(调用处理程序)

Invocationhandler:调用处理程序实现的接口,每个代理实例都有一个关联的调用处理程序

Proxy:提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类

例1:

接口:

//租房接口
public interface Rent {

    public void rent();
}

真实角色:

//房东
public class Host implements Rent {

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

动态代理:

//这个类将自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent){
        this.rent = rent;
    }

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

    @Override
    //处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质,就是使用反射机制实现!
        seeHouse();
        Object invoke = method.invoke(rent, args);
        fare();
        return invoke;
    }

    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();
        
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);
        Rent proxy = (Rent) pih.getProxy();//这里的proxy就是动态生成的
        proxy.rent();
    }
}

image-20220224134308534

例2:

接口:

package com.wang.demo02;

public interface UserService {

    public void add();
    public void delete();
    public void update();
    public void query();
}

真实角色:

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询一个用户");
    }
}

动态代理(万能代码)

//这个类将自动生成代理类
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);
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        log(method.getName());
        //动态代理的本质,就是使用反射机制实现!
        Object invoke = method.invoke(target, args);
        return invoke;
    }

    public void log(String msg){
        System.out.println("[Debug]执行了"+msg+"方法");
    }
}

客户:

public class Client {

    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //处理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //设置要代理的对象
        pih.setTarget(userService);
        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();
    }
}

image-20220224134614548

动态代理的好处:能实现静态代理所有功能,一个动态代理类代理的是一个接口,对应一类业务

一个动态代理类可以代理多个类,只要是实现了同一个接口即可

AOP

横向编程思想:在不影响原来业务类的情况下,实现动态的增强

AOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

AOP在Spring中的作用:提供声明式事务;允许用户自定义切面

新建一个项目,导入依赖

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8</version>
</dependency>

方式一:使用Spring的API接口

主要SpringAPI接口实现。

接口:

public interface UserService {

    public void add();
    public void delete();
    public void update();
    public void query();

}

真实角色:

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询一个用户");
    }
}

log:

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice {

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

    }
}
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[] objects, Object target) throws Throwable {
        System.out.println("执行力"+method.getName()+"方法,返回结果为:"+returnValue);

    }
}

beans1.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
        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="userService" class="com.wang.service.UserServiceImpl"/>
    <bean id="log" class="com.wang.log.Log"/>
    <bean id="afterLog" class="com.wang.log.AfterLog"/>

<!--    配置AOP:需要导入aop的约束-->
    <aop:config>
<!--        切入点pointcut
            表达式execution(要执行的位置 *(修饰词) *(返回值) *(类名) *(方法名) *(参数))-->
        <aop:pointcut id="poincut" expression="execution(* com.wang.service.UserServiceImpl.*(..))"/>

<!--        执行环绕增加!-->
        <aop:advisor advice-ref="log" pointcut-ref="poincut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="poincut"/>
    </aop:config>
    
</beans>

测试:

public class MyTest {

    @Test
    public void test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");

        userService.add();
        userService.update();
        userService.delete();
        userService.query();
    }
}

运行结果:

image-20220224152908087

方式二:使用自定义类来实现AOP

主要是切面定义。

自定义类:

public class DiyPointCut {

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

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

beans2.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
        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 id="userService" class="com.wang.service.UserServiceImpl"/>
    <bean id="diy" class="com.wang.diy.DiyPointCut"/>

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

</beans>

测试代码不变,运行结果:

image-20220224155023509

方式三:使用注解实现AOP

自定义类:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//方式三:使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {


    @Before("execution(* com.wang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("==========方法执行前==========");
    }
}

beans3:

<?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 id="userService" class="com.wang.service.UserServiceImpl"/>
    <bean id="annotation" class="com.wang.diy.AnnotationPointCut"/>
<!--    开启注解支持      JDK(默认:proxy-target-class="false")      cglib(proxy-target-class="true")-->
    <aop:aspectj-autoproxy/>

</beans>

关于环绕:

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.wang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");

//        Signature signature = jp.getSignature();//获得签名
//        System.out.println("signature:"+signature);

        //执行方法
        Object proceed = jp.proceed();
        System.out.println("环绕后");
//        System.out.println(proceed);
    }

运行结果:

image-20220225091850726

整合mybatis

mybatis

步骤:

导入相关jar包(junit、mybatis、mysql、spring、aop、mybatis-spring)

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.8</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
<!--        Spring操作数据库还需要一个spring-jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.8</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
    </dependencies>

实体类:

import lombok.Data;

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

}

接口:

import com.wang.pojo.User;

import java.util.List;

public interface UserMapper {
    public List<User> selectUser();

}

mybatis核心配置文件:

<?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="com.wang.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT"/>
                <property name="username" value="root"/>
                <property name="password" value="201314"/>
            </dataSource>
        </environment>
    </environments>

    <!--    每一个Mapper.xml都需要在Mybatis核心配置文件中注册-->
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>

</configuration>

UserMapper.xml

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.wang.mapper.UserMapper">

    <!--    查询语句-->
    <select id="selectUser" resultType="user">
        select * from mybatis.user
    </select>

</mapper>

测试:

@Test
public void test01(){
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.selectUser();

        for (User user : list) {
            System.out.println(user);
        }
        //关闭SqlSession
        sqlSession.close();
}

运行结果:

image-20220225102010396

Mybatis-Spring

SqlSessionTemplate是Mybatis-Spring的核心,作为SqlSession的一个实现。

1.编写数据源配置

2.sqlSessionFactory

3.sqlSessionTemplate

4.给接口加实现类

5.将自己写的实现类注入到Spring中,测试使用

spring-mapper.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
">

<!--    DataSource:使用Spring来管理数据源,替换Mybatis的配置 c3p0 dbcp druid
        我们这里使用spring提供的jdbc : org.springframework.jdbc.datasource -->
    <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;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT"/>
        <property name="username" value="root"/>
        <property name="password" value="201314"/>
    </bean>

<!--    sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"/>
<!--        绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--        注册UserMapper.xml-->
        <property name="mapperLocations" value="classpath:UserMapper.xml"/>
    </bean>

<!--    sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--        只能使用构造器注入sqlSessionFactory,因为它没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    
</beans>

实现类:

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

import java.util.List;

public class UserMapperImpl implements UserMapper{

    //在原来,我们的所有操作都使用sqlSession来执行,现在使用sqlSessionTemplate
    private SqlSessionTemplate sqlSession;

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

    @Override
    public List<User> selectUser() {
//        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        return mapper.selectUser();
        return sqlSession.getMapper(UserMapper.class).selectUser();
    }
}

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
">

    <import resource="spring-mapper.xml"/>

    <bean id="userMapper" class="com.wang.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>

</beans>

测试:

@Test
public void test02(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}

运行结果:

image-20220225111610030

方式二

实现类:

import com.wang.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{
    @Override
    public List<User> selectUser() {
//        SqlSession sqlSession = getSqlSession();
//        return sqlSession.getMapper(UserMapper.class).selectUser();
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

注册:

<bean id="userMapper2" class="com.wang.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

测试:

@Test
public void test02(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
    for (User user : userMapper.selectUser()) {
        System.out.println(user);
    }
}

声明式事务

把一组业务当初一个业务来做,要么都成功,要么都失败!!事务在项目开发中十分重要,涉及到数据的一致性问题。

事务的ACID原则:

  • A 原子性:事务是最小的单位,不可再分割
  • C 一致性:事务要求,同一事务中的sql语句,必须保证同时成功或者同时失败
  • I 隔离性:事务1 和 事务2 之间是具有隔离性的(多个事务操作互相隔离)
  • D 持久性:事务一旦结束(commit,rollback),就不可以返回

Spring中的事务管理:

  • 声明式事务:AOP
  • 编程式事务:需要在代码中进行事务的管理

在上一个例子中,新增插入方法和删除方法:

UserMapper.xml:

<insert id="addUser" parameterType="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>

UserMapperImpl:

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {

        User user = new User(5, "小明", "321321");

        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);

        mapper.addUser(user);//插入5号
        mapper.deleteUser(2);//删除2号

        return mapper.selectUser();
    }

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

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

使用横切的方式插入事务:

spring-mapper.xml

<!--    配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>

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

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

测试:

@Test
public void test01(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    List<User> userList = userMapper.selectUser();
    for (User user : userList) {
        System.out.println(user);
    }
}

运行结果:

image-20220225143659344

在加如了事务后,插入操作和删除操作将保持一致,在删除操作出错时,不会进行插入操作。。

事务有7个传播特性,默认为REQUIRED。事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎。

posted @ 2022-02-25 14:46  萘汝  阅读(28)  评论(0编辑  收藏  举报
我发了疯似的祝你好!