SSM框架--sping

spring部分


spring 系统架构

核心容器是spring中最核心的模块,其他模块都是依赖他运行的


核心概念


IOC:从程序直接new对象,变成了由外部提供对象。spring使用IOC容器充当外部,实现了IOC思想

  • IOC最终目的充分解耦

IOC入门案例

  • maven标准项目结构解释

    Application接口是IOC容器的一个实现,为我们使用IOC容器提供了方便
  • 1.在pom.xml中引入spring的jar包
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId><!--隶属的组织-->
        <artifactId>spring-context</artifactId><!--依赖项-->
        <version>5.2.10.RELEASE</version><!--版本-->
    </dependency>

</dependencies>
  • 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的配置文件-->
    <!--BookDaoImpl-->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"></bean>
    <!--BookServiceImpl-->
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"></bean>
</beans>
  • dao
package com.itheima.dao;

public interface BookDao {
    public void  save();


}

package com.itheima.dao.impl;

import com.itheima.dao.BookDao;

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}

  • 测试
public class Test1 {
    public static void main(String[] args) {
        //1.获取IOC容器(加载配置文件)
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.获取Bean对象
       BookDao bookDao= (BookDaoImpl)applicationContext.getBean("bookDao");
       bookDao.save();

    }
}

在加载配置文件的时候bean就已经被创建

DI(注入属性)入门案例




bean基础配置


bean的别名配置
id属性认为是bean的标识符(不写默认是类名首字母小写)(编号,且唯一)name属性是给bean起别名,可以有多个。不管是唯一的id还是多个别名都对应着同一个bean


bean的作用范围

  • 总结

bean的实例化---构造方法实例化

  • 默认是调用我们的无参构造方法实例化的

对于spring报错信息的阅读

  • 给构造加上参数
  • 此时没有找到需要的构造,所以报错

静态工厂实例化(注意工厂方法是静态的)

(不需要创建对象,直接调用静态方法即可)
使用静态工厂实例化bean,在一些早期的项目中使用的比较多

  • 工厂类
package com.itheima.dao.impl;

public class BookDaoFactory {
    public static BookDaoImpl getInstance() {
        return new BookDaoImpl();
    }
}

实例工厂创建bean

因为是实例工厂,所以我们必须先创建工厂的对象。

我们创建的工厂对象,好像仅仅是是为我们创建对象服务的,能不能优化一下简化这个程序

package com.itheima.dao;

public interface UserDao {
    void serve();
}

package com.itheima.dao.impl;

import com.itheima.dao.UserDao;

public class UserDaoImpl implements UserDao {
   @Override
   public void serve() {
       System.out.println("serve ...");
   }
}

  • factoryBean类
package com.itheima.dao.impl;

import com.itheima.dao.UserDao;
import org.springframework.beans.factory.FactoryBean;

public class UserDaoFactoryBean implements FactoryBean<UserDao>  {

    //创建对象
    //替代工厂中创建对象的方法
    @Override
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}



bean的生命周期





setter注入

分为注入引用类型和注入普通类型

构造器注入

  • 写法1


  • 重点

setter和构造器相比,setter注入更自由,可以不注入全部的属性

自动装配

按名称装配:属性名和bean的id名相同及匹配
以后一般使用按类型匹配

1.建议使用按名称装配
2.自动装配在底层使用的还是setter方法
3.手动装配和自动装配不用同时使用

集合注入


1,array和list标签可以混用


在以后的开发中集合的注入使用的很少

配置管理第三方bean

https://mvnrepository.com/这个网站可以查找maven资源对应的资源坐标

对于一个陌生的类的管理,需要我们去探索该类的组成结构。setter方法,构造方法的情况

  • druid和c3po的管理
<!--德鲁伊连接池的创建-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="username" value="swt"></property>
        <property name="password" value="123456"></property>
        <property name="url" value="jdbc:mysql://database"></property>
    </bean>
    <!--配置c3p0数据库连接池-->
    <bean id="a" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="swt"></property>
        <property name="password" value="123456"></property>
        <property name="jdbcUrl" value="jdbc:mysql://database"></property>
    </bean>

