Spring 是什么呢?首先它是一个开源的项目,而且非常活跃;它是一个基于 IOC 和 AOP 的构架多层 j2ee 系统的框架,但它不强迫你必须在每一层中必须使用 Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的 MVC,对不同的数据访问技术提供了统一的接口,采用 IOC 使得可以很容易的实现 bean 的装配,提供了简洁的 AOP 并据此实现 Transaction Management,等等……

IoC 控制翻转

控制反转是一种通过描述(注解或者 XML)并通过第三方去生产或获得特定的对象的方式。在 Spring 中实现控制反转的是 IoC 容器,其实现的方法是通过依赖注入(Dependency Injection,DI)

IoC🏁:对象由 Spring 来创建、管理、装配

IoC 创建对象的方式

  1. 使用 NoArgsConstruction(无参构造方法)创建对象 👉 默认实现
  2. 使用有参构造方法创建对象
    1. 下标赋值
<bean id="user" class="com.faraj.pojo.User">
    <constructor-arg index="0" value="Faraj"/>
</bean>
2. 类型匹配赋值 👉 只适用于形参列表中一种类型只有一个参数的情况,所以不推荐❌
<bean id="user" class="com.faraj.pojo.User">
    <constructor-arg type="java.lang.String" value="Faraj"/>
</bean>
3. 通过参数变量名赋值⭐️
<bean id="user" class="com.faraj.pojo.User">
    <constructor-arg name="name" value="Faraj"/>
</bean>

Spring 配置

别名

alias
给一个 bean 添加别名,我们在获取这个对象的时候,也可以用别名来获取
<alias name="user1234" alias="user"/>

bean 的配置

<bean id="user" class="com.faraj.pojo.User" name="u uu; uuu, use">
    <constructor-arg name="name" value="Faraj"/>
</bean>
  • id => 一个 bean 对应的唯一标识符,类似于变量名 🐳
  • class => 一个 bean 对应的类的全名 包 + 类型
  • name => 可以给这个 bean 起别名 可以起多个,用, ;分开都可以 作用大于 alias 🐠

import

import 一般适用于团队开发 🏭,可以在一个配置文件中使用 import 导入多个配置文件

依赖注入

别名

alias
给一个 bean 添加别名,我们在获取这个对象的时候,也可以用别名来获取
<alias name="user1234" alias="user"/>

bean 的配置

<bean id="user" class="com.faraj.pojo.User" name="u uu; uuu, use">
    <constructor-arg name="name" value="Faraj"/>
</bean>
  • id => 一个 bean 对应的唯一标识符,类似于变量名 🐳
  • class => 一个 bean 对应的类的全名 包 + 类型
  • name => 可以给这个 bean 起别名 可以起多个,用, ;分开都可以 作用大于 alias 🐠

import

import 一般适用于团队开发 🏭,可以在一个配置文件中使用 import 导入多个配置文件

Bean Scope 作用域

bean 的作用域 bean scope

共有 6⃣️ 种作用域

  • singleton(Spring 的默认实现机制)
<bean id="user" class="com.faraj.pojo.User" c:name="Mujey" c:age="20" scope="singleton"/>

显示的声明这个 bean 的作用域是 singleton 单例模式 🚶‍♂️

  • prototype⭕️(原型模式,每次从容器中 get 的时候都会返回一个新的对象)
<bean id="user" class="com.faraj.pojo.User" c:name="Mujey" c:age="20" scope="prototype"/>
  • request
  • session
  • application
  • websocket
    ✍️ 除了 singleton 和 prototype 其余的作用域都是在 web 中实现的

自动装配

autowire

  1. byName == 会自动在容器上下文中寻找和自己对象 set 方法相对于的 bean🆔
    <bean id="dog" class="com.faraj.pojo.Dog"/>
    <bean id="cat" class="com.faraj.pojo.Cat"/>
    <bean id="person" class="com.faraj.pojo.Person" autowire="byName">
        <property name="name" value="Faraj"/>
    </bean>
  1. byType == 查找和自己对象 set 方法类型相同的 bean 必须保证自动装配的类型全局唯一
    <bean id="dog123" class="com.faraj.pojo.Dog"/>
    <bean id="cat321" class="com.faraj.pojo.Cat"/>
    <bean id="person" class="com.faraj.pojo.Person" autowire="byType">
        <property name="name" value="Faraj"/>
    </bean>

使用注解实现自动装配

使用注解步骤 ⬇️

  1. 导入约束 ==> context 约束
  2. 配置注解的支持
<?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"
    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>

⚠️ <context:annotation-config/>

👇👇👇👇👇👇👇👇👇👇

    @Autowired
    private Cat cat;

    @Autowired
    private Dog dog;

    private String name;
<?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"
       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/>
    <bean id="dog" class="com.faraj.pojo.Dog"/>
    <bean id="cat" class="com.faraj.pojo.Cat"/>
    <bean id="person" class="com.faraj.pojo.Person"/>
