一、Spring初见

优点

  • Spring是开源免费的框架(容器)

  • Spring是轻量级、非入侵式的框架

  • 控制反转(IOC),面向切面编程(AOP)

  • 支持事物的处理,对框架整合的支持

配置

看看

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

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>


        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="hello" class="com.ming.pojo.Hello">
        <property name="name" value="Spring" />
        <constructor-arg index="0" value="MINGYUE" />
        <constructor-arg name="name" value="MINGYUE" />
        <constructor-arg type="java.lang.String" value="MINGYUE" />
    </bean>

    <!-- more bean definitions go here -->
</beans>
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello = (Hello) context.getBean("hello");
        Hello hello1 = context.getBean("hello", Hello.class);
        System.out.println(hello.toString());
    }
}

创建对象

1.使用无参构造创建对象,默认!
2.假设我们要使用有参构造创建对象

<bean id="hello" class="com.ming.pojo.Hello" name="otherName1,otherName2">
    <property name="name" value="Spring" />
    <constructor-arg index="0" value="MINGYUE" />
    <constructor-arg name="name" value="MINGYUE" />
    <constructor-arg type="java.lang.String" value="MINGYUE" />
</bean>

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

别名

<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user alias="userNew"/>

import

合并

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

依赖注入

构造注入

set注入

public class student {
    private String name
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String, String> card;
    private Set<String> games;
    private Properties info;
}
<bean id="moreComplexObject" class="example.ComplexObject">
    <bean class="ExampleBean">
        <property name="email">
            <null/>
        </property>
    </bean>
    <!-- results in a setAdminEmails(java.util.Properties) call -->
    <property name="adminEmails">
        <props>
            <prop key="administrator">administrator@example.org</prop>
            <prop key="support">support@example.org</prop>
            <prop key="development">development@example.org</prop>
        </props>
    </property>
    <!-- results in a setSomeList(java.util.List) call -->
    <property name="someList">
        <list>  <!--Array-->
            <value>a list element followed by a reference</value>
            <ref bean="myDataSource" />
        </list>
    </property>
    <!-- results in a setSomeMap(java.util.Map) call -->
    <property name="someMap">
        <map>
            <entry key="an entry" value="just some string"/>
            <entry key ="a ref" value-ref="myDataSource"/>
        </map>
    </property>
    <!-- results in a setSomeSet(java.util.Set) call -->
    <property name="someSet">
        <set>
            <value>just some string</value>
            <ref bean="myDataSource" />
        </set>
    </property>



</bean>

扩展方式注入

需要导入约束

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <!-- p参数 -->
    <bean name="p-namespace" class="com.example.ExampleBean"
        p:email="someone@somewhere.com"/>
        <!-- traditional declaration with optional argument names -->

    <!-- c构造 -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg name="thingTwo" ref="beanTwo"/>
        <constructor-arg name="thingThree" ref="beanThree"/>
        <constructor-arg name="email" value="something@somewhere.com"/>
    </bean>
    <!-- c-namespace declaration with argument names -->
    <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
        c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>

</beans>

作用域

默认singleton scope="singleton"

UTOOLS1595768708837.png

自动装配

装配方式

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

autowire

  • byName
    • 名称相同
  • byType
    • 类型全局唯一
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<!-- byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的名称 -->
<bean id="people" class="com.kuang.pojo.People" autowire="byName">
    <property name="name" value="明月"/>
</bean>

注解装配

使用

属性上@Autowired

使用后不用编写set方法(反射),但是这个属性需要在IOC容器中存在,且符合byType获取,如果不能唯一确定,则需要使用@Qualifier

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>
</beans>
@Autowired
@Nullable 标记这个字段可以为null
@Qualifier(value="指定id")

@Resource
@Resource(name = "cat1")
先通过name获取,再通过type获取

注解开发

注册

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 指定包下的注解生效 -->
    <context:component-scan base-package="com.ming.pojo"/>
    <context:annotation-config />
    <!-- 类上添加@Component,即可注册 -->
</beans>

Bean

类上添加@Component,即可注册

@Scope("singleton")

属性注入

属性或者set方法上添加注解

@Value("ming")

