SSM框架专题-Spring从零入门笔记

一、什么是spring

它是一个容器,它是整合其他框架的框架,它的核心是IOC和AOP,他在很多领域都是优秀的

1.代码解耦合

spring的主要作用是为代码解耦合,降低代码间的耦合度。
任何一个地方发生异常,可以针对这个地方修改,不会牵一发动全身
使用ioc降低业务对象的耦合度

2.spring特点

  1. 轻量级,由20多个模块构成,每个jar包都很小,小于1M,核心包也就3M左右。对代码无污染
  2. 面向接口编程,使用接口就是面向灵活,项目的可扩展性,可维护性都极高,接口不关心实现类的类型,使用时使用实现类即可切换整个功能
  3. AOP:面向切面编程,就是将公共的、通用的、重复的代码单独开发,在需要的时候反之回去,底层的原理是动态代理
  4. 整合其他框架,他整合后使其他框架更易用

二、IOC控制反转

是一个概念,是一种思想
由spring容器进行对象的创建和依赖注入,程序员在使用时直接取出使用
正转:由程序员进行对象的创建和依赖注入称为正转,程序员说了算

Student stu = new Student();   ====>程序员创建对象
stu.setName("张三");				===>程序员进行赋值
stu.setAge(22);

反转:将控制权交给spring容器,由spring容器创建对象和依赖注入称为反转

<bean id="stu" class="com.powernode.pojo.Student">
	<property name="name" value="张三"> ===>spring容器负责对象创建
	<property name="age" value="22"> ===>spring容器依赖注入值
</bean>
    @Test
    public void test(){
    
    
        //正转,程序员自己操作:
        Student stu = new Student();
        System.out.println(stu);
    }
    @Test
    public void testIOC(){
    
    
        //反转,spring帮你操作:
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student stu = (Student) ac.getBean("stu");
        System.out.println(stu);
    }

2.1 简单类型注入

简单类型注入:value
引用类型注入:ref
注意:不论是简单类型注入还是引用类型注入,都需要在pojo类中写对应的setXXX方法

    <bean id="school" class="com.powernode.spring.pojo.School">
        <property name="name" value="第一职业中学"/>
        <property name="address" value="北京市市中心"/>
    </bean>

    <bean id="stu" class="com.powernode.spring.pojo.Student">
        <!--简单类型注入-->
        <property name="name" value="哈比"/>
        <!--简单类型注入-->
        <property name="age" value="6"/>
        <!--这里用到引用类型注入 ref="填上面school bean的id"-->
        <property name="school" ref="school"/>
    </bean>

2.2 简单mvc结构

接下来通过将一个例子从普通三层架构升级成spring架构,从而更好的理解spring
新建mvc架构的简单例子:
在这里插入图片描述

  1. 新建实体化类,pojo包下User类
public class User {
    
    
    private String name;
    private Integer age;

    public User() {
    
    
    }