加载Propertirs配置文件

一个现象:我们在加载过程中除了会加载properties文件中我们自己写的属性外,也会加载一些系统属性。而且这些系统属性的属性名可能和我们的属性名一样(如:就存在系统属性名username)。而且会优先使用系统属性



容器




beanFactory是容器的最顶层接口,也可以完成响应操作,但是有局限性

  • 使用beanFactory


核心容器的总结

注解开发定义bean

纯注解开发



package com.itheima.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration//表明是配置类
@ComponentScan(basePackages = "com.itheima")//扫描组件
public class SpringConfig {
}

使用注解管理bean的作用范围和bean的生命周期

uploading-image-48888.png

注解开发的依赖注入

注解注入引用类型

注意:注解注入普通类型


-1.加载配置文件
-2.使用注解给普通属性注入配置文件中的值

  • PropertySource注解的值也可以以数组的形式写多个,但是不能使用*

注解开发管理第三方的bean

  • 以管理德鲁伊数据库连接池为例子

    我们springconfig是spring的配置文件,而使用注解管理连接池应该写在jdbc专门的配置类中
  • jdbc配置类
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;


public class JdbcConfig {
    @Bean//将返回的bean交给spring管理
    public DataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setPassword("123456");
        druidDataSource.setUsername("sss");
        druidDataSource.setUrl("jdbc");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return druidDataSource;
    }
}

  • spring配置类(在spring配置类中导入jdbc配置类)
package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.*;
@Configuration//表明是配置类
@ComponentScan(basePackages = "com.itheima")//扫描组件
@Import({JdbcConfig.class})//导入jdbc配置类(这种方式可以清楚的知道导入的是哪个配置类)
public class SpringConfig {

}

为第三方bean注入资源

package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.dao.BookDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

## 注解开发总结
![](https://img2023.cnblogs.com/blog/2942946/202312/2942946-20231204212308271-1614110233.png)
- 标红为使用频率高的
![](https://img2023.cnblogs.com/blog/2942946/202312/2942946-20231204212730060-1879977177.png)
 


public class JdbcConfig {
    /*第三方bean普通类型的注入*/
    @Value("sss")
    private  String userName;
    @Value("123456")
    private  String password;
    @Value("jdbc")
    private String url;
    @Value("com.mysql.jdbc.Driver")
    private String driverClassName;
    @Bean//将返回的bean交给spring管理
    public DataSource getDruidDataSource(BookDao bookDao) {//假如第三方bean需要注入UserDao对象
        System.out.println(bookDao);//成功注入了BookDao对象
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setPassword(userName);
        druidDataSource.setUsername(password);
        druidDataSource.setUrl(url);
        druidDataSource.setDriverClassName(driverClassName);
        return druidDataSource;
    }
}

spring整合mybatis暂时省略

AOP



切入点理解为一个式子,这个式子可以匹配多个方法

AOP的入门案例



对应AOP的理解
AOP即面向切面编程,主要思想是代理模式的应用。虽然AOP使用的是动态代理,但是动态代理在本质上原理和和普通的代理相同,所以1.在理解AOP时使用简单代理思考即可
2.其中使用注解来实现AOP比较抽象,可以这样理解。切面表示的是通知方法和增强方法间的映射关系,他们之间是绑定的,这不就是在普通的代理模式中,在代理类中的需求方法中调用增强方法吗
3.我们的切入点方法指定为dao中的方法和dao实现类中的方法都可以,因为无论是指定接口中的方法还是实现类中的方法,后面都会调用实现类中的方法作为切入点方法,和增强方法一起完成方法的增强操作

AOP的工作流程

切入点表达式


这样写的话过于繁琐了

  • 书写技巧

AOP通知类型


对于环绕通知的一个注意点

  • dao
public interface BookDao {
    public int save();
}

public int save() {
        System.out.println("book dao save ...");
        return 100;
    }
  • 环绕通知
//环绕通知方法
    @Around("pointcut()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知前");
        //调用方式方法
       pjp.proceed();
        System.out.println("环绕通知后");
    }


这个应该很好理解,save方法本来应该返回一个int,返回被代理之后返回的是一个void,会原始方法不符合,我们应该在通知中将原始方法的返回值进行返回

案例业务层接口执行效率

/切面类
@Component//将增强类交给spring管理(制定了切入点方法和通知的对应关系)
@Aspect
public class MyAdvice {
    //定义切入点
    @Pointcut("execution(* com.itheima..save(..))")
    public void pointcut(){}

    //环绕通知方法
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //获取切入点签名
        final Signature signature = pjp.getSignature();
        final Class declaringType = signature.getDeclaringType();//获取类型
        final String name = signature.getName();//获取切入点的方法名
        final long start = System.currentTimeMillis();
        //调用方式方法
        for (int i = 0; i < 10000; i++) {
            pjp.proceed();
        }
        final long end = System.currentTimeMillis();
        System.out.println(declaringType+name+"万次执行的时间是"+(end-start)+"毫秒");
return 100;
    }
}

