Spring总结

Spring使用了哪些设计原则

OCP开闭原则,DIP依赖倒置原则,控制反转IoC(新的设计模式)
IOC的实现方法:依赖注入,依赖注入有两种方式,set方法注入,构造方法注入

Spring的八大模块

image.png
Spring Core:通过IoC实现对Bean的创建和管理,主要组件是BeanFactory接口。ApplicationContext继承了BeanFactory接口,提供了AOP等功能,它的具体实现有ClassPathXmlApplicationContext,AnnotationConfigApplicationContext等等。
Spring AOP模块:实现面向切面编程,一方面可以分离非业务代码,另一方面可以进行横向的功能扩展。如自定义拦截器,日志,事务等等。
Spinrg ORM:用于集成第三方的ORM框架,如MyBatis(实现对象和数据库的转换,简化数据库操作,将sql语句与java代码分离)
Spring Web MVC:为Web应用提供了一个表现层框架。

Spring入门

Spring如何创建对象:通过反射获取类,使用根据类创建对象,然后将对象放入容器(Map)中。

Class clazz = Class.forName("com.powernode.spring6.bean.User");
Object obj = clazz.newInstance();

Spring的日志框架:log4j(log4j2)

SpringIoC的实现方式:依赖注入

依赖注入的两种方法:set方法和构造方法
Set方法:

public class UserService {
    private UserDao userDao;

    // 使用set方式注入,必须提供set方法。
    // 反射机制要调用这个方法给属性赋值的。
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    public void save(){
        userDao.insert();
    }
}
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
  <property name="userDao"> //调用属性的SetUserDao方法注入
    <ref bean="userDaoBean"/>//bean的名字
  </property>
</bean>

构造方法:

public class OrderService {
    private OrderDao orderDao;
    // 通过反射机制调用构造方法给属性赋值
    public OrderService(OrderDao orderDao) {
        this.orderDao = orderDao;
    }
    public void delete(){
        orderDao.deleteById();
    }
}
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
  <!--index="0"表示构造方法的第一个参数,将orderDaoBean对象传递给构造方法的第一个参数。-->
  <constructor-arg index="0" ref="orderDaoBean"/>
</bean>

注入简单类型value:基本类型及包装,String,Date,Number等

<bean id="userBean" class="com.powernode.spring6.beans.User">
        <!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref-->
        <!--<property name="age" value="20"/>-->
        <property name="age">
            <value>20</value>
        </property>
    </bean>

注入级联属性:给属性的属性赋值

注入数组:array

注入List集合:list

注入Set集合:set

注入Map集合:map entry

使用P命名空间可以简化Set注入配置
使用C命名空间可以简化构造方法注入配置
使用Util命名空间可以复用配置

Bean的作用域 scope

单例bean:(默认)在Spring容器初始化时创建
多例bean:调用getBean()方法时创建
request:一个请求对应一个bean
session:一个会话对应一个bean

GoF之工厂模式

1.工厂方法模式:每个产品对应要给factory
2.抽象工厂模式:每个产品系列对应一个factory

bean的实例化方式

1.使用默认构造方法
2.使用工厂方法
3.通过FactoryBean接口构造

BeanFacotry和FactoryBean的区别:
beanFactory是创建bean的抽象工厂,factorybean是一个工厂bean,可以复制Spring实例化其他对象。

bean的生命周期

只有单例bean才会被Spring管理完整的生命周期,多例bean Spring只复制创建。
1.实例化bean
2.属性赋值
初始化前before 如果配置了后处理器
3.初始化bean
初始化后after
4.使用bean
5.销毁bean context.close()

如何使用后处理器:
实现BeanPostPorcessor接口,然后配置为bean

Spring Bean的循环依赖问题

A依赖B对象,而B又依赖A对象
Spring通过三级缓存解决循环依赖问题。
原理是将bean的实例化和属性赋值分开
一级缓存:存放完整单例对象
二级缓存:存放早期单例对象
三级缓存:存放单例对象的工厂对象,工厂对象可以创建代理对象,解决AOP的循环依赖
简单来说

Spring使用三级缓存解决循环依赖 - 掘金 (juejin.cn)

源码级详解Spring的三级缓存,循环依赖的处理流程 - 掘金 (juejin.cn)

创建bean时:

1.先将beanA实例化,然后将beanA的工厂对象放入三级缓存