</beans>

@Autowired

👉 可以直接在实例类的属性或者 set 方法上使用,让他在能够自动装配的情况下自动装配
如果使用@Autowired注解,确保自动装配可以成功的情况下,可以省略属性的 set 方法

👉 @Autowired会通过 byType 方式来寻找自动装配,如果在一个配置文件中出现两个或着多个类型相同的 bean,@Autowired则无法实现自动装配,会报NoUniqueBeanDefinitionException
此时应该使用 👇

@Qualifier

可以显示地使用这个注解来给属性显示写名字
如果使用@Autowired这个注解在实现自动装配的时候产生了问题,则可以配合@Qualifier实现显示指定

用法 🌰:

@Autowired
@Qualifier(value = "beanName")
private Blog blog;

@Resource

java 原生的注解,也可以用来自动装配,可以自动匹配名字或者类型
可以自己手动指定名字 @Resource(name = "name")

⬇️⬇️⬇️ 两种注解实现相同的效果 ⬇️⬇️⬇️

    @Resource(name = "cat2")
    private Cat cat;

    @Autowired
    @Qualifier(value = "cat2")
    private Cat cat;

使用注解开发

在 Spring4 之后使用注解开发必须要导入 AOP 的 🎒

  • @Autowired: 自动装配
  • @Qualifier: 给 Autowired 显示提供 bean🆔
  • @Nullable: 字段可以为空
  • @Resource: 自动装配
  • @Component: 组件,这个注解放在类上,说明这个类被 Spring 管理了,自动生成 bean
  • @Value: 给属性赋值
  • @Scope: 配置作用域

导入上下文依赖

<?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"
       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">
    <!-- 这条语句让com.faraj.pojo这个🎒下所有的类型都能使用注解 -->
    <context:component-scan base-package="com.faraj.pojo"/>
    <!-- 导入注解config -->
    <context:annotation-config/>

</beans>

@Component 👇👇👇

package com.faraj.pojo;

import org.springframework.stereotype.Component;

// 效果等于 <bean id="user" class="com.faraj.pojo.User"/>
@Component
public class User {
    private String name = "Faraj";

    public void printName(){
        System.out.println(this.name);
    }

}

@Value 👇👇👇

@Value("values") 相当于 <property name="name" value="values"/>配合@Component 可以将一个类交给 Spring 管理,并且给它简单的属性赋值

注解的衍生

@Component 有几个衍生的注解,对应 web 的三层架构,他们的功能都是一样的

  • dao - @Repository
  • controller - @Controller
  • service - @Service

使用注解实现作用域

@Scope("singleton | prototype")

纯 java 类作为配置

package com.faraj.config;

import com.faraj.dao.User;
import org.springframework.context.annotation.*;

@Configuration
@ComponentScan("com.faraj.dao")
public class BeanConfig {

    @Bean
    public User getUser(){
        return new User();
    }
}

如果使用 java 类作为 spring 配置,那么获取 bean 的时候就应该 ⬇️⬇️

public static void main(String[] args) {
    ApplicationContext context =
        new AnnotationConfigApplicationContext(BeanConfig.class);
    User user = context.getBean("getUser", User.class);
    System.out.println(user.getName());
}

使用 AnnotationConfig 上下文来获取 context
但是这样的 🌰 在 Spring 中并不常见,但是在 Spring boot 中就比较常见了

代理模式

Spring AOP 的底层就是代理模式 【SpringAOP 和 Spring MVC】

代理模式的分类

  • 静态代理
  • 动态代理

静态代理

角色分析

  • 抽象角色:一般会使用接口或者抽象类
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,一般会有一些附属的操作
  • 客户:访问代理对象的人

代理模式的好处:

  • 可以试真实角色更具啊纯粹,不用去关注一些公共业务
  • 公共业务交给代理角色,实现业务分工
  • 当公共业务发生更改或者拓展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色 🎭 代码量会翻倍 开发效率会变低 🐒

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是直接写的
  • 动态代理分为两大类:1⃣️ 基于接口的动态代理 2⃣️ 基于类的动态代理
    • 基于接口 — JDK 动态代理
    • 基于类 — cglib
    • java 字节码 — JAVAssist

基于 JDK 接口实现的动态代理

Invoke 类和 InvokeHandler 接口

底层基于反射实现 ⛳️

package com.faraj.demo04;

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 getInvoke(){
        return Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        target.getClass().getInterfaces(),
        this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        return method.invoke(target, args);
    }

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

AOP 面向切面

❗️ 使用 Spring AOP 织入,需要导入一个依赖

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

1⃣️ 使用 Spring API 接口

🌰: 写两个日志的增强类,一个前置日志,一个后置日志

👇👇👇👇👇 前置日志 👇👇👇👇👇

