—— 目录 ——

  • 0. Spring 简介
  • 1. 基础使用
  • 2. 别名与导入
  • 3. 依赖注入
  • 4. 作用域
  • 5. 自动装配
  • 6. 使用注解开发
  • 7. AOP 面向切面编程
  • ① AOP 配置
  • ② 使用 spring 的接口实现 AOP
  • ③ 使用自定义类实现 AOP
  • ④ 使用注解实现 AOP
  • 8. 整合 MyBatis
  • ① 导入依赖
  • ② 在 Spring 配置文件中整合 MyBatis
  • ③ 具体使用
  • 9. Spring 声明式事务(AOP 方法)

0. Spring 简介

  • Spring 是一个轻量级的,非入侵式的框架(包很小,也不需要改变原本的代码)
  • 控制反转(IOC),面向切面编程(AOP)
  • 支持事务的处理,支持对框架的整合

包含了七大模块:

spring 删除log数据_spring


IOC 理论

IOC 控制反转是一种设计思想,DI 依赖注入是其中一种实现方法

对象由 Spring 来创建以及管理,不用手动创建了


1. 基础使用

① 文件格式与构造无参对象

<?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">

    <!-- 使用 Spring 来创建对象,在 Spring 中都称为 bean -->
    <bean id="user" class="com.iceclean.po.User">
        <property name="userName" value="IceClean"/>
    </bean>
</beans>

里边的 bean 相当于对象,当配置文件中的 bean 被注册是,对象就已经被创建了,之后直接 get 就可以拿到对象了

② 有参构造器创建对象的方法:

  • 方法1:使用名字赋值(推荐!)
<bean id="user1" class="com.iceclean.po.User">
    <constructor-arg name="userName" value="IceClean"/>
    <constructor-arg name="userPass" value="iceclean"/>
</bean>
  • 方法2:使用下标赋值
<bean id="user2" class="com.iceclean.po.User">
    <constructor-arg index="0" value="IceClean"/>
    <constructor-arg index="1" value="iceclean"/>
</bean>
  • 方法3:使用类型赋值(不建议使用!如下两个参数都是 String 类型,就会出现参数按顺序赋值,可能颠倒位置)
<bean id="user3" class="com.iceclean.po.User">
    <constructor-arg type="java.lang.String" value="iceclean"/>
    <constructor-arg type="java.lang.String" value="IceClean"/>
</bean>

③ 具体使用
在 Java 中获取到 Spring 的上下文对象,使用 get 得到对象

@Test
public void test01() {
    // 获取 Spring 的上下文对象
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

    // 直接去 Spring 中取出对象就好了
	User user = (User) context.getBean("user");
	User user2 = (User) context.getBean("user");
	System.out.println(user == user2);
}

注意:
以上结果输出为 true
通过 get 同一个 bean,获取到的是同意个对象,Spring 只创建了一份

2. 别名与导入

① 别名
可以通过别名可以创建对象

1) 通过 name 取别名(可以取多个,中间用逗号、空格、分号而开)
<bean id="user" class="com.iceclean.po.User" name="newUser1, newUser2">
    <property name="userName" value="IceClean"/>
</bean>

通过 alias 创建别名(只能创建一个别名,用 name 比较好)
<alias name="user" alias="newUser"/>

② 导入
使用 import 可以将其他 bean 文件导入,达到可以使用其他文件的作用

<import resource="beans.xml"/>

3. 依赖注入

<bean id="temp" class="com.iceclean.po.temp"/>

<bean id="user" class="com.iceclean.po.User" name="newUser1, newUser2">

	(1) 普通注入
    <property name="userName" value="IceClean"/>

	(2) Bean 注入
	<property name="temp" ref="tmp"/>

	(3) 数组注入
	<property name="xxx"/>
		<array>
			<value>xxx</value>
			<value>xxx</value>
		</array>
	</property>
	
	(4) List 注入
	<property name="xxx"/>
		<list>
			<value>xxx</value>
			<value>xxx</value>
		</list>
	</property>
	
	(5) Set 注入
	<property name="xxx"/>
		<set>
			<value>xxx</value>
			<value>xxx</value>
		</set>
	</property>
	
	(6) Map 注入
	<property name="xxx"/>
		<map>
			<entry key="xxx" value="xxx"/>
			<entry key="xxx" value="xxx"/>
		</map>
	</property>

	(7) 空注入
	<property name="xxx"> <null/> </property>
	
	(8) Properties 注入
	<property name="xxx"/>
		<props>
			<prop key="xxx">xxx</prop>
			<prop key="xxx">xxx</prop>
		</map>
	</props>