    public User(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

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

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 新建持久化层,UserMapper以及实现类
    在这里插入图片描述
public interface UserMapper {
    
    
    /**
     * 插入一条用户数据
     * @param user
     * @return
     */
    int insert(User user);
}
public class UserMapperImpl implements UserMapper {
    
    
    @Override
    public int insert(User user) {
    
    
        System.out.println("用户"+user+"数据插入成功!");
        return 1;
    }
}
  1. 新建业务层UserService以及其实现类
    在这里插入图片描述
public interface UserService {
    
    
    /**
     * 插入一条用户数据的业务类
     * @param user
     * @return
     */
    int insert(User user);
}
public class UserServiceImpl implements UserService {
    
    
    UserMapper userMapper = new UserMapperImpl();
    @Override
    public int insert(User user) {
    
    
        int count = userMapper.insert(user);
        return count;
    }
}

  1. 新建控制层,负责接收前端来的数据,并调用业务层处理,完成后返回前端数据
    在这里插入图片描述
public class UserController {
    
    

    public void insert(User user){
    
    
        UserService userService = new UserServiceImpl();
        int insert = userService.insert(user);
        System.out.println(insert);
    }
}
  1. 新建测试类,模拟前端向后端发送数据情况
    在这里插入图片描述

2.3 通过spring改造

把其他类中的new对象全部删除,然后在resources目录下新建spring配置文件,spring接管对象创建

<?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="uMapper" class="com.powernode.spring.mapper.impl.UserMapperImpl"/>
    <!--业务层-->
    <bean id="uService" class="com.powernode.spring.service.impl.UserServiceImpl">
        <property name="userMapper" ref="uMapper"/>
    </bean>
    <!--控制层-->
    <bean id="uController" class="com.powernode.spring.controller.UserController">
        <property name="userService" ref="uService"/>
    </bean>
    
</beans>

2.4 构造方法注入

constructor-arg表示构造方法参数

    public User(String name, Integer age) {
    
    
        this.name = name;
        this.age = age;
    }
    <bean id="stu" class="com.powernode.spring.pojo.User">
        <constructor-arg name="age" value="30"/>
        <constructor-arg name="name" value="哈哈"/>
    </bean>

2.5 下标注入法

    <!--构造方法:下标注入-->
    <bean id="stu2" class="com.powernode.spring.pojo.User">
        <constructor-arg index="1" value="30"/>
        <constructor-arg index="0" value="哈哈"/>
    </bean>

2.6 构造方法默认注入

    <!--构造方法:默认注入-->
    <bean id="stu3" class="com.powernode.spring.pojo.User">
        <constructor-arg value="哈哈6"/>
        <constructor-arg value="6"/>
    </bean>

2.7 基于注解的IOC

@Component:可以创建任意对象,创建的对象默认名称是类名的驼峰命名法,也可以指定对象的名称@Component(“指定名称”)
@Value:可以给属性赋值
@Autowired:在所有bean中查找同源类
:在有父子类的情况下,如果使用按类型注入就意味着有多个可注入的对象,此时选与该属性名相同的对象进行注入
@Qualifier(“school”):在同源类中查找对象名为school的对象

@Repository:专门用来创建数据访问层的对象
@Service:专门用来创建业务逻辑层的对象
@Controller:专门用来创建控制器的对象

2.8 添加包扫描的方式

  1. 单个包扫描(推荐使用)
    在这里插入图片描述

  2. 多个包扫描
    在这里插入图片描述

  3. 扫描根包(不推荐)
    在这里插入图片描述

2.9 spring配置文件的拆分

  1. 按层拆(控制层、业务层、持久层)
    applicationContext_controller.xml
    applicationContext_servicexml
    applicationContext_mapper.xml
  2. 按功能拆
    applicationContext_users.xml
    applicationContext_book.xml
    在这里插入图片描述

2.10 spring的配置文件导入

total.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="applicationContext_controller.xml"></import>-->
    <!--<import resource="applicationContext_mapper.xml"></import>-->
    <!--<import resource="applicationContext_service.xml"></import>-->
    <!--<import resource="applicationContext_other.xml"></import>-->

    <!--批量导入-->
    <import resource="applicationContext_*.xml"></import>
</beans>

三、AOP面向切面编程

1.概念

AOP(Aspect Orient Programming),面向切面编程
切面:公共的、通用的、重复的功能被称为切面,面向切面编程就是将切面提取出来,单独开发,在需要调用的方法中通过动态代理的方式进行置入

2.手写AOP框架

业务:图书购买业务
切面:事务
1:第一个版本:业务和切面紧耦合在一起,没有拆分
2:第二个版本:使用子类代理的方式拆分业务和切面
3:第三个版本:使用静态代理拆分业务和切面,业务和业务接口已拆分,此时切面紧耦合在业务中
4:第四个版本:使用静态代理拆分业务和业务接口,切面和切面接口
5:第五个版本:使用动态代理完成第四个版本的优化

第五个版本:(改造Agent)

public class ProxyFactory {
    
    
    public static Object getProxy(Service service,Aop aop){
    
    
        return Proxy.newProxyInstance(
                service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                new InvocationHandler() {
    
    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                        Object invoke = null;
                        try {
    
    
                            //切面
                            aop.before();
                            //业务
                            invoke = method.invoke(service, args);
                            //切面
                            aop.after();
                        } catch (Exception e) {
    
    
                            aop.exception();
                        }
                        return invoke;
                    }
                });
    }
}

在这里插入图片描述

3. Spring支持的AOP的实现

在这里插入图片描述

4. AOP常用的术语

在这里插入图片描述

5. 什么是AspectJ框架

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

6. AspectJ常见通知类型

AspectJ常用四种类型:

  1. 前置通知@Before
  2. 后置通知@AfterReturning
  3. 环绕通知@Around
  4. 最终通知@After
  5. 定义切入点@Pointcut(了解)

7. AspectJ的切入点表达式(掌握)

规范的公式:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

用到的符号:

* 代码任意个字符(通配符)
.. 如果出现在方法的参数中,则代表任意参数
   如果出现在路径中,则代表本路径及其所有子路径

示例:
execution(public * (…)):公共访问权限的任意方法
execution(
set*(…)):任何一个以"set"开始的方法
execution(* com.xyz.service.impl..(…)):任意的返回值类型,在com.xyz.service.impl包下的任意类的任意方法
execution(* com.xyz.service….(…)):任意的返回值类型,在com.xyz.service及其子包下的任意方法的任意参数

8. AspectJ的前置通知@Before

在目标方法执行前切入切面功能,在切面方法中不可以获得目标方法的返回值,只能得到目标方法的签名
在这里插入图片描述
applicationContext.xml中,3个步骤

    <!--目标方法-->
    <bean name="serviceSome" class="com.powernode.spring1.ServiceSome"></bean>
    <!--切入方法-->
    <bean name="myAspect" class="com.powernode.spring1.MyAspect"></bean>
    <!--绑定-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

public class ServiceSome implements Service{
    
    
    @Override
    public String doSome(String name,Integer age) {
    
    
        System.out.println("目标方法实现。。");
        return name+"你给我看看你都"+age+"岁了";
    }
}
/**
 * @author huochai
 * @date 2022/10/21 20:52
 * 切入方法
 */
@Aspect
public class MyAspect {
    
    
    /**
     * 前置切入规范:
     * 必须是公共、无返回值、方法名任意、无参
     */
    @Before(value = "execution(public String com.powernode.spring1.ServiceSome.doSome(String,Integer))")
    public void aspect(){
    
    
        System.out.println("前置通知执行。。。");
    }
}

9.改为注解方式下的@before前置通知

  1. 在目标方法类中添加注解@Component
@Component
public class ServiceSome implements Service{
    
    
    @Override
    public String doSome(String name,Integer age) {
    
    
        System.out.println("目标方法实现。。");
        return name+"你给我看看你都"+age+"岁了";
    }
}
  1. 在切入方法类中添加注解@Component
@Aspect
@Component
public class MyAspect {
    
    
    /**
     * 前置切入规范:
     * 必须是公共、无返回值、方法名任意、无参
     */
    @Before(value = "execution(public String com.powernode.spring1.ServiceSome.doSome(String,Integer))")
    public void aspect(){
    
    
        System.out.println("前置通知执行。。。");
    }
}
  1. 添加包扫描
    <!--添加包扫描-->
    <context:component-scan base-package="com.powernode.spring1"></context:component-scan>

10.JoinPoint详解

    @Before(value = "execution(public String com.powernode.spring1.ServiceSome.doSome(String,Integer))")
    public void aspect(JoinPoint jp){
    
    
        System.out.println("方法的签名:"+jp.getSignature());
        System.out.println("方法的参数:"+ Arrays.toString(jp.getArgs()));
        System.out.println("前置通知执行。。。");
    }

11.后置通知@AfterReturning

后置通知带参数在这里插入图片描述

    /**
     * 后置通知的规范:
     * public void *(Object)
     * 带有的参数是目标方法的返回值
     * @param obj
     */
    @AfterReturning(
            //切入点位置
            value = "execution(* com.powernode.spring1.ServiceSome.*(..))",
            //返回值类型
            returning = "obj"
    )
    public void afterReturning(Object obj){
    
    
        if (obj instanceof String){
    
    
            System.out.println(obj.toString()+"被我捕获到了");
        }
        if (obj instanceof Student){
    
    
            ((Student)obj).setName("改了个哈");
        }
    }

12.环绕通知@Around

功能最强大的通知,可以吧目标方法拦住,随便修改值
在这里插入图片描述

@Service
public class SomeServiceImpl implements SomeService{
    
    
    @Override
    public String doSome(String name, Integer age) {
    
    
        System.out.println("目标方法实现.");
        return "你好,"+age+"岁的"+name;
    }
}
@Aspect
@Component
public class MyAspect {
    
    
    @Around(value = "execution(public String com.powernode.spring3.SomeServiceImpl.doSome(String,Integer))")
    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
    
    
        //切面前置方法
        System.out.println("切面前置方法执行");
        //目标方法
        Object obj = pjp.proceed(pjp.getArgs());
        //切面后置方法
        System.out.println("切面后置方法执行");
        return obj.toString().replaceAll("你好","滚啊");
    }
}
public class TestAround {
    
    
    @Test
    public void test1(){
    
    
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext3.xml");
        SomeService service = (SomeService) ac.getBean("someServiceImpl");
        String str = service.doSome("小明", 50);
        System.out.println(str);
    }
}