component衍生

  • dao @Repository

  • service @Service

  • controller @Controller

对比

xml:万能、维护方便

注解:只能使用自己的类,不易于维护

一般

  • xml用来管理bean

  • 注解只负责完成属性的注入

  • 注解生效,需要开启注解的支持

    <context:component-scan base-package="com.ming.pojo" />
    <context:annotation-config />

使用java的方式

我们现在要完全不使用 Spring的xm配置了,全权交给java来做

// config/MingConfig
@Configuration  // 类似xml注册bean
@ComponentScan("com.ming.pojo") // 扫描注册包下有@component注解的类
// @Import(sha.class)
public class MingConfig {
    @Bean   // 只使用这个也能注册
    public User user() {
        return new User();
    }
}

// User
@Component
public class User {
    public String name = "ming";
}

// Test
public static void main(String[] args) {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MingConfig.class);
    User user = applicationContext.getBean("user", User.class);
    System.out.println(user.name);
}

代理模式

SpringAop的底层

代理模式分类

  • 静态代理
  • 动态代理

静态代理

角色分析

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作(很多功能)
  • 客户:访问代理的人

好处

  • 真实角色的操作更纯粹,不去关注公共的业务
  • 公共交给了代理角色,实现业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点

  • 一个真实角色产生一个代理角色,代码增加,开发效率低

横切,便于扩展

动态代理

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

分类

  • 基于接口 – JDK动态代理
  • 基于类 –cglib
  • java字节码 –javassist

好处

  • 真实角色的操作更纯粹,不去关注公共的业务
  • 公共交给了代理角色,实现业务的分工
  • 公共业务发生扩展的时候,方便集中管理
  • 一个动态代理类代理的是一个接口,一般对应一类业务
  • 可以代理多个实现同一接口的类

Proxy、InvocationHandler

package com.ming.demo02;


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

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

    // 被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 生成得到代理类
    public Object getProxy() {  // 类在哪个位置   接口  InvocationHandler
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        // 动态代理的本质,使用反射实现
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg) {
        System.out.println("[INFO]"+msg);
    }
}

使用

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();
}

AOP

依赖

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

使用spring接口

第一种 给他用啥

给该切面使用方法前和方法后

applicationContext.xml

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册bean -->
    <bean id="userService" class="com.ming.service.UserServiceImpl" />
    <bean id="afterLog" class="com.ming.log.AfterLog" />
    <bean id="log" class="com.ming.log.Log" />
    <!-- 使用原生aop -->
    <!-- 配置aop -->
    <aop:config>
        <!-- 切入点 expression(要执行的位置) -->
        <aop:pointcut id="pointcut" expression="execution(* com.ming.service.UserServiceImpl.*(..))" />
        <!-- 执行环绕 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut" />
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" />
    </aop:config>
</beans>

log

package com.ming.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class Log implements MethodBeforeAdvice { 
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() +"的" +method.getName() +"方法被执行了");
    }
}
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" +method.getName() +"方法,返回结果为" +returnValue);
    }
}

Test

public class Test {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //UserService userService = (UserService) applicationContext.getBean("userService");
        UserService userService = applicationContext.getBean("userService", UserService.class);
        userService.add();

    }
}

第二种 将它给谁

定义切面,定义该切面的方法前后

<bean id="diy" class="com.ming.log.Diy" />

<aop:config>
    <aop:aspect id="diy" ref="diy">
        <aop:pointcut id="pointcut1" expression="execution(* com.ming.service.UserServiceImpl.*(..))" />
        <aop:after method="after1" pointcut-ref="pointcut1" />
        <aop:before method="before1" pointcut-ref="pointcut1" />
    </aop:aspect>
</aop:config>

第三种

<bean id="annotationPointCut" class="com.ming.log.AnnotationPointCut"/>
<aop:aspectj-autoproxy/> <!-- 注解支持 -->
package com.ming.log;

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;

import javax.xml.ws.Action;

@Aspect /// 声明
public class AnnotationPointCut {

    @After("execution(* com.ming.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("after2");
    }
    @Before("execution(* com.ming.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("before2");
    }
    @Around("execution(* com.ming.service.UserServiceImpl.*(..))")
    public void arround(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("前");
        Object proceed = jp.proceed();
        System.out.println("后");
    }
}