2.对beanA进行属性赋值,发现依赖beanB,然后执行创建B的过程

4.beanB进行属性赋值,发现依赖A,先从一级缓存中查找,没有,再从二级缓存中查找,没有,再从三级缓存中查找,然后通过beanA的工厂对象创建beanA,将beanA放入二级缓存,从三级缓存中移除。

5.把不完整的beanA注入beanB中并进行初始化,然后将B放入一级缓存。

6.此时A从一级缓存中获取B对象,然后进行属性赋值和初始化

7.最后将beanA放入一级缓存。

Spring IoC注解式开发

创建Bean的注解:

@Component ,
@Controller @Service @Repository都是@Component的别名
表现层用@Controller,业务层用@Service,持久层用@Repository

依赖注入的注解:

@Value 简单类型,@Autowired 默认按照类型注入,可以和@Qualifier结合实现按名称注入,@Resource(属于jdk扩展包的注解,Autowired属于Spring的注解)默认按照名称注入,找不到时再根据类型注入。

配置文件的注解:
使用配置类代替spring配置文件

image.png

@Configuration
@ComponentScan({"com.powernode.spring6.dao", "com.powernode.spring6.service"})
public class Spring6Configuration {
}

创建容器时使用AnnotationConfigApplicationContext,而不是ClassPathXmlApplicationContext。

@Test
public void testNoXml(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Configuration.class);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
}

JdbcTemplate

JdbcTemplate是Spring提供的一个JDBC模板,用于操作数据库,是对JDBC的封装,简化jdbc代码,一般集成其他ORM框架框架进行数据库操作,如MyBatis,Hibernate等。

代理模式

使用代理对象代替对原对象的访问,一方面可以隐藏目标对象,另一方面可以在不修改目标对象的情况下增加额外的功能。
Java中的代理:
静态代理:编译时生成,原对象和目标对象实现相同的接口
动态代理:运行时生成
JDK动态代理:使用jdk的proxy类创建代理对象
cglib动态代理:使用cglib的Enhancer类创建代理对象
jdk代理的对象必须实现接口,它是针对接口的代理,cglib所代理的对象不需要实现接口,它是通过继承实现的,所以一般有接口的使用jdk代理,没有接口的使用cglib代理
静态代理和动态代理的区别:静态代理每个接口都需要创建一个代理类,导致类爆炸,并且灵活性很低。动态代理只需要一个代理类,并且灵活性更高。

面向切面编程AOP

面向切面编程可以分离业务逻辑无关的代码作为单独的模块,例如日志,事务等。一方面可以减少重复代码,提供可维护性,另一方面可以使开发者只关注业务逻辑。AOP的原理是动态代理实现的,代理接口使用jdk,代理类使用cjlib。
AOP的核心概念是切面,切面由切入点和通知组成,切入点是我们要增强的方法,通知是我们要植入的代码,包括环绕通知,前置通知,后置通知等等。

使用AOP的方法:
使用AOP有两种方式,第一种方式是基于注解,第二种基于xml。一般使用注解。
1.引入依赖
2.添加配置spring.xml或配置类

@Configuration
@ComponentScan("com.powernode.spring6.service")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class Spring6Configuration {
}

3.定义目标类和目标方法

// 目标类
@Component
public class OrderService {
    // 目标方法
    public void generate(){
        System.out.println("订单已生成!");
    }
}

4.定义切面:切入点+通知

// 切面类
@Aspect
@Component
public class MyAspect {
    // 切点表达式
    @Before("execution(* com.powernode.spring6.service.OrderService.*(..))")
    // 这就是植入的代码(通知) @Before代表前置通知
    public void advice(){
        System.out.println("我是一个通知");
    }
}

AOP的使用示例:事务处理,安全日志

@Aspect
@Component
// 事务切面类
public class TransactionAspect {
    
    @Around("execution(* com.powernode.spring6.biz..*(..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("开启事务");
            // 执行目标
            proceedingJoinPoint.proceed();
            System.out.println("提交事务");
        } catch (Throwable e) {
            System.out.println("回滚事务");
        }
    }
}
@Component
@Aspect
public class SecurityAspect {
    @Pointcut("execution(* com.powernode.spring6.biz..save*(..))")
    public void savePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..delete*(..))")
    public void deletePointcut(){}

    @Pointcut("execution(* com.powernode.spring6.biz..modify*(..))")
    public void modifyPointcut(){}

    @Before("savePointcut() || deletePointcut() || modifyPointcut()")
    public void beforeAdivce(JoinPoint joinpoint){
        System.out.println("XXX操作员正在操作"+joinpoint.getSignature().getName()+"方法");
    }
}

