Spring使用手册
本文最后更新于:2021年1月8日 下午
先对Spring有个大概的认识…
一、Spring概述
1)介绍
- Spring:春天 —-> 简化开发流程,为软件行业带来了春天
- 2002年,首次推出了Spring框架的雏形:interface21框架
- Spring框架以interface21框架为基础,经过重新设计并不断丰富内涵,于2004年3月24日,发不了1.0正式版
- Rod Johnson,Spring FrameWork 创始人,著名作者,悉尼大学的博士,音乐学???!!
Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
两套技术框架有:
- SSH(基本不用了):Struct2 + Spring + Hibernate
- SSM:SpringMvc + Spring + Mybatis
- 官网:https://spring.io/
2)优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵式(不会对原先代码产生影响)的框架!
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务的处理,对框架整合支持较好
一句话总结,即 Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
3)组成
4)拓展
现代化的Java开发,基本就是基于Spring的开发了
- SpringBoot
- 一个快速开发的脚手架,可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- SpringCloud是基于SpringBoot开发的
- 先学习Spring及Spring MVC,再学习SpringBoot
二、IOC理论推导
- UserDao接口、UserDaoImpl实现类
- UserService接口、UserService实现类
在我们之前的业务中,用户的需求可能会影响我们原先的代码,我们需要根据用户的需求去修改源代码!如果程序代码量非常大,修改一次的成本代价将十分昂贵!
我们改为使用一个Set接口进行动态注入:
// UserServiceImpl.java
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao; // 利用set进行动态值的注入
}
// Mytest.java
userService.setUserDao(new UserDaoImpl());
//userService.setUserDao(new UserDaoMysqlImpl());
userService.getUser();
- 之前程序是主动创建对象,如
private UserDao userDao = new UserDaoMysqlImpl()
- 现在是被动接收对象,即控制反转了!
IOC原型:通过控制反转思想,从本质上解决了因需求大量修改源代码的问题,系统耦合性降低,可以更加专注的在业务实现上。
控制反转IOC(Inversion of Control)是一种 『基于XML或注解,通过第三方去生产或获取特定对象 』的方式。
Spring通过IoC容器实现控制反转,其实现方法是 依赖注入(Dependency Injection,DI)。
没有IoC的程序中,我们采用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制。控制反转后,将对象的创建转移给第三方来实现。
Spring容器在初始化时:
- 先读取配置文件,
- 根据配置文件,创建对象,并存入容器中,
- 程序使用时,再从Ioc容器中取出需要的对象。
所谓的IoC,可以理解为:对象由Spring来创建、管理和装配!
<?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来创建对象,这些对象在Spring中被称为bean(组件)
类型 变量名/对象名 = new 类型();
class hello = new Hello();
id = 变量名
class = new 的对象
property 相当于 给对象中的属性设置一个值
value: 具体的值,基本数据类型
ref:引用Spring容器中创建好的对象
控制:谁来控制对象的创建。传统程序中,对象由程序本身控制创建;而使用Spring后,对象由Spring来创建
反转:程序本身不再创建对象,而是被动地接收对象
依赖注入:通过set方法进行注入
-->
<bean id="hello" class="com.gaowl.pojo.Hello"> <!-- 该类中需设置了以下变量的set方法,否则报错 -->
<property name="str" value="Spring"/>
</bean>
<bean id="mysqlImpl" class="com.gaowl.dao.UserDaoMysqlImpl">
</bean>
<bean id="daoImpl" class="com.gaowl.dao.UserDaoImpl">
</bean>
<bean id="UserServiceImpl" class="com.gaowl.service.UserServiceImpl">
<property name="userDao" ref="mysqlImpl" />
</bean>
</beans>
测试示例:
import com.gaowl.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 解析xml文件,获取Spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); // 文件名需对应
// 我们的对象 现在全部在 Spring中了,如要使用,直接取出即可
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
三、IOC创建对象的方式
使用带参构造创建对象
<bean id="user" class="com.gaowl.pojo.User"> <property name="name" value="gaowl_up"/> </bean>
采用无参构造创建对象:
- 下标赋值
- 通过类型创建(不建议使用)
- 直接通过参数名设置
<bean id="user" class="com.gaowl.pojo.User"> <constructor-arg index="0" value="日拱一卒"/> <constructor-arg type="java.lang.String" value="gaowl"/> <constructor-arg name="name" value="gaowl"/> </bean>
总结:在配置文件加载时,容器中管理的对象就已经初始化了
四、Spring配置说明
1)alias
<!-- 设置别名,如果添加了别名,便可以通过别名来获取该对象了。更推荐使用name来设置别名-->
<alias name="user" alias="user_1"/>
2)bean
id
:bean的唯一标识符,对象名class
:bean对象的全限定名(全类名)name
:别名,而且 name可以起多个别名
<bean id="userT" class="com.gaowl.pojo.UserT" name="user2 u2,u3;u4">
<property name="name" value="gaowl_up"/>
</bean>
3)import
常用于团队开发使用,将多个配置文件导入合并为一个。
- 假设某项目需要张三、李四、王五一个开发,每个人负责不同的类,不同的类注册在不同的bean中,这时我们可以利用import将所有人的bean.xml合并为一个总的
<!-- applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
五、依赖注入
依赖注入,dependency injection,即 由容器来设置和装配bean对象所依赖的资源
1)构造器注入
通过xml文件中<constructor-arg index="0" value="日拱一卒"/>
方式注入,具体参考第三节
2)通过set方式注入
要求被注入的属性, 必须有set方法 。
student类
package com.gaowl.pojo; import java.util.*; public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Set<String> games; private String wife; private Properties info; private Map<String,String> card; // set、get、toString方法等 }
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"> <bean id="address" class="com.gaowl.pojo.Address"> <property name="address" value="重庆"/> </bean> <bean id="student" class="com.gaowl.pojo.Student"> <!-- 1.普通值注入,value --> <property name="name" value="gaowl"/> <!-- 2.引用类型/bean注入,ref --> <property name="address" ref="address"/> <!-- 引用上面已声明的bean --> <!-- 3.数组注入 --> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> </array> </property> <!-- 4.list注入 --> <property name="hobbies"> <list> <value>羽毛球</value> <value>骑行</value> </list> </property> <!-- 5.set注入 --> <property name="games"> <set> <value>LOL</value> <value>GTA5</value> </set> </property> <!-- 6.map注入 --> <property name="card"> <map> <entry key="中国邮政" value="123456789"/> <entry key="工商银行" value="987654321"/> </map> </property> <!-- 7.null注入,String类型初始值也为null --> <property name="wife"> <null/> </property> <!-- 8.property注入 --> <property name="info"> <props> <prop key="学号">6191922009</prop> <prop key="性别">男</prop> <prop key="爱好">女</prop> </props> </property> </bean> </beans>
3)其他方式注入
P命名空间注入:properties
导入约束 : xmlns:p="http://www.springframework.org/schema/p" <!--P(属性: properties)命名空间 , 属性依然要设置set方法--> <bean id="user" class="com.gaowl.pojo.User" p:name="gaowl" p:age="24"/>
C命名空间注入:constructor
导入约束 : xmlns:c="http://www.springframework.org/schema/c" <!-- C(构造: Constructor)命名空间 , 属性依然要设置set方法,还要有带参构造方法 --> <bean id="user" class="com.gaowl.pojo.User" c:name="gaowl" c:age="25"/>
4)Bean的作用域
singleton
:默认值,单例模式,bean以单例的方式存在。prototype
:原型模式,每次容器从容器中调用bean时,都会返回一个新的实例,即new xxxbean()<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton"> <bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="prototype">
request
:每次HTTP请求都会创建一个新的beansession
:同一个HTTP session共享一个bean,不同Session使用不同bean。- request和session作用域仅在基于Web的应用中使用,只能用在web的
Spring ApplicationContext
环境中
- request和session作用域仅在基于Web的应用中使用,只能用在web的
六、Bean的自动装配(重要)
Spring会在上下文中自动寻找,并自动给bean装配属性
1)byName和byType
byName
:自动在容器上下文中寻找 和自己对象 set方法后面的值对应的bean id;- 需要保证所有bean的id值唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
byType
:自动在容器上下文中寻找,和自己对象 属性类型相同的bean。因此,此时id是可以省略的- 需要保证所有bean的class唯一,并且这个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">
<bean id="cat" class="com.gaowl.pojo.Cat"/>
<bean id="dog" class="com.gaowl.pojo.Dog"/>
<bean id="person" class="com.gaowl.pojo.Person">
<property name="name" value="gaowl"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
</beans>
<?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">
<bean id="cat" class="com.gaowl.pojo.Cat"/>
<bean id="dog" class="com.gaowl.pojo.Dog"/>
<!-- byName:自动在容器上下文中寻找 和自己对象set方法后面的值对应的bean id;此处为搜索 cat和dog;-->
<bean id="person" class="com.gaowl.pojo.Person" autowire="byName">
<property name="name" value="gaowl"/>
</bean>
<!-- byType:自动在容器上下文中寻找,和自己对象属性类型相同的bean。因此,此时id是可以省略的,此处搜索类型 Cat类型 和 Dog类型 -->
<bean id="person" class="com.gaowl.pojo.Person" autowire="byType">
<property name="name" value="gaowl"/>
</bean>
</beans>
2)注解自动装配(更实用)
JdK1.5之后的版本,Spring2.5之后的版本
要使用注解,需先导入约束,并配置注解的支持
<?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/>
<bean id="cat" class="com.gaowl.pojo.Cat"/>
<bean id="dog" class="com.gaowl.pojo.Dog"/>
<bean id="person" class="com.gaowl.pojo.Person"/>
</beans>
🔎
@Autowired
:直接在属性上使用即可,也可以在set方式上使用!
可以省略Set方法,前提是自动装配的属性在IOC容器中存在,且可以通过
byType
找到多个时,采用
@Qualifier(value=“xxx”)
来协作使用,指定一个唯一的bean@Autowired private Cat cat; @Autowired @Qualifier(value="dog222") private Dog dog;
🔎
@Resource
:先通过名称去匹配唯一的bean,找不到再通过type去寻找唯一的bean,依然找不到会报错
多个时,通过
name
属性来指定唯一bean@Resource private Cat cat; @Resource(name="dog222") private Dog dog;
🔎
@Autowired
与@Resource
的区别:- 都是自动装配的,都可以放在属性字段上
- @Autowired通过
byType
的方式实现,且对象必须存在 - @Resource默认通过
byName
的方式实现,如果找不到名字,再通过byType
进行寻找
七、使用注解开发(重要)
在spring4之后,使用注解开发时,需保证aop的包已被导入;之后再在applicationContext.xml文件中导入约束并配置注解的支持
1)Bean的实现
配置扫描哪些包下的注释:
<!-- 扫描指定路径下的注解 --> <context:component-scan base-package="com.gaowl"/>
在指定包下编写类,增加注解
// 等价于在beans.xml文件中的 <bean id="user" class="com.gao.pojo.User"/> @Component public class User { // public String name = "gaowl"; // 相当于 <property name="name" value="gaowl"/> @Value("gaowl") public String name; }
2)属性注入
- 直接在变量名称上方添加
@Value(“具体值”)
,无需set方法 - 如果提供了set方法,也可以在set方法上添加
@Value(“具体值”)
。。没必要啊
3)@Component的衍生注解
@Controller
:web层@Service
:service层@Repository
:dao层- 以上均为将某个类注册到容器中,装配bean
@Scope("prototype")
指定作用域- 单例模式:singleton,仅有一个实例存在
- 原型模式:prototype,每次调用bean时均会返回一个新的实例
4)小结
XML与注解比较
- XML可以适用任何场景 ,结构清晰,维护方便
- 注解不是自己提供的类使用不了,开发简单方便
xml与注解整合开发 :
- xml管理Bean
- 注解只负责完成属性的注入
- 使用过程中, 可以不用扫描,但必须开启注解的支持
<context:annotation-config/>
的作用:- 进行注解驱动注册,从而使注解生效
- 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
- 如果不扫描包,就需要手动配置bean
- 如果不加注解驱动,则注入的值为null!
八、基于Java类进行配置
非常常用的一种方式
- 实体类
package com.gaowl.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
// 将该类注册到容器中
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("gaowl") // 注入数值
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
- 配置类
// 声明为配置类,并注册到容器中
@Configuration
@ComponentScan("com.gaowl.pojo")
@Import(GaowlConfig.class) // 将多个配置文件合并为一个
public class GaowlConfig2 {
// 将该方法注册为bean,相当于之前写xml文件的一个<bean>标签
// 这个方法的名字,相当于bean标签的id属性;
// 返回值,相当于bean标签的class属性
@Bean
public User getUser(){
return new User();
}
}
- 测试类
public class MyTest {
public static void main(String[] args) {
// 完全使用配置类时,通过 AnnotationConfig上下文 来获取容器,之后再通过 配置类的class对象 加载
ApplicationContext context = new AnnotationConfigApplicationContext(GaowlConfig.class);
User user = context.getBean("getUser", User.class); // 和config中的bean对应
System.out.println(user.getName());
}
}
九、代理模式(重要)
代理模式的分类:
- 静态代理
- 动态代理
1)静态代理
角色分析:
- 抽象角色:一般使用接口或者抽象类来解决,比如出租房子这个操作
- 真实角色:被代理的角色,比如房东
- 代理角色:代理真实角色,且代理真实角色后,一般会做一些附属操作,比如房子中介
- 客户:访问代理对象的人,比如想租房子的人,其直接跟房产中介谈就好,不用接触房东
代码步骤:
接口(出租房子操作)
public interface Rent { public void rent(); }
真实角色(房东)
public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子"); } }
代理角色(房中介)
public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent(){ seeHouse(); host.rent(); // 通过有参构造器拿到host对象 signContract(); fare(); } // 看房 public void seeHouse(){ System.out.println("中介正在带你看房..."); } // 签合同 public void signContract(){ System.out.println("签署合同..."); } // 收中介费 public void fare(){ System.out.println("收取中介费用..."); } }
客户端访问代理角色(租户)
package com.gaowl.demo01; public class Client { public static void main(String[] args) { // 房东要出租房子 Host host = new Host(); // host.rent(); // 代理:中介帮房东出租房子,且附属有一些其他操作 Proxy proxy = new Proxy(host); // 不用面对房东,直接找中介租房即可 proxy.rent(); } }
静态代理的好处:
- 使真实角色的操作更加纯粹,不用去关注一些公共事务
- 代理角色来负责公共事务,实现了业务的分工
- 公共事务发生扩展时,方便集中管理
静态代理的缺点:
- 一个真实角色就要有一个代理角色(???),代码量翻倍
一言以蔽之,代理的作用为:在不改变原有业务的基础上,增加一些新的功能
代码见:spring-08-proxy-demo2
2)动态代理
- 也是四个角色:抽象角色、真实角色、代理角色、用户
- 动态代理的代理类是动态生成的,无需自己手动编写
- 基于接口的动态代理:JDK动态代理
- 基于类的动态代理:cglib
- Java字节码实现:javasist
📘 InvocationHandler
由代理实例的 调用处理程序实现的 接口(java.lang.reflect
下,别导错了)
📘 Proxy类
提供创建动态代理类和实例的静态方法
package com.gaowl.demo04;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
// 被代理的接口
private Object target;
// 设置set方法
public void setTarget(Object target) {
this.target = target;
}
// 生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 处理代理实例,并返回结果
@Override
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("执行了" + msg + "方法");
}
}
测试程序
package com.gaowl.demo04;
import com.gaowl.demo02.UserService;
import com.gaowl.demo02.UserServiceImpl;
public class Client {
public static void main(String[] args) {
// 真实角色
UserServiceImpl userService = new UserServiceImpl();
// 代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
// 设置要代理的对象
pih.setTarget(userService);
// 动态生成代理类
UserService proxy = (UserService) pih.getProxy();
// 通过代理类执行相关方法
proxy.add();
}
}
动态代理的好处
- 使真实角色的操作更加纯粹,不用去关注一些公共事务
- 代理角色来负责公共事务,实现了业务的分工
- 公共事务发生扩展时,方便集中管理(以上三条和静态代理相同)
- 一个动态代理类代理的是一个接口,一般对应一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
十、AOP
1)什么是AOP
AOP(Aspect Oriented Programming),面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护。
利用AOP可以对业务逻辑的各个部分进行分离,从而降低业务逻辑各个部分间的耦合性,提高程序的可用性,从而提高开发效率
2)AOP在Spring中的应用
==提供声明式事务;允许用户自定义切面==
以下名词需要了解下:
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ….
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。一般为接口或者某个方法
- 代理(Proxy):向目标对象应用通知之后创建的对象。代理类
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,共有5种类型的Advice:
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前面 | org.springframework.aop.MethodBeforeAdvice |
后置通知 | 方法后面 | org.springframework.aop.AfterReturningAdvice |
环绕通知 | 方法前后 | org.springframework.aop.MethodInterceptor |
异常抛出通知 | 方法抛出异常 | org.springframework.aop.ThrowsAdvice |
引介通知 | 类中增加新的方法属性 | org.springframework.aop.IntroductionInterceptor |
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .
3)使用Spring实现AOP
使用AOP织入,需要先导入aspectjweaver依赖包!
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
A.通过 Spring API 实现
/**
* method:要执行的目标对象的方法
* args:被调用的方法的参数
* target:目标对象
*/
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了...");
}
}
/**
* returnValue:方法的返回值
* thod:要执行的目标对象的方法
* args:被调用的方法的参数
* target:目标对象
*/
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了" + target.getClass().getName() + "的" + method.getName() + "方法,其返回值为" + returnValue);
}
}
<?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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean -->
<bean id="userService" class="com.gaowl.service.UserServiceImpl"/>
<bean id="afterLog" class="com.gaowl.springapi.AfterLog"/>
<bean id="beforeLog" class="com.gaowl.springapi.BeforeLog"/>
<!--aop的配置方法1:使用springapi -->
<aop:config>
<!-- 切入点,此处expression匹配要执行的方法 -->
<aop:pointcut id="pointcut" expression="execution(* com.gaowl.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/> <!-- 执行环绕;advice-ref为 执行 上面已声明的方法 -->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
B.自定义类实现AOP
- 自定义切入类
public class DiyPointCut {
public void before(){
System.out.println("---------方法执行前---------");
}
public void after(){
System.out.println("---------方法执行后---------");
}
}
- 在spring配置文件中注册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"
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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean -->
<bean id="userService" class="com.gaowl.service.UserServiceImpl"/>
<bean id="diy" class="com.gaowl.diy.DiyPointCut"/>
<!-- aop的配置方法二:自定义类 -->
<!-- 当执行UserServiceImpl中所有方法时,先执行前置before方法,再执行具体的方法,最后还要执行后置after方法 -->
<aop:config>
<aop:aspect ref="diy"> <!-- 自定义切面 -->
<aop:pointcut id="diyPointcut" expression="execution(* com.gaowl.service.UserServiceImpl.*(..))"/> <!--切入点-->
<aop:before method="before" pointcut-ref="diyPointcut" /> <!--通知,method为 自定义类中的方法-->
<aop:after method="after" pointcut-ref="diyPointcut" />
</aop:aspect>
</aop:config>
</beans>
C.通过注解实现AOP
- 创建一个通过注解实现的增强类
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.gaowl.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.gaowl.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.gaowl.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
- 在Spring配置文件中,注册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"
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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean -->
<bean id="userService" class="com.gaowl.service.UserServiceImpl"/>
<bean id="diy" class="com.gaowl.diy.DiyPointCut"/>
<!--aop的配置方法三:通过注解实现-->
<bean id="annotationPointcut" class="com.gaowl.diy.AnnotationPointcut"/>
<aop:aspectj-autoproxy/> <!-- 该声明自动为spring容器中那些配置@aspectj切面的bean创建代理,织入切面 -->
</beans>
十一、整合Mybatis
1)基本步骤
在pom.xml中导入相关jar包
- junit、mybatis、mysql数据库、spring相关的、aop织入、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.gaowl</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-10-mybatis</artifactId> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> </dependencies> </project>
创建实体类、接口、Mapper实现
package com.gaowl.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
package com.gaowl.dao; import com.gaowl.pojo.User; import java.util.List; public interface UserMapper { public List<User> getUsers(); }
package com.gaowl.dao; import com.gaowl.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper{ private SqlSessionTemplate sqlSession; // 通过spring-dao.xml传入 public void setSqlSession(SqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } // 之前是在测试类中获取sqlSession并调用相关方法 @Override public List<User> getUsers() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.getUsers(); } }
<?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"> <mapper namespace="com.gaowl.dao.UserMapper"> <select id="getUsers" resultType="User"> select * from mybatis.user </select> </mapper>
编写Spring的配置文件
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid 此外,我们使用Spring提供的JDBC --> <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=false&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- 【重点】sqlSessionFactory,并制定和上面datasource的连接 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 绑定Mybatis配置文件 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:com/gaowl/dao/UserMapper.xml"/> </bean> <!-- 【重点】使用SqlSessionTemplate来创建我们要使用的SqlSession, 此处采用sqlSessionFactory作为构造方法的参数创建SqlSessionTemplate对象。 --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory"/> </bean> </beans>
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 引入子配置文件 --> <import resource="spring-dao.xml"/> <!-- 将实现类注入到Spring中 --> <bean id="UserMapper" class="com.gaowl.dao.UserMapperImpl"> <property name="sqlSession" ref="sqlSession"/> </bean> </beans>
测试
import com.gaowl.dao.UserMapper; import com.gaowl.pojo.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.util.List; public class MyTest { @Test public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 动态代理代理的是接口,而不是类,因此此处为 UserService 而不是 UserServiceImpl UserMapper userMapper = context.getBean("UserMapper", UserMapper.class); List<User> users = userMapper.getUsers(); for (User user : users) { System.out.println(user); } } }
2)遇到的问题
java.lang.NoSuchMethodError: org.springframework.beans.factory.config.BeanDefinition.getResolvableType()Lorg/springframework/core/ResolvableType;
at org.springframework.context.event.AbstractApplicationEventMulticaster.supportsEvent(AbstractApplicationEventMulticaster.java:311)
at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:243)
- 检查maven中的spring-jdbc和spring-webmvc是否版本一致。此处使用
5.2.0.RELEASE
版本
3)使用SqlSession
在 MyBatis 中,你可以使用 SqlSessionFactory
来创建 SqlSession
。 一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。 使用 MyBatis-Spring 之后,你不再需要直接使用 SqlSessionFactory
了,因为你的 bean 可以被注入一个线程安全的 SqlSession
,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session。
- SqlSessionTemplate
SqlSessionTemplate
是 MyBatis-Spring 的核心。作为 SqlSession
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession
。 SqlSessionTemplate
是线程安全的,可以被多个 DAO 或映射器所共享使用。
具体见11.1基本步骤
<!-- 【重点】使用SqlSessionTemplate来创建我们要使用的SqlSession,
此处采用sqlSessionFactory作为构造方法的参数创建SqlSessionTemplate对象。 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
- SqlSessionDaoSupport
SqlSessionDaoSupport
是一个抽象的支持类,用来为你提供 SqlSession
。调用 getSqlSession()
方法你会得到一个 SqlSessionTemplate
,之后可以用于执行 SQL 方法。
package com.gaowl.dao;
import com.gaowl.pojo.User;
import java.util.List;
import org.mybatis.spring.support.SqlSessionDaoSupport;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> getUsers() {
return getSqlSession().getMapper(UserMapper.class).getUsers();
}
}
<bean id="UserMapper2" class="com.gaowl.dao.UserMapperImpl2">
<!--此处传入sqlSessionFactory给父类,相当于原先SqlSessionTemplate创建,现在SqlSessionDaoSupport创建,
省略了实现类中的set方法之类的,也没方便多少,看着用吧-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
十二、事务
1)事务简介
要么都成功,要么都失败。有效地确保了完整性和一致性(非常重要!!)。
事务的ACID原则:
- 原子性、一致性、隔离性(多个业务操作同一个资源时相互隔离)、持久性
2)spring中的事务管理
声明式事务(不改变原有的代码)
MyBatis-Spring 借助了 Spring 中的
DataSourceTransactionManager
来实现事务管理,在spring-dao.xml中配置如下:<!-- 配置声明式事务 --> <!-- 为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的是同一个数据源,否则事务管理器就无法工作了。 --> <bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource"/> </bean> <!-- 结合aop实现事务的织入 --> <!-- 配置事务通知 --> <tx:advice id="txAdvice" transaction-manager="transaction"> <!-- 指定要配置事务的方法,事务的传播特性(总共7种) --> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="update" propagation="REQUIRED"/> <tx:method name="query" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置事务切入 --> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.gaowl.dao.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> <!-- 该包下的所有方法均配置事务--> </aop:config>
- 编程式事务(需要改变代码,不建议使用)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!