JDBC

初始化

引入相关 jar 包,在 spring 配置文件配置数据库连接池

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
 destroy-method="close">
     <property name="url" value="jdbc:mysql:///test" />
     <property name="username" value="root" />
     <property name="password" value="root" />
     <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>

配置 JdbcTemplate 对象,注入 DataSource

<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
 <!--注入 dataSource-->
    <property name="dataSource" ref="dataSource"></property><!--set方式注入-->
</bean>
<!-- 组件扫描 -->
<context:component-scan base-package="com.atguigu"></context:component-scan>

使用

// ---------------- update ---------------------
String sql = "update t_book set username=?,ustatus=? where user_id=?";
Object[] args = {
    book.getUsername(), 
    book.getUstatus(),
    book.getUserId()
};
int update = jdbcTemplate.update(sql, args);

// ---------------- delete ---------------------
String sql = "delete from t_book where user_id = ?";
int update = jdbcTemplate.update(sql, id);

// ---------------- select ---------------------

// 查询表记录数

String sql = "select count(*) from t_book";
// queryForObject方法中:第一个参数代表--sql语句;第二个参数代表--返回类型class  
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);



// 查询一个

String sql = "select * from t_book where user_id=?";
/*
    queryForObject方法中:
        第一个参数:sql语句
        第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面 实现类 完成数据封装
        第三个参数:sql 语句值
*/
 Book book = jdbcTemplate.queryForObject(
     sql, 
     new BeanPropertyRowMapper<Book>(Book.class), 
     id
 );

// 查询集合

String sql = "select * from t_book";
 //调用方法
 List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));

批量操作

List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3","java","a"};
Object[] o2 = {"4","c++","b"};
Object[] o3 = {"5","MySQL","c"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);

// 批量插入 
String sql = "insert into t_book values(?,?,?)";
// batchUpdate方法 第一个参数:sql语句        第二个参数:List集合,添加多条记录数据
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);

// 批量更新
String sql = "update t_book set username=?,ustatus=? where user_id=?";
int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);

二、整合mybatis

回忆mybatis

mybatis-config.xml

<?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.ming.pojo" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.ming.mapper.UserMapper"  />
        <!--<mapper resource="com/ming/mapper/UserMapper.xml"  />-->
    </mappers>
</configuration>

mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.ming.mapper.UserMapper">
    <select id="getAllUsers" resultType="com.ming.pojo.User">
        select * from mybatis.user
    </select>

</mapper>

test

public void testMybatis01() throws IOException {
    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> users = mapper.getAllUsers();
    for (User user : users) {
        System.out.println(user);
    }

}

pom导出xml

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

mybatis-spring

初见

http://mybatis.org/spring/zh/getting-started.html

步骤

spring-dao.xml

  • dataSource 准备数据源
  • 准备sqlSessionFactory,注入dataSource,绑定mybatis配置文件,注册各个mapper.xml
  • 使用sqlSessionFactory构造生成sqlSession(Template)对象
  • 使用sqlSession注入对应的接口实现类MapperImpl
  • MapperImpl中对应方法使用sqlSession生成mapper对象,返回mapper对象调用方法的结果

导入

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>2.0.5</version>
</dependency>
<?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>spring-study</artifactId>
        <groupId>com.ming</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-06-mybatis</artifactId>
    <!--导入依赖-->
    <dependencies>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--mybatis-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.5</version>
        </dependency>
        <!--spring操作数据库-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.17.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>
    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

mybatis-config.xml

<?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.ming.pojo" />
    </typeAliases>
</configuration>

spring-dao.xml

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8" />
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!-- SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 绑定mybatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 登记mapper -->
        <property name="mapperLocations" value="classpath:com/ming/mapper/*.xml"/>
    </bean>
    <!-- sqlSessionTemplate就是sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <bean id="userMapper" class="com.ming.mapper.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession" />
    </bean>
    <!-- 可以将其他的mapper抽离到applicationContext使用import导入 -->
</beans>

UserMapperImpl