Spring对事务的支持

什么是事务:一组操作要么全部成功,要么全部失败,保证数据的安全。
事务的四大特性:ACID,原子性,一致性,隔离性,持久性

Spring中对事务的支持:注解方式
配置事务

@Configuration   //配置类  代替spring.xml
@ComponentScan("com.powernode.bank") //组件扫描 代替 <bean>
@EnableTransactionManagement   //告诉spring开启事务 代替 <tx:annotation-driven transaction-manager="transactionManager"/>
public class Spring6Config {
    @Bean//定义数据源
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring6");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }
    @Bean(name = "jdbcTemplate") //定义jdbctemplate操作数据源
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){//spring自动注入参数
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    @Bean//定义事务管理器管理数据源
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

使用事务:给类或方法添加@Transactinal注解

@Service("accountService")
@Transactional
public class AccountServiceImpl implements AccountService {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    public void transfer(String fromActno, String toActno, double money) {
        // 查询账户余额是否充足
        Account fromAct = accountDao.selectByActno(fromActno);
        if (fromAct.getBalance() < money) {
            throw new RuntimeException("账户余额不足");
        }
        // 余额充足,开始转账
        Account toAct = accountDao.selectByActno(toActno);
        fromAct.setBalance(fromAct.getBalance() - money);
        toAct.setBalance(toAct.getBalance() + money);
        int count = accountDao.update(fromAct);

        // 模拟异常
        String s = null;
        s.toString();

        count += accountDao.update(toAct);
        if (count != 2) {
            throw new RuntimeException("转账失败,请联系银行");
        }
    }
}

测试:

@Test
public void testNoXml(){
    //根据配置类创建容器
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Spring6Config.class);
    //从容器中获取bean
    AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
    try {
        accountService.transfer("act-001", "act-002", 10000);
        System.out.println("转账成功");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

事务的属性:
事务的传播行为:
REQUIRE(合并事务)
REQUIRE_NEW(新建事务)
事务的隔离级别:
读未提交
读提交 - 脏读 读取的数据还未提交
可重复读 - 不可重复度 同一事务两次读取的数据不一样
串行化 -幻读 读取的数据和实际表中的数据不一样
事务的超时:
@Transactional(timeout = 10)设置事务的超时时间为10秒。
事务超时是指在一定时间内事务中的所有DML语句还没有执行完,发生回滚。
只读事务:
@Transactional(readOnly = true)
该事务只允许执行select语句,使用spring优化策略,提高效率
设置哪些异常回滚,哪些异常不回滚:
@Transactional(rollbackFor = RuntimeException.class)
@Transactional(noRollbackFor = NullPointerException.class)

Spring整合JUnit

@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:spring.xml")
public class SpringJUnit5Test {
    @Autowired
    private User user;
    @Test
    public void testUser(){
        System.out.println(user.getName());
    }
}

Spring继承MyBatis

1.引入依赖
2.配置spring:配置数据源,mybatis

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       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">
  
    <!--组件扫描-->
    <context:component-scan base-package="com.powernode.bank"/>
  
    <!--外部属性配置文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!--SqlSessionFactoryBean-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--mybatis核心配置文件路径-->
        <property name="configLocation" value="mybatis-config.xml"/>
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--起别名-->
        <property name="typeAliasesPackage" value="com.powernode.bank.pojo"/>
    </bean>

    <!--Mapper扫描器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.powernode.bank.mapper"/>
    </bean>

    <!--事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="txManager"/>

</beans>

3.编写mapper接口和对象的配置文件

Spring中的八大模式

1.简单工厂模式:IOC,一个工厂根据名称返回对象,不可扩展
2.工厂方法模式:每种对象使用一个工厂创建,可扩展
3.单例模式:双重判断加锁
4.代理模式:AOP
5.装饰器模式:IO流,动态的为已有的对象添加新的功能。
6.观察者模式:一对多的依赖关系,当主题对象状态发生变化时,同时所有观察者对象更新状态。
7.策略模式:将算法的功能和算法的实现分开,实现算法的动态扩展或切换
8.模板方法模式:抽象类中定义方法的执行的模板,子类中重写方法的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值