</bean>

拓展:p 命名空间和 c 命名空间

<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- 需要加入上面的 p 和 c 才能使用 -->
	<!-- p 命名空间,对变量注入 -->
	<bean id="user" class="com.iceclean.po.User" p:userName="IceClean"/>
	<!-- c 命名空间,对构造器注入 -->
	<bean id="user1-1" class="com.iceclean.po.User" c:userName="IceCLean" c:userPass="iceclean"/>

</beans>

4. 作用域

1) 单例模式(默认就是,可以不用谢)
<bean id="user" class="com.iceclean.po.User" scope="singleton"/>
该作用域下,每一次通过 bean 获取得到的对象是同一个

2) 原型模式
<bean id="user" class="com.iceclean.po.User" scope="prototype"/>
该作用域下,每一次通过 bean 获取都是不同的对象

3) 此外还有:request、session、application 和 websocket,只能在 web 开发中用到

5. 自动装配

在引用其他 bean 类型时,通常需要通过 ref=“xxx” 来完成装配,但如果该类型已经在容器中装配过了,另一个 bean 需要用到时是否能自动识别并装配减少重复代码呢?

答案是肯定的,旧用法可以在 bean 配置文件中加入 autowire="byName"autowire="byType"来完成

前一个通过匹配 bean 中需要的属性名和已装配的 bean id 来完成自动装配
后一个则是通过匹配类型完成
前一个只要属性名和 bean id 对不上则匹配失败,后一个若同时又多个相同的类型也匹配失败

而新版的则使用注解实现自动装配:

导入约束和配置注解的支持

<?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/context
        https://www.springframework.org/schema/context/spring-context.xsd
        
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- 配置注解支持 -->
	<context:annotation-config/>
</bean>

接着在 java 类中加入 @Autowired,若设置 required = false,则若该值为空也不会报错
默认会通过 byType 类型去装配,找不到的话讲报错
但使用 @Qualifier(value=xxx) 可以指定一个名字指去匹配 bean 的 id (byName),从而完成自动装配


6. 使用注解开发

同上需要导入约束和配置注解的支持
其次还需要设置扫描哪一个包:

<context:component-scan base-package="com.iceclean.po"/>

@Component: 代表被注解的类是一个 bean
它有三个衍生注解,功能都和 @Component 一样
都代表将被注解的类注册到 Spring 中,装配 Bean

  • dao 层使用 @Repository
  • service 层使用 @Service
  • controller 层 使用 @Controller

@Value(“xxx”): 注入值,相当于 <property name="name", value="xxx">@Scope(“xxx”): 设置作用域,默认为 singleton 单例模式

纯 Java 配置(在 SpringBoot 常用)
@Configurable: 代表被注解的类是一个配置类,相当于之前的 beans.xml,同时它也拥有 @Component 的注解,会被注册到 Spring 中

@ComponentScan(“xxx”): 扫描包

@Bean: 代表被注册的方法是一个 bean,相当于之前的 bean 标签,方法的名字相当于 bean 的 id 属性,方法的放回置相当于 bean 的 class 属性,如:

@Bean
public User user() {
	return new User();	// 返回要注入到 bean 的对象
}

@Import(“xxx.class”): 导入另一个配置类,相当于之前导入其他 beans.xml 文件


7. AOP 面向切面编程

AOP 的主要作用就是在不侵入原有程序的基础上实现对原有功能的增强,底层就是动态代理

① AOP 配置

(1) 需要导入一个依赖

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