package com.faraj.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

public class BeforeLog implements MethodBeforeAdvice {
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName() + "." + method.getName() + "() 执行了");
    }
}

👇👇👇👇👇 后置日志 👇👇👇👇👇

package com.faraj.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println(method.getName() + "() 执行成功,执行结果为:" + o);
    }
}

🚧🚧🚧 配置 ApplicationContest.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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.faraj.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.faraj.log.BeforeLog"/>
    <bean id="afterLog" class="com.faraj.log.AfterLog"/>

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.faraj.service.UserServiceImpl.*(..))"/>
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

⚠️ 需要导入 aop 的约束
👉xmlns:aop="http://www.springframework.org/schema/aop"
👉http://www.springframework.org/schema/aop
👉https://www.springframework.org/schema/aop/spring-aop.xsd

测试 🔚

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

⚠️ 动态代理代理的是接口,不是实现类

2⃣️ 使用自定义切面

👉 STEP ONE:

package com.faraj.diy;

public class DiyPointCut {
    public void before(){
        System.out.println("Before method execute -->");
    }

    public void after(){
        System.out.println("--> After method execute");
    }
}

自定义插入的方法,是一个类

👉 STEP TWO:

<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.faraj.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.faraj.log.BeforeLog"/>
    <bean id="afterLog" class="com.faraj.log.AfterLog"/>

    <bean id="diy" class="com.faraj.diy.DiyPointCut"/>
    <aop:config>
        <aop:aspect ref="diy">
            <aop:pointcut id="pointcut" expression="execution(* com.faraj.service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="pointcut"/>
            <aop:after method="after" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

👉 STEP THREE: 测试类保持相同

3⃣️ 使用注解实现 AOP

👉 STEP ONE:

package com.faraj.diy;

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

@Aspect
public class AnnotationPointCut {

    @Before("execution(* com.faraj.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("before method execute");
    }

    @After("execution(* com.faraj.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("after method execute");
    }
}

👉 STEP TWO:

<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="userService" class="com.faraj.service.UserServiceImpl"/>
    <bean id="beforeLog" class="com.faraj.log.BeforeLog"/>
    <bean id="afterLog" class="com.faraj.log.AfterLog"/>

    <!--
    <bean id="annotationPointCut" class="com.faraj.diy.AnnotationPointCut"/>
    <aop:aspectj-autoproxy>
        <aop:include name="annotationPointCut"/>
    </aop:aspectj-autoproxy>
    -->
    <bean id="annotationPointCut" class="com.faraj.diy.AnnotationPointCut"/>
    <aop:aspect-autoproxy/>

</beans>

使用<aop:aspectj-autoproxy>实现使用注解自动完成 AOP

Spring 整合 Mybatis

相关 jar🎒

  • spring💭
  • Mybatis
  • Junit
  • aop 织入 - aspectjweaver
  • mysql
  • mybatis-spring🆕
<?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.faraj</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

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

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.19</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.3</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId >
            <artifactId>aspectjweaver</artifactId >
            <version>1.9.5</version >
        </dependency>
    </dependencies>

</project>

整合 Mybatis

1⃣️ 使用 Spring 管理 Mybatis 数据源

<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=true&amp;useUnicode=true&amp;
        characterEncoding=UTF-8&amp;serviceTimeZone=GMT"/>
    <property name="username" value="root"/>
    <property name="password" value="12345678"/>
</bean>

2⃣️ 注册 sqlSessionFactory

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="datasource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    <property name="mapperLocations" value="classpath:com/faraj/mapper/*.xml"/>
</bean>

3⃣️ 注册 SqlSessionTemplate

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

⚠️ 这里的 SqlSessionTemplate 类中没有 set 方法,所以只能通过构造方法注入

4⃣️ 创建实现类实现接口

package com.faraj.mapper;

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

import java.util.List;

public class UserMapperImpl implements UserMapper {
    private static SqlSessionTemplate sqlSession;

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

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

5⃣️ 注册实现类到 Spring 容器中并注入 SqlSessionTemplate

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

事务

标准配置

⚠️ 要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:

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

👆👆 在 Spring 容器中注入DataSourceTransactionManager并将dataSource注入

交由容器管理事务 ✍️(声明式事务)

<!-- 结合AOP实现事务的织入 -->
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="select" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

propagation

一、在声明式的事务处理中,要配置一个切面,其中就用到了 propagation,表示打算对这些方法怎么使用事务,是用还是不用,其中 propagation 有七种配置,REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。默认是 REQUIRED。
二、Spring 中七种 Propagation 类的事务属性详解:

· REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
· SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
· MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
· REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
· NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
· NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
· NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。

三、注意.
这个配置将影响数据存储,必须根据情况选择。

需要导入事务(tx)的约束‼️
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

AOP 织入 实现事务添加

<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.faraj.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>


java   spring      spring

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!