package com.ming.mapper;

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

import java.util.List;

public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSession;

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

    public List<User> getAllUsers() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.getAllUsers();
    }
}

简化

实现类

public class UserMapperImpl1 extends SqlSessionDaoSupport implements UserMapper {
    public List<User> getAllUsers() {
        return getSqlSession().getMapper(UserMapper.class).getAllUsers();
    }
}

注入

只需注入sqlSessionFactory,SqlSessionDaoSupport需要,原本还需要注入sqlSessionTemplate

<bean id="userMapper1" class="com.ming.mapper.UserMapperImpl1">
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

声明式事务

要么都成功,要么都失败

ACID: 原子性、一致性、隔离性、持久性

隔离性:多个业务处理同一个资源,防止数据损坏;

持久性:事务一旦提交,结果不会被影响

<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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--about mysql-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8" />
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!-- SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 绑定mybatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- 登记mapper -->
        <property name="mapperLocations" value="classpath:com/ming/mapper/*.xml"/>
    </bean>
    <!-- sqlSessionTemplate就是sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    <!--<bean id="userMapper" class="com.ming.mapper.UserMapperImpl">-->
        <!--<property name="sqlSession" ref="sqlSession" />-->
    <!--</bean>-->

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


    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--进行事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.ming.mapper.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/><!--对哪里使用事务-->
    </aop:config> 
</beans>

三、Spring5

Webflux

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html

用于Web开发,功能与SpringMVC类似,使用响应式编程

传统Web框架,SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,在Servlet3.1后才支持,,基于Reactor API 实现

异步非阻塞:

异步和同步:针对调用者,调用者发送请求,是否等待回应再去做其他事情

阻塞和非阻塞:针对被调用者,收到请求是否马上反馈

等公交车时我能做其他事情(异步),公交车被调用,公交车广播告诉我,三分钟后进站(非阻塞),没有广播一直等(阻塞)

特点:

  • 异步非阻塞:有效资源下提高吞吐量
  • 函数式编程:spring5基于java8,使用函数式编程实现路由请求

对比

image-20211211114313326

都可以运行到tomcat、注解

springMVC采用命令式编程(一行一行),Webflux采用异步响应式编程

Webflux适用于网关,远程调用

响应式编程:excel中,和是由多个格子决定的,一个格改变,和就改变,观察者模式

java8提供了Observer和Observable

reactor两个核心类:Mono和Flux,实现了Publisher,提供丰富操作符,Flux对象实现发布者,返回N个元素,Mono返回0或者1个元素

flux和mono都是数据流的发布者,都可以发出3种数据信号:元素值、错误信号、完成信号(错误和完成都代表终止,结束数据流)

依赖

<spring-boot.version>2.2.1.RELEASE</spring-boot.version>

<dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-core</artifactId>
    <version>3.1.5.RELEASE</version>
</dependency>

API

// 声明数据流
Flux.just(1,2,3);
Mono.just(1);

Flux.fromArray(array);
Flux.fromIterable(list);
Flux.fromStream(stream);

// .subscribe(System.out:println) 只有订阅后才会触发数据流

操作符

对数据流操作

map:元素映射为新元素

flatMap:元素映射为流,元素转为流,流合并

执行流程

webFlux基于Reactor,默认容器是netty,netty是高性能NIO框架,异步非阻塞框架

Netty:多路复用,一个选择器

webflux和springMVC流程相似,核心控制器DispatchHandler,实现接口WebHandler

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

image-20211211153934376

HandlerMapping:根据路径查询方法

HandlerAdaper:适配器,真正负责请求处理

HandlerResultHandler:响应处理结果

SpringWebFlux实现函数式编程,两个接口:RouterFunction(路由功能),HandlerFuction(处理具体函数)

注解使用

与SpringMVC相似,配置相关依赖,SpringBoot自动配置相关运行容器,默认使用Netty服务器

  1. 创建SpringBoot工程,引入WebFlux依赖
  2. server.port=8081
  3. 创建包,controller、service、entity
// new HashMap();

Mono<User> getUserById(int id); // Mono.justOrEmpty(this.users.get(id));

Flux<User> getAllUser(); // Flux.fromInterable(this.users.values());