13. 最终通知@After

不论报不报异常,都会执行切入方法

@Aspect
@Component
public class MyAspect {
    
    
    /**
     * 最终通知的规范:
     * public void aspect()
     * 若是有参数,也是JoinPoint
     */
    @After(value = "execution(public String com.powernode.spring4.SomeServiceImpl.doSome(String,Integer))")
    public void aspect(){
    
    
        System.out.println("最终通知执行");
    }
}

14.给切入点表达式起别名

    //这边的切入点表达式直接使用别名即可
    @After(value = "qiMing()")
    public void aspect(){
    
    
        System.out.println("最终通知执行");
    }

    //这个空方法负责给,切入点表达式起别名,
    @Pointcut(value = "execution(public String com.powernode.spring4.SomeServiceImpl.doSome(String,Integer))")
    public void qiMing(){
    
    }

15.spring的两种事务处理方式

  1. 注解式的事务
    使用@Transactional注解完成事务控制,此注解可添加到类上,或者方法上
  2. 声明式事务(必须掌握),在配置文件中添加一次,整个目录遵循事务的设定

16.spring中事务的五大隔离级别

  1. 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
  2. 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复读)
  3. 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在S0L标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读
  4. 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
  5. 使用数据库默认的隔离级别isolation = Isolation.DEFAULT

MySQL:mysql 默认的事务处理级别是REPEATABLE-READ,也就是可重复读

28.为什么添加事务管理器

事务管理器用来生成相应技术的连接+执行语句的对象
如果使用MyBatis框架,必须使用DataSourceTransactionManager类完成处理

<bean id="transactionManager"class="org.springEramework.jdbc.datasource.DataSourceTransactionManager">
<!--因为事务必须关联数据库处理,所以要配置数据源-->
<property name="dataSource"ref="dataSource"></property>
</bean>

29.Spring事务的传播特性

多个事务之间的合并,互斥等都可以通过设置事务的传播特性来解决
常用:

  • PROPAGATION_REQUIRED:必被包含事务(增删改必用)
  • PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
  • PROPAGAPIONSUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
  • PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
  • PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境

不常用:

PROPAGATION_MANDATORY:必须包在事务中,没有事务川抛异常

PROPAGATION NESTED:嵌套事务

在这里插入图片描述

30.声明式事务的实现

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


    <!--此配置文件与applicationContext service.xml的功能一样,只是事务配置不同-->

    <!--导入applicationContext_mapper.xml-->
    <import resource="applicationContext_mapper.xml" ></import>
    <!--添加包扫描-->
    <context:component-scan base-package="com.powernode.spring.service.impl"></context:component-scan>
    <!--添加事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务切面-->
    <tx:advice id="myadvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*select*" read-only="true"/>
            <tx:method name="*find*" read-only="true"/>
            <tx:method name="*search*" read-only="true"/>
            <tx:method name="*get*" read-only="true"/>
            <tx:method name="*insert*" propagation="REQUIRED"/>
            <tx:method name="*add*" propagation="REQUIRED"/>
            <tx:method name="*save*" propagation="REQUIRED"/>
            <tx:method name="*set*" propagation="REQUIRED"/>
            <tx:method name="*update*" propagation="REQUIRED"/>
            <tx:method name="*modify*" propagation="REQUIRED"/>
            <tx:method name="*delete*" propagation="REQUIRED"/>
            <tx:method name="*remove*" propagation="REQUIRED"/>
            <tx:method name="*drop*" propagation="REQUIRED"/>
            <tx:method name="*clear*" propagation="REQUIRED"/>
            <tx:method name="*" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>
    <!--绑定切面和切入点-->
    <aop:config>
        <aop:pointcut id="mycut" expression="execution(* com.powernode..service.impl.*.*(..))"/>
        <aop:advisor advice-ref="myadvice" pointcut-ref="mycut"></aop:advisor>
    </aop:config>
</beans>

猜你喜欢

转载自blog.csdn.net/u011005040/article/details/127429985