AOP通知获取数据

我们现在已经处理切入点方法了,但是并不是所有的情况都需要处理,或者说不同的参数我们的处理方案不同。所有我们必须可以在通知中获取原始操作的数据

  • 注意点1

ProceedingJoinPoint接口是JoinPoint接口的子接口

  • 注意点2

  • 获取切入点运行时异常

百度网盘密码数据兼容处理


这题的关键点在于我们需要获取原始方法中的密码对他进行处理(去除密码中的空格),所以我们必须使用环绕通知

  • 切面类
 @Around("pointcut()")
    public void hh(ProceedingJoinPoint pjp) throws Throwable {
       //1.获取原始方法的参数
        final Object[] args = pjp.getArgs();
        //对参数进行去除空格处理
        for (int i = 0; i < args.length; i++) {
            if(args[i] instanceof String){
                args[i]=((String)args[i]).trim();
            }
        }
        //调用原始方法
        pjp.proceed(args);//将修改后的参数交给原始方法执行
    }
  • 测试
public class App2 {
    public static void main(String[] args) {
        //3.获取IoC容器
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        //4.获取Bean
        BookDao bookDao = ctx.getBean(BookDao.class);

       bookDao.openURL("hello      ","123     ");

    }

AOP总结


切入点表达式对接口描述会让我们的程序耦合度更低


环绕通知可以模拟出其他四种通知

spring事务简介

事务的作用:保障事务层操作的一致性




事务角色

spring事务属性


事务并不是遇到异常就回滚,也就是事务并不是所有异常都会回滚
我们的程序只有在遇到运行时异常和Error才会归滚,遇到非运行时异常不会进行回滚,如IOException.我们可以通过属性来设置回滚的异常
rollBackFor可以配置哪些异常进行回滚(一般是配置默认不回滚的异常)

我们需要在程序中加入一个业务进行日志记载

  • 日志处理类
package com.aiheima.dao;

import org.apache.ibatis.annotations.Insert;

public interface LogDao {
@Insert("insert into log(id,info,crateDate) values(1,#{info},now())")
    public void log(String info);
}

  • service
package com.aiheima.service;

import com.aiheima.dao.LogDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class LogService {
    @Autowired
    private LogDao logDao;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void log(String out,String in ,double money){
        logDao.log("转账由"+out+"转入"+in+"金额:"+money);
    }
}


但是当我们转账方法出现异常的后,这个日志方法并没有被执行,这个方法和其他的方法一起都进行了回滚。用户表和日志表都没有改变

  • 事务传播行为

    注意这个视频的事务的隔离级别没有将,这尚硅谷的视频里面有讲到事务的隔离级别

    这就涉及到了事务的传播行为,上面这种情况的发生是因为此时日志的传播行为为默认,此时这个servcie中的转账方法开启了事务,他调用的方法也开启了事务,但是他们会加入到转账方法的事务中,所以他们会一起执行事务的回滚,而此时我们应该让日志的操作的事务独立出来,不受其他方法的影响,改变这个日志方法的事务传播行为就可以了



此时用户表的金额没有改变,但是日志表中有了记录

posted @ 2023-12-26 14:55  一往而深,  阅读(27)  评论(0编辑  收藏  举报