public Flux<Void> addUser(Mono<User> userMono) {
    return userMono.doOnNext(person -> {
       user.put(1, user);
    }).thenEmpty(Mono.empty());
}

SpringMVC同步阻塞,SpringMVC+Servlet+Tomcat

SpringWebFlux,异步非阻塞,SpringWebF+Reactor+Netty

函数式编程使用

1.需要自己初始化服务器,RouterFunction(路由功能,转发给对应Handler)、HandlerFunction(处理请求生成响应的函数)

2.定义函数式接口的实现并且启动需要的服务器

3.SpringWebFlux的请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse

1.pom依赖

2.实现接口

public Mono<ServerResponse> getUserById(ServerRequest request) {
    int userId = Integer.valueOf( request.pathVarible("id") );
    // 空值处理
    Mono<ServerResponse> notFound = ServerResponse.notFoun().build();

    Mono<User> userMono = this.userService.getUserById(userId);
    userMono.flatMap(user - > {
        ServerReponse.ok().contentType(MediaType.APPLICATION_JSON)
            .body(fromObject(user))
            .switchIfEmpty(notFound);
    });
}

public Mono<ServerResponse> getAllUsers(ServerRequest request) {
   Flux<User> users =  this.userService.getAllUser();
    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(user, User.class);
}

public Mono<ServerResponse> saveUser(ServerRequest request) {
    Mono<User> userMono = request.bodyToMono(User.class);
    return ServerResponse.ok().build( this.userService.saveUserInfo(userMono) );
}

初始化服务器,编写Router

public class Server{
    public RouterFunction<ServerResponse> routingFunction() {
        UserService userService = new UserServiceImpl();
        UserHandler handler = new UserHander(userService);

        return RouterFunctions.route( 
            GET("/users/{id}").and(accept(MediaType.APPLICATION_JSON)), handler::getUserById)) // org.springframework.web.reactive.function.server.RequestPredicates.GET / access
        .andRoute( GET("/users")).and(accept(MediaType.APPLICATION_JSON)), handler::getAllUsers);
    }
}

创建服务器完成适配

public void createRectorServer() {
    // 路由和handler适配
    RouterFunction<ServerResponse> route = routingFunction();
    HttpHandler httpHandler = toHttpHandler(route);
    ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);

    // 创建服务器
    HttpServer httpServer = HttperServer.create();
    httpServer.handler(adapter).bindNow();
}

使用

// Server
public static void main(String[] args) {
    Server server = new Server();
    server.createReactorServer();
    System.out.println("enter to exit")
    System.in.read(); // 退出
}

webclient

psvm() {
    // 调用服务器地址
    WebClient webclient = WebClient.create("http://127.0.0.1:5794");
    String id = "1";
    User userResult =  webClient.get()
        .uri("/users/{id}", id) //   /users
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(User.class) //   bodyToFlux(User.class)
        .block();
    sout(userResult);
   Flux<User> usersResult =  webClient.get()
        .uri("/users") //   
        .accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToFlux(User.class)
        .block();
    usersResult.map(user - > user.getName())
        .buffer().doOnNext(System.out::println).blockFirst();
}

注解

AliasFor【TODO】

注解到自定义注解的两个属性上,表示这两个互为别名,简化编码

设置互相别名的属性值必须相同,若两设置成不同,则会报错

定义

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AliasFor {
    @AliasFor("attribute")
    String value() default "";

    @AliasFor("value")
    String attribute() default "";

    /** 
     * 如果显式设置了AliasFor#annotation()属性
     * 并且当前注解中有多个属性指向元注解中的某个属性(可以是直接指向或者通过传递指向),
     * 那么这几个属性互为隐式别名 
     */
    Class<? extends Annotation> annotation() default Annotation.class;
}

使用

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Executor {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";
}

关联其他

/**
 * 自定义注解MyComponent的value和beanName互为隐式别名,
 * 因为它们都指向元注解Component的value属性
 */
@Component
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
  @AliasFor(annotation = Component.class)
  String value() default "";

  @AliasFor(attribute = "value", annotation = Component.class)
  String beanName() default "";
}