(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: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>

② 使用 spring 的接口实现 AOP

BeforeLog.java:
public class BeforeLog implements MethodBeforeAdvice {

    /**
     * 前置增强
     * @param method 要执行的目标对象的方法
     * @param args 参数
     * @param target 目标对象
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + " => " + method.getName());
    }
}

AfterLog.java:
public class AfterLog implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + method.getName() + " 返回了:" + returnValue);
    }
}
<bean id="testService" class="com.iceclean.service.TestServiceImpl"/>
<bean id="beforeLog" class="com.iceclean.log.BeforeLog"/>
<bean id="afterLog" class="com.iceclean.log.AfterLog"/>

<!-- 配置 AOP -->
<aop:config>
    <!-- 切入点:expression 中的 execution(要执行的位置 public void 类名 方法名 参数) -->
    <aop:pointcut id="pointcut" expression="execution(* com.iceclean.service.TestServiceImpl.*(..))"/>

    <!-- 执行环绕增强 -->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

③ 使用自定义类实现 AOP

Log.java:
public class Log {
    public void beforeLog() {
        System.out.println("执行了前置日志");
    }

    public void afterLog() {
        System.out.println("执行了后置日志");
    }
}
<bean id="log" class="com.iceclean.log.Log"/>
<!-- 自定义类配置 AOP -->
<aop:config>
    <!-- 自定义切面,ref 为要引用的类 -->
    <aop:aspect ref="log">
        <!-- 切入点 -->
        <aop:pointcut id="point" expression="execution(* com.iceclean.service.TestServiceImpl.*(..))"/>
        <!-- 通知 -->
        <aop:before method="beforeLog" pointcut-ref="point"/>
        <aop:after method="afterLog" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

④ 使用注解实现 AOP

// 使用 @Aspect 声明该类为切面
@Aspect
public class PowerLog {

    @Before("execution(* com.iceclean.service.TestServiceImpl.*(..))")
    public void beforeLog() {
        System.out.println("超级日志前置增强");
    }

    @After("execution(* com.iceclean.service.TestServiceImpl.*(..))")
    public void afterLog() {
        System.out.println("超级日志后置增强");
    }

    @Around("execution(* com.iceclean.service.TestServiceImpl.*(..))")
    public void aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("超级环绕日志前");
        joinPoint.proceed();
        System.out.println("超级环绕日志后");
    }
}
<!-- 方式三:使用注解 -->
<bean id="poserLog" class="com.iceclean.log.PowerLog"/>
<!-- 开启注解支持 -->
<aop:aspectj-autoproxy/>
运行结果:

超级环绕日志前
超级日志前置增强
服务1执行了
超级日志后置增强
超级环绕日志后

可以看到环绕是在最外层的,前置和后置则在方法执行的两边,被环绕包裹

注意: 环绕方法可以不加,但如果加了需要有参数 ProceedingJoinPoint joinPoint,并且执行方法 joinPoint.proceed();,否则被增强的方法不会执行


8. 整合 MyBatis

① 导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.9</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.23</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <!-- Spring 操作数据库需要的包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.9</version>
    </dependency>
	<!-- 整合 mybatis 和 spring 需要的包 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
</dependencies>

② 在 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- DataSource:使用 Spring 的数据源替换 MyBatis 的配置 -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="xxx"/>
        <property name="url" value="xxx"/>
        <property name="username" value="xxx"/>
        <property name="password" value="xxx"/>
    </bean>

    <!-- sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>

        <!-- 绑定 MyBatis 配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- SqlSessionTemplate 就是以前使用的 SqlSession -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 使用构造器注入 -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
</beans>

③ 具体使用

(1) 使用方法一

public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

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

    @Override
    public User findUserById(int userId) {
        return sqlSession.getMapper(UserMapper.class).findUserById(userId);
    }
}
<bean id="userMapper" class="com.iceclean.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

(2) 使用方法二

public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{

    @Override
    public User findUserById(int userId) {
        return getSqlSession().getMapper(UserMapper.class).findUserById(userId);
    }
}
<bean id="userMapper" class="com.iceclean.mapper.UserMapperImpl">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

9. Spring 声明式事务(AOP 方法)

(1) 配置声明式事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <constructor-arg ref="dataSource"/>
</bean>

(2) 配置事务通知

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 设置给哪些方法配置事务 -->
    <tx:attributes>
        <!-- 给所有方法配置事务 -->
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

(3) 配置事务切入

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

看着冰河,慢慢地就融为万物(IceClean)