SSM框架:Spring
狂神说
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.23version>
dependency>
https://docs.spring.io/spring-framework/docs/
https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/core.html#spring-core
总结:Spring就是一个 轻量级 的 控制反转(IOC) 和 面向切面编程(AOP) 的框架!
1、Spring Core
Core模块是Spring的核心容器,它采用工厂模式实现了IoC模式容器(即依赖注入),提供了Spring框架的基础功能。主要组件是BeanFactory,负责对JavaBean的配置与管理。
2、Context模块
继承BeanFactory类,向框架提供上下文信息。提供了事件处理、国际化、资源装载、透明装载、数据校验等功能,还有其它企业服务功能如JNDI访问、支持EJB、远程调用、集成模板框架、E-mail、定时任务调度等功能。
其它程序可通过Context访问Spring的Bean资源(相当于资源注入)
3、AOP模块
集成了所有AOP功能,通过事务管理可使任何Spring管理的对象AOP化。提供了常用的拦截器,供用户自定义和配置。
Aspect Oriented Programming:面向切面(方面)编程
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等。
4、DAO模块
提供了JDBC的抽象层,提供了有意义的异常层次结构,简化了数据库厂商的异常错误处理,大幅减少代码量,提供对声明式和编程式事务的支持。
5、ORM映射模块
提供对现有ORM框架的管理和支持,本身并不对ORM进行实现,仅对常见ORM框架进行封装并进行管理。
6、Web模块
建立在Spring Context基础上,提供了Servlet监听器的Context和Web应用的上下文。
对现有的Web框架提供了集成管理,能将Spring的资源注入给这些框架,并在这些框架前后插入拦截器。
简化了处理多部分请求,以及将请求参数绑定到域对象的工作。
7、MVC模块
建立在Spring核心功能上,拥有Spring框架的所有特性,适应多种模板技术,多视图,国际化和验证服务,实现控制逻辑和业务逻辑的清晰分离。是一个全功能的构建Web应用的MVC实现。
通过策略接口,MVC框架变得高度可配置,容纳了大量视图技术,包括JSP,Velocity,Tiles,iText和POI等。
Spring三大核心思想:
1、控制反转
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法
2、依赖注入
(常用的注入方式有四种:
1.属性注入
2.构造方法注入
3.工厂方法注入
4.注解注入
3、面向切面编程
Spring最核心,最基础的概念是什么?
将spring类比java,java最核心,最基础的概念就是object了。java中,所有的操作都是针对object的(基础类型除外),java中,一切皆对象,一切都是object。类比下来,spring中最基础的概念是bean。在spring中,所以的类都可以认为是一个bean。
在spring中的所有文件,都可以认为是注册了的bean和未注册的bean。 spring中所有操作都是针对bean的操作。自然,spring的三大核心思想中操作的对象,也是bean
在Spring的官网有这个介绍:现代化的Java开发!说白了就是基于Spring的开发
(学习思路)
构建 --> 协调 --> 连接
Spring Boot 构建
Spring Cloud 协调
Spring Cloud Data Flow 连接
因为现在大多数公司都在使用 SpringBoot 进行快速开发,学习 SpringBoot 的前提,需要完全掌握 Spring 及 SpringMVC !承上启下的作用!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”
1.UserDao 接口
2.UserDaoImpl 实现类
3.UserService 业务接口
4.UserServiceImpl 业务实现类
新建一个项目
导包
删除src目录
检查导包是否成功
1、先写一个UserDao接口
public interface UserDao {
public void getUser();
}
2、再去写Dao的实现类
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
3、然后去写UserService的接口
public interface UserService {
public void getUser();
}
4、最后写Service的实现类
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
(继承加组合的概念)
5、测试一下
public static void main(String[] args) {
UserService service = new UserServiceImpl();
service.getUser();
}
6、当用户需求增加或修改
7、把Userdao的实现类增加一个
public class UserDaoMySqlImpl implements UserDao {
@Override
public void getUser() {
System.out.println("MySql获取用户数据");
}
}
8、去service实现类里面修改对应的实现
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoMySqlImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
9、用户在增加需求
10、再增加一个Userdao的实现类
public class UserDaoOracleImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
要使用Oracle , 又需要去service实现类里面修改对应的实现 . 假设我们的这种需求非常大 , 这种方式就根本不适用了.每次变动 , 都需要修改大量代码 . 这种设计的耦合性太高了, 牵一发而动全身 .
缺点:程序受不了用户的变更,修改时需要不断修改太多代码(数据库操作层 --> 业务层)
12、解决方法:
利用set进行动态实现值的注入!
从 由我们手动修改业务层 到 由客户动态选择操作
不用改源码,解耦合
在需要用到它的地方 , 不去实现它 , 而是留出一个接口 , 利用set进行动态实现值的注入
13、修改UserService实现类
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set实现(留出一个接口)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
14、测试类里 , 进行测试
public static void main(String[] args) {
UserServiceImpl service = new UserServiceImpl();
service.setUserDao( new UserDaoMySqlImpl() );
service.getUser();
//那我们现在又想用Oracle去实现呢
service.setUserDao( new UserDaoOracleImpl() );
service.getUser();
}
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!如果程序代码量十分大,修改一次的成本代价十分昂贵!
之前:主动权在程序员(程序是主动创建对象!控制权在程序员手上)
使用set注入后,程序不再具有主动性,而是变成了被动的接受对象
我们使用一个Set接口实现,已经发生了革命性的变化!
核心代码:
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
这种思想,从本质上解决了问题,我们程序员不用再去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上!这是IOC的原型!【控制反转】
之前:主动权在程序员
现在:主动权在用户
程序员:提供者;用户:调用者
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
1、新建一个maven项目,编写实体类
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
2、编写xml配置文件 beans.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring这些都称为Bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值!
-->
<bean id="hello" class="com.qia.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
3、测试
import com.qia.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象! //ClassPathXmlApplicationContext,用xml加载时必写的,可以传多个配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
Hello hello = (Hello) context.getBean("hello");//原来用new取对象,现在context.getBean可以取一切对象
System.out.println(hello.toString());
}
}
思考问题?
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的。
IOC是一种编程思想,由主动的编程变成被动的接收。
可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。
OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!
配置后出现左侧小叶子,点击跳转beans.xml里的bean容器(实例化容器)
Spring代码 不断继承
所以ClassPathXmlApplicationContext类路径加载xml只是继承他的某一个类,也说明了DI依赖并不直接等于IOC,他也是继承的其中之一的一种方式
举个例子:
原来是,你来餐厅吃饭,点菜单上的菜(菜单上的菜:固定好的,只能吃这些)
现在是,你来餐厅吃饭,你告诉餐厅你要吃什么菜(由用户选择)
主动权置换
修改项目1试试
1、beans.xml
<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="mysqlImpl" class="com.qia.dao.UserDaoMysqlImpl"/>
<bean id="oracleImpl" class="com.qia.dao.UserDaoOracleImpl"/>
<bean id="sqlserverImpl" class="com.qia.dao.UserDaoSqlserverImpl"/>
<bean id="UserServiceImpl" class="com.qia.service.UserServiceImpl">
<property name="userDao" ref="mysqlImpl"/>
bean>
beans>
从改程序变成了改配置,改配置比该程序好,因为程序之间有调用关系改起来很麻烦
且配置不需要编译,改了就能用,源码需要编译才能运行
定义了有参构造,没定义无参构造
没有无参构造,底层反射就不好直接new对象了,所以会报错
https://docs.spring.io/spring-framework/docs/
https://docs.spring.io/spring-framework/docs/5.2.0.RELEASE/spring-framework-reference/core.html#spring-core
1、使用无参构造创建对象,默认!
2、假设我们要使用有参构造创建对象。
1.下标赋值
2.类型
3.参数名
<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="user" class="com.qia.pojo.User">
<constructor-arg type="java.lang.String" value="tutu"/>
bean>
<bean id="user" class="com.qia.pojo.User">
<constructor-arg name="name" value="李发"/>
bean>
beans>
import com.qia.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//以前
// User user = new User();//User的无参构造
//对比
//现在
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");//User的无参构造//获取spring上下文的时候,所有的对象在容器中就已经创建好了
user.show();
// System.out.println(user);
}
}
写一个UserTwo实体类进行测试
package com.qia.pojo;
public class UserTwo {
private String name;
public UserTwo() {
System.out.println("User2的无参构造");
}
public UserTwo(String name) {
this.name = name;
System.out.println("User2的有参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name="+name);
}
}
<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="user" class="com.qia.pojo.User">
<constructor-arg name="name" value="狂神"/>
bean>
<bean id="userTwo" class="com.qia.pojo.UserTwo">
<constructor-arg name="name" value="狂神2"/>
bean>
beans>
import com.qia.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");//User的无参构造//获取spring上下文的时候,所有的对象在容器中就已经创建好了
user.show();
// System.out.println(user);
/*
bean容器放了UserTwo后,程序执行结果变成了
User的有参构造
User2的有参构造
name=狂神
所以,在获取ApplicationContext对象,即获取上下文环境时,Spring容器中的所有对象都会被创建
*/
System.out.println("==========");
User user2 = (User) context.getBean("user");
System.out.println(user == user2);//true,同一个类只会被加载一次 //scope=prototype就是false了
}
}
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
bean
alias
beans
description
import
方式一
<alias name="user" alias="userAlias"/>
import com.qia.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// User user = (User) context.getBean("user");
User user = (User) context.getBean("userAlias");
user.show();
}
}
方式二
<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="userTwo" class="com.qia.pojo.UserTwo" name="user2 u2,u3;u4">
<property name="name" value="狂神"/>
bean>
beans>
import com.qia.pojo.User;
import com.qia.pojo.UserTwo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// UserTwo user2 = (UserTwo) context.getBean("userTwo");
// UserTwo user2 = (UserTwo) context.getBean("user2");
// UserTwo user2 = (UserTwo) context.getBean("u2");
// UserTwo user2 = (UserTwo) context.getBean("u3");
UserTwo user2 = (UserTwo) context.getBean("u4");
user2.show();
//以上别名都可以使用,程序都能正常运行
}
}
<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="userTwo" class="com.qia.pojo.UserTwo" name="user2 u2,u3;u4">
<property name="name" value="狂神"/>
bean>
beans>
如:
张三
李四
王五
applicationContext.xml
<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">
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
beans>
使用的时候,直接使用总的配置就可以了。
根据import的顺序,同id名的bean相当于同一个变量,会后面的覆盖前面的
前面已经介绍过了
参考 四、IOC创建对象的方式
【环境搭建】
1、复杂类型
基本数据类型value赋值,引用类型ref赋值
package com.qia.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2、真实测试对象
package com.qia.pojo;
import lombok.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name; //String
private Address address; //类对象数型
private String[] books; //数组
private List<String> hobbies; //List
private Map<String,String> card;//Map
private Set<String> games; //Set
private String wife; //有没有妻子,可以设置成空指针
private Properties info; //配置类
}
3、beans.xml
<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="student" class="com.qia.pojo.Student">
<property name="name" value="兔兔"/>
bean>
beans>
4、测试类
import com.qia.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.getName());
System.out.println(student);
}
}
5、完善注入信息
注入属性的值有多种类型可以设置
<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="address" class="com.qia.pojo.Address">
<property name="address" value="中国"/>
bean>
<bean id="student" class="com.qia.pojo.Student">
<property name="name" value="兔兔"/>
<property name="address" ref="address"/>
<property name="books">
<array>
<value>红楼梦value>
<value>西游记value>
<value>水浒传value>
<value>三国演义value>
array>
property>
<property name="hobbies">
<list>
<value>听歌value>
<value>敲代码value>
<value>看电影value>
list>
property>
<property name="card">
<map>
<entry key="身份证" value="111122223333"/>
<entry key="银行卡" value="123123123123"/>
map>
property>
<property name="games">
<set>
<value>LOLvalue>
<value>COCvalue>
<value>BOBvalue>
set>
property>
<property name="wife">
<null/>
property>
<property name="info">
<props>
<prop key="driver">jdbcprop>
<prop key="url">mysqlprop>
<prop key="username">rootprop>
<prop key="password">123456prop>
props>
property>
bean>
beans>
import com.qia.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
// System.out.println(student.getName());
System.out.println(student);
/*
Student(
name=兔兔,
address=Address(address=中国),
books=[红楼梦, 西游记, 水浒传, 三国演义],
hobbies=[听歌, 敲代码, 看电影],
card={身份证=111122223333, 银行卡=123123123123},
games=[LOL, COC, BOB],
wife=null,
info={password=123456, url=mysql, driver=jdbc, username=root}
)
*/
}
}
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
使用:
<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">
<bean id="user" class="com.qia.pojo.User" p:name="兔兔" p:age="18"/>
<bean id="user2" class="com.qia.pojo.User" c:name="狂神" c:age="22"/>
beans>
测试:
import com.qia.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
// User user = context.getBean("user", User.class);//p
User user = context.getBean("user2", User.class);//c
//上下两种方式等价
// User user = (User) context.getBean("user");
System.out.println(user);
}
}
注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
默认是单例
单例作用域
仅管理单例 Bean 的一个共享实例,并且所有对 ID 或 ID 与该 Bean 定义匹配的 Bean 的请求都会导致 Spring 容器返回该特定 Bean 实例。
换句话说,当你定义一个bean定义并且它的范围是一个单例时,Spring IoC容器只创建由该bean定义定义的对象的一个实例。此单个实例存储在此类单例 Bean 的缓存中,并且对该命名 Bean 的所有后续请求和引用都将返回缓存的对象。下图显示了单例作用域的工作原理:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2", User.class);
User user2 = context.getBean("user2", User.class);
System.out.println(user == user2);//true
}
}
当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
默认就是单例作用域,也可以显式的定义单例作用域
设置 singleton=false 的bug被修复了
:每次从容器中get的时候,都会产生一个新对象!
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
<bean id="user2" class="com.qia.pojo.User" c:name="狂神" c:age="22" scope="prototype"/>
System.out.println(user == user2);//false
其余的request、session、application、这些只能在web开发中使用到!
Singleton:并发时可能会有问题
Prototype:可能会浪费内存
尽量使用单例
前面都是手动装配
在Spring中有三种装配的方式:
环境搭建:创建项目,一个人有两个宠物!
public class Cat {
public void shout(){
System.out.println("miao~");
}
}
public class Dog {
public void shout(){
System.out.println("wang~");
}
}
import lombok.*;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
private String name;
private Cat cat;
private Dog dog;
}
<bean id="cat" class="com.qia.pojo.Cat"/>
<bean id="dog" class="com.qia.pojo.Dog"/>
<bean id="people" class="com.qia.pojo.People">
<property name="name" value="狂神"/>
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
bean>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.toString();
people.getCat().shout();
people.getDog().shout();
}
<bean id="people" class="com.qia.pojo.People" autowire="byName">
<property name="name" value="狂神"/>
bean>
<bean id="people" class="com.qia.pojo.People" autowire="byType">
<property name="name" value="狂神"/>
bean>
jdk1.5支持的注解,Spring2.5就支持注解了!
要使用注解须知:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
<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>
@Autowired是先通过Type注入,后通过Name注入的
直接在属性上使用即可(就可以不写set方法了)! 也可以在set方式上使用!这两种方式效果一样
使用@Autowired 我们可以不让用编写set方法了 , 前提是你这个自动装配的属性在IOC(Spring) 容器中存在 , 且符合名字 byname !
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
}
<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.qia.pojo.Cat"/>
<bean id="dog" class="com.qia.pojo.Dog"/>
<bean id="people" class="com.qia.pojo.People"/>
beans>
import com.qia.pojo.People;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
People people = context.getBean("people", People.class);
people.toString();
people.getCat().shout();
people.getDog().shout();
}
}
方法一:
字段标记了这个注解,说明这个字段可以为null;
方法二:
说明:false,对象可以为null;true,对象必须存入值,不能为null。
public class People {
//如果显式定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
}
public @interface Autowired {
boolean required() default true;
}
修改bean进行以下测试
<bean id="dog1" class="com.kuang.pojo.Dog"/>
<bean id="dog2" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = “xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
private String name;
//如果显式定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
@Autowired
@Qualifier(value = "dog222")
private Dog dog;
借助了@Qualifier可以指定Bean的名称,@Resource同时指定了byname和bytype,如果两种都没有匹配,那么需要使用到name
@Resource注解
private String name;
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
@Resource 和 @Autowired 的区别:
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入约束,配置注解的支持!
<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>
使用扫描机制,指定要扫描的包,这个包下的注解就会生效
<?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:component-scan base-package="com.qia.pojo"/>
<!--开启注解的支持-->
<context:annotation-config/>
<!--可以二选一-->
</beans>
组件,放在类上,说明这个类被String管理了,就是bean!
等价于 < bean id=“user” class=“com.qia.pojo.User”/>
package com.qia.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于
//@Component 组件
@Component
public class User {
public String name = "兔兔";
}
相当于 < property name=“name” value=“兔兔”/>
package com.qia.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//等价于
//@Component 组件
@Component
public class User {
//相当于
@Value("兔兔")
public String name;
}
所以 简单操作用注解,复杂的用xml配置
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
@Component
@Scope("singleton")
public class User {
//相当于
@Value("兔兔")
public String name;
}
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
(不使用xml后,这些东西还是要去做的,所以Config就来做了)
JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!
package com.qia.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Data
@AllArgsConstructor
@NoArgsConstructor
// 这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器中
// 使用@Component声明的类会被@ComponentScan扫描到,并注册为@Bean,这样配置类里就不需要显式声明@Bean了
// 但是配置类中已经注册了@Bean,这里就可以不用写@Component了
// 二选一
//@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("兔兔")
public void setName(String name) {
this.name = name;
}
}
package com.qia.config;
import com.qia.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
// @SuppressWarnings("all")//忽略全部警告
// 这个也会Spring容器托管,注册到容器中,因为它本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans.xml一样
@Configuration
@ComponentScan("com.qia.pojo")
@Import(QiaConfig2.class)
public class QiaConfig {
// 注册一个bean,就相当于我们之前写的一个bean标签
// 这个方法的名字,就相当于bean标签中id属性
// 这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user(){
return new User(); // 就是返回要注入到bean的对象!
}
}
package com.qia.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QiaConfig2 {
}
import com.qia.config.QiaConfig;
import com.qia.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
//如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
ApplicationContext context = new AnnotationConfigApplicationContext(QiaConfig.class);
User user = context.getBean("user", User.class);
System.out.println(user.getName());
}
}
注册bean的两种方法:二选一即可
这种纯Java的配置方式,在SpringBoot中随处可见!
相当于中介,黄牛等
为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP 和 SpringMVC】
代理模式的分类:
角色分析:
(本来两方能直接做这件事,但现在出现了一个第三方,这个第三方更擅长做这件事,所以两方现在都需要通过这个第三方来完成相对的目的)
(真实角色和代理角色都有相同的共性,因此这个共性就是程序的接口,所以可以这样子理解,房东和中介都要出租房屋,所以提取出租房这个接口)
代码步骤:
package com.qia.demo01;
//租房的接口
public interface Rent {
public void rent();
}
package com.qia.demo01;
//房东主人,继承接口
public class Host implements Rent{
public void rent() {
System.out.println("房东出租房子!");
}
}
package com.qia.demo01;
//中介代理,组合的方式来组合房东,组合优于继承,合成复用原则 //可以理解为中介得有房东的联系方式 //继承是is a的关系,组合是has a的关系
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
host.rent();
seeHouse();
sign();
fee();
}
//看房
public void seeHouse(){
System.out.println("中介带着看房子!");
}
//签合同
public void sign(){
System.out.println("和中介签署租赁合同!");
}
//收费用
public void fee(){
System.out.println("中介收取费用!");
}
}
package com.qia.demo01;
//客户
public class Client {
public static void main(String[] args) {
//要先有卖房的人,然后才有中介,买房的人才有地方买
//房东要出租房子
Host host = new Host();
// host.rent();
//代理,中介帮房东出租房子,并且代理角色一般会有一些附属操作!
Proxy proxy = new Proxy(host);
//不用面对房东,直接找中介租房即可!
proxy.rent();
}
}
代理模式的好处:
缺点:
程序员有一个原则,尽量不去修改原来的代码,所以学会了不断地加一层,加一层
当业务增加或修改
原来
使用再写一个类去继承这个接口,组合原来的类(也实现了这个接口的一个类,这个类负责原来的业务),把新业务的方法去调用组合的类成员
现在
spring建议加set方法
springboot建议加构造器注入
代码:
1、接口
package com.qia.demo02;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
2、原来的业务实现类
package com.qia.demo02;
//真实角色
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("修改了一个用户!");
}
public void query() {
System.out.println("查询了一个用户!");
}
}
3、新增业务,在调用原来的业务的同时,增加日志输出
以组合实现扩展业务
package com.qia.demo02;
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
//日志方法
public void log(String msg){
System.out.println("[Debug] 使用了一个"+msg+"方法");
}
}
开闭原则,对扩展开放,对修改关闭
4、客户端调用业务
package com.qia.demo02;
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
proxy.delete();
}
}
静态代理模式就是在不修改原来代码的基础上实现功能的扩展
拓展:
OOP七大原则
需要了解两个类:
【InvocationHandler:调用处理程序】
处理代理实例上的方法调用并返回结果。
当在与其关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
Object invoke(Object proxy, 方法 method, Object[] args);
//参数
//proxy - 调用该方法的代理实例
//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):
测试:
房东还是房东,还是出租房这件事
代理角色的调用方法
代码步骤:
1、接口
//租房的接口
public interface Rent {
public void rent();
}
2、真实角色
//房东主人,继承接口
public class Host implements Rent {
public void rent() {
System.out.println("房东出租房子!");
}
}
3、代理角色
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
// this.getClass().getClassLoader(), 获得这个类的类加载器
// rent.getClass().getInterfaces(), 获得这个接口的所有实现类 //getClass().getInterfaces() 返回的是一个接口数组
// this 这个类本身
// 代理关系也就组建好了
}
//处理代理实例,并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质,就是使用反射机制实现!
Object result = method.invoke(rent, args);
seeHose();
fee();
return result;
}
// ↓ ==== 中介自己的业务 ====
public void seeHose(){
System.out.println("中介带着看房子!");
}
public void fee(){
System.out.println("中介收取费用!");
}
}
3.1.生成实例化代理角色
组合的接口,我们需要set,然后需要拿到接口本身,去生成代理角色(确定要做的事(这个租房事件的接口))
3.2.处理代理角色业务
看要买谁的房子,可能不只有房东这个类在出租房,可能还有其他的rent接口实现类
然后可以有代理角色本身自己的其他业务
4、客户端
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
//代理角色:现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象!
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy(); //这里的proxy就是动态生成的,我们并没有写
proxy.rent();
//1、房东找中介;2、生成业务了;3、客户端调用出租房业务
/*
房东出租房子!
中介带着看房子!
中介收取费用!
*/
}
}
核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
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 methodName){
System.out.println("执行了"+methodName+"方法");
}
}
我们使用动态代理,来实现一下,动态代理我们写的UserService!
代码:
//接口,这个相当于租房这件事本身
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
//真实角色:相当于房东
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("修改了一个用户!");
}
public void query() {
System.out.println("查询了一个用户!");
}
}
import com.qia.demo03.Rent;
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(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
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 methodName){
System.out.println("执行了"+methodName+"方法");
}
}
//相当于客户端调用
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.delete();//这就是代理对象的invoke方法传的method
//先有了真实对象,代理对象,当真实对象找上代理,业务就准备好了,最后看你要执行哪个业务方法
}
}
invoke方法是实现InvocationHandler这个反射的接口,必须实现的方法
JAVA的反射是指,可以通过一个类名来探察这个类里面的信息,比如说类的属性名,属性名的修饰符,方法名,方法返回值,方法修饰符等等,反正除了方法体得不到,其他都可以用反射得到;反射还可以生成类的实例,通过这个实例定义属性,调用方法,特别是能调用私有的属性和私有的方法。
所以invoke方法可以调用真实对象的所有信息
学习方法:可以用debug走一遍代码
动态代理的好处:
静态代理缺点:
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
静态代理与动态代理区别就是,静态代理只能代理一个接口,而动态代理能代理任意接口,也就意味着可以为任何接口的实现类增添功能
中介不再只可以处理出租房业务(静态),还可以做结婚包办等业务(动态)
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
AOP基于动态代理(动态代理本身的原理就是接口套娃,或者暴力继承)
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
提供声明式事务;允许用户自定义切面
以下名词需要了解下:
横切关注点就是我们想在当前类添加的一些业务功能,切面就是把这些业务抽象成一个类,通知就是类里面的方法
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
(1)前置通知(Before Advice):在连接点(Join point)之前执行的通知。
(2)后置通知(After Advice):当连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
(3)环绕通知(Around Advice):包围一个连接点的通知,这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
(4)返回后通知(AfterReturning Advice):在连接点正常完成后执行的通知(如果连接点抛出异常,则不执行)
(5)抛出异常后通知(AfterThrowing advice):在方法抛出异常退出时执行的通知
例如:
<bean id="bankService" class="com.chenqa.springaop.example.service.impl.BCMBankServiceImpl"/>
<bean id="myAspect" class="com.chenqa.springaop.example.aspect.MyAspect"/>
<aop:config>
<aop:aspect ref="myAspect">
<aop:pointcut expression="execution(* com.chenqa.springaop.example.service.impl.*.*(..))" id="pointcut"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
<aop:around method="around" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
【主要是SpringAPI接口实现】
【重点】使用AOP织入,需要导入一个依赖包!
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
expression表达式的写法规则
表达式例子
execution(public * *(..)):任意公共方法的执行
execution(* set*(..)):任何一个以“set”开始的方法的执行
execution(* com.xyz.service.AccountService.*(..)):AccountService 接口的任意方法的执行
execution(* com.xyz.service.*.*(..)):定义在service包里的任意方法的执行
execution(* com.xyz.service..*.*(..)):定义在service包和所有子包里的任意类的任意方法的执行
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))"):定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行
在多个表达式之间使用 ||,or表示 或,使用 &&,and表示 与,!表示 非
<aop:config>
<aop:pointcut id="pointcut" expression="(execution(* com.ccboy.dao..*.find*(..))) or (execution(* com.ccboy.dao..*.query*(..)))"/>
<aop:advisor advice-ref="jdbcInterceptor" pointcut-ref="pointcut" />
</aop:config>
通配符说明
1、导入依赖
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
2、接口(抽象对象)
//接口
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
3、接口实现类(真实对象)
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户!");
}
public void delete() {
System.out.println("删除了一个用户!");
}
public void update() {
System.out.println("修改了一个用户!");
}
public void query() {
System.out.println("查询了一个用户!");
}
}
4、其他业务(代理对象的其他业务)
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
//method: 要执行的目标对象的方法
//args:参数
//target:目标对象
public void before(Method method, Object[] agrs, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
//returnValue: 返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);
}
}
5、applicationContext.xml(beans.xml)(代理对象)(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="userService" class="com.qia.service.UserServiceImpl"/>
<bean id="log" class="com.qia.log.Log"/>
<bean id="afterLog" class="com.qia.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.qia.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
解析这个表达式
<aop:pointcut id="pointcut" expression="execution(* com.qia.service.UserServiceImpl.*(..))"/>
此处省略了方法访问修饰符;
第一个是返回值类型,此处是*;
第二个是类的全路径名;
第三个是 .方法名,此处是 .*,即全部方法;
第四个(. .)代表所有参数;
省略了抛出异常类型
意思:切入点(也可以理解为插入)在 com.qia.service.UserServiceImpl 这个类下的所有方法
6、测试类,客户端调用
import com.qia.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理 代理的是接口:注意点
UserService userService = (UserService) context.getBean("userService");
userService.add();
// userService.select();
}
}
【主要是切面定义】
1、在diy包下定义自己的DiyPointCut切入类
package com.qia.diy;
public class DiyPointCut {
public void before(){
System.out.println("======方法执行前======");
}
public void after(){
System.out.println("======方法执行后======");
}
}
2、去spring中配置文件
<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="userService" class="com.qia.service.UserServiceImpl"/>
<bean id="diy" class="com.qia.diy.DiyPointCut"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.qia.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
aop:aspect>
aop:config>
beans>
3、测试类
一样
1、在diy包下定义注解实现的AnnotationPointCut增强类
package com.qia.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//声明式事务!
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
@Before("execution(* com.qia.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("====方法执行前====");
}
@After("execution(* com.qia.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("====方法执行后====");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
@Around("execution(* com.qia.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("环绕前");
Signature signature = jp.getSignature();// 获得签名
System.out.println("signature:"+signature);
Object proceed = jp.proceed(); //执行方法
System.out.println("环绕后");
System.out.println(proceed);
}
}
2、在Spring配置文件中,注册bean,并增加支持注解的配置。
<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="userService" class="com.qia.service.UserServiceImpl"/>
<bean id="annotationPointCut" class="com.qia.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
beans>
3、测试
一样
输出结果顺序,优先级别
XML --> DIY --> 环绕 --> 注解 --> 执行方法前 --> 执行方法 --> 执行方法后 --> 注解 --> 环绕 --> DIY --> XML
Spring版本不同,执行顺序不同
区别:JDK稚嫩恶搞代理接口实现类,而cglib需要的是类
<aop:aspectj-autoproxy/>
在不影响真实类方法的前提下(不改动源代码),进行动态的增强,去作横向切面,即横向扩展一些功能(横向开发~)
SpringAop 原理就是动态代理
对于实现接口的目标类使用的是jdk动态代理
对于没有实现任何接口的目标类,使用的是cglib的动态代理
代理类是程序在运行期间由JVM根据反射等机制动态生成的自动生成代理类和代理对象。
所谓动态就是指在程序运行前不存在代理类的字节码文件。
三种配置方式
一:SpringAop 1.x 使用ProxyFactoryBean手动代理
二:SpringAop 2.x 基于命名控件的配置
三:Annotation 基于注解的配置
SpringAop支持五种类型的通知(增强(业务))
注意:多个Advice之间不允许有耦合,即多个Advice之间不允许有业务交叉。
配置方式:
基本用法:
添加jar包
1: 配置增强类(继承Advice相关接口的类,MethodBeforeAdvice等),且注册bean
2: 配置目标类实例(目标对象,真实对象,业务接口实现类),且注册bean
3: 配置advicor,将Advice和pointCut结合 织入的过程
4: 在advicor里,配置切入点PoinCut 指定匹配哪些方法,直接写方法名
5: 在advicor里,指定增强类(通知advice)
6. 配置代理,使用ProxyFactoryBean配置代理
其中
定义切入点,配置位置信息,指定那些类的哪些方法需要被执行Aop。
使用NameMathodPointcutAdvisor根据方法名匹配切入点
Advisor是Pointcut和Advice的配置器,Pointcut+Advice=Advisor
例如:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
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 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userServiceTarger" class="springaop02.service.impl.UserServlceImpl"/>
<bean id="logAdvice" class="springaop02.advice.BeforeAdvice"/>
<bean id="logAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="advice" ref="logAdvice"/>
<property name="mappedNames">
<list>
<value>loginvalue>
list>
property>
bean>
<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="userServiceTarger"/>
<property name="interfaces">
<list>
<value>springaop02.service.UserServicevalue>
list>
property>
<property name="interceptorNames">
<list>
<value>logAdvisorvalue>
list>
property>
bean>
beans>
也可以用我们的项目代码这种方式来配置代理
<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="userService" class="com.qia.service.UserServiceImpl"/>
<bean id="log" class="com.qia.log.Log"/>
<bean id="afterLog" class="com.qia.log.AfterLog"/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.qia.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
beans>
1、导入相关jar包
junit
mybatis
mysql数据库
spring相关
aop织入器
mybatis-spring整合包【new 重点】在此还导入了lombok包。
https://mybatis.org/spring/zh/index.html
配置Maven静态资源过滤问题!
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.8.13version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.7version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
dependencies>
2、编写配置文件
3、测试
1、编写pojo实体类
2、编写实现mybatis的配置文件
3、编写UserMapper接口
4、编写UserMapper.xml文件
5、测试
代码:
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
package com.qia.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
//sqlSessionFactory --> sqlSession
// 读取配置文件把它加载成流!!!
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;//局部变量变成全局变量,提升作用域,方便调用
static{
try {
//使用Mybatis的第一步:获取sqlSessionFactory对象 -- 读取配置文件把它加载成流!!!
String resource = "mybatis-config.xml";//获取资源名称,即配置文件名称
InputStream inputStream = Resources.getResourceAsStream(resource);//把配置文件转成流的形式
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//把配置文件的流形式进行加载、构建
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们就可以从中获得 SqlSession 的实例了。
// SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。
// 即 getSqlSession 这个方法用于 获取SqlSession
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
}
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=utf-8
username=root
password=123456
包含读取properties文件,给包一次性取别名(里面的所有类都默认有首字母小写的别名),mapper注册
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties">properties>
<typeAliases>
<package name="com.qia.pojo"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
dataSource>
environment>
environments>
<mappers>
<package name="com.qia.mapper"/>
mappers>
configuration>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
package com.qia.mapper;
import com.qia.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> selectUser();
}
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qia.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user
select>
mapper>
import com.qia.mapper.UserMapper;
import com.qia.pojo.User;
import com.qia.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
public class MyTest {
@Test
public void selectUser() throws IOException {
// 1.以流的形式加载类得到SqlSession,可以封装成一个工具类
// String resource = "mybatis-config.xml";//获取配置文件的文件名
// InputStream inputStream = Resources.getResourceAsStream(resource);//以流的形式读取类
// SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);//以流的形式加载类获得工厂SqlSessionFactory
// SqlSession sqlSession = sqlSessionFactory.openSession();//从工厂中打开一个sql会话SqlSession
//即下面这个
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 2.从SqlSession中,通过反射,获取将使用的mapper的类的所有信息
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 3.开始操作
List<User> userList = mapper.selectUser();
for (User user: userList){
System.out.println(user);
}
// 4.关闭sqlSession
sqlSession.close();
}
}
什么是MyBatis-Spring?
MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。
它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean 中,以及将 Mybatis 的异常转换为 Spring 的 DataAccessException。 最终,可以做到应用代码不依赖于 MyBatis,Spring 或 MyBatis-Spring。
.
文档链接:http://mybatis.org/spring/zh/index.html
如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.2version>
dependency>
要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类(即DataSource)。
万物皆可bean
配置数据源替换mybaits的数据源
<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=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
pom.xml --> spring-dao.xml
↓
配置SqlSessionFactory,关联MyBatis
<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/qia/mapper/*.xml"/>
bean>
一般会让mybatis-config.xml只做取别名和设置settings 操作
注册sqlSessionTemplate的bean,关联sqlSessionFactory
即注册了SqlSession
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
bean>
UserMapperImpl 实现类,私有化sqlSessionTemplate
public class UserMapperImpl implements UserMapper {
//我们的所有操作,都使用sqlSession来执行,在原来,现在都使用SqlsessionTemplate
private SqlSessionTemplate sqlSession;
//不需要get方法,因为取这个sqlSession,也只会通过bean来取,不会用到get方法
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
要记得注册bean
<bean id="userMapper" class="com.qia.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
bean>
@Test
public void test() throws IOException{
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!
整合别人项目的话,也可以写个总的beans,即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/beans/spring-aop.xsd">
<import resource="spring-dao.xml"/>
beans>
思路:
1、
Spring把所有的东西都变成了一个个的bean,用beans配置文件作用容器装起来,要的时候可以用CPX的上下文来获取。
要用哪个bean就取哪个bean就可以了,bean里的东西、方法都可以用
2、
先把原来的mybatis.xml变成bean,里面的 数据源,sql会话工厂,sql会话(sql会话盘子),都变成了bean。
让mybatis-config.xml只剩下做取别名和设置settings 操作
3、
写个mapper的接口实现类,且注册bean
主要是为了间接把mapper接口及绑定的mapper.xml变成一个bean,现在只需要通过这个实现类就可以执行数据库操作(解耦:mapper.xml实现了数据库sql和java代码的解耦)
spring无法注册接口,只能注册类,所以写实现类是必要的
4、
测试时
获取上下文这个容器
在上下文这个容器里拿bean
拿出bean,就可以调用里面的东西了
结论
Spring要接管所有对象,让他们可以直接创建
这样做业务时就可以简化操作
不用为了在业务层调用数据库层的操作时,要先创建会话工厂,在得到会话,在得到mapper执行对象
现在可以一步到位,所有的东西都是bean,用什么拿什么
JavaMVC的解耦,实现了前端页面,处理前后端请求响应,业务层,数据库层的解耦
mybatis的解耦,实现了数据库层,java代码和sql语句的解耦
Spring-mybatis的解耦,实现了所有的东西都是bean,可以用什么拿什么
mybatis-spring1.2.3版以上的才有这个
dao实现类继承Support类 , 然后直接注入SqlSessionFactory ,利用 getSqlSession() 获得SqlSession , 比起整合方式一 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看。
代码:
1、将我们上面写的UserMapperImpl修改一下
package com.qia.mapper;
import com.qia.pojo.User;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser(){
// SqlSession sqlSession = getSqlSession();
// UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// List userList = mapper.selectUser();
// return userList;
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
2、写完一个类,就马上注册他的bean
<bean id="userMapper2" class="com.qia.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
bean>
3、测试
@Test
public void test2() throws IOException{
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper2", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
两种整合方式差别在sqlSession
一种是私有化 sqlSessionTemplate ,在注册接口实现类为bean的时候将sqlSessionTemplate注入属性,即这个接口实现类在构造的时候这个属性就赋值好了,可以直接用
一种是通过继承 sqlSessionDaoSupport 这个类,SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法
一个是自己手动注入,一个是由父类创建获得
不需要关闭sqlSession,这两种方法都是线程安全的
背后的难是为了表面的简单
事务ACID原则:
测试:
1、将上面的代码拷贝到一个新项目中
导入maven依赖
导入配置文件
spring-dao.xml放三个bean,dataSource,sqlSessionFactory,sqlSession
mybatis-config.xml放别名和日志设置
applicationContext.xml用来导入spring-dao.xml包和注册bean
mapper接口
mapper.xml
mapper实现类
注册mapper实现类的bean
测试类
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
2、在之前的案例中,我们给userMapper接口新增两个方法,删除和增加用户;
//添加一个用户
int addUser(User user);
//根据id删除用户
int deleteUser(int id);
UserMapper文件,我们故意把 deletes 写错,测试是否是事务要么都成功要么都失败!
<insert id="addUser" parameterType="com.qia.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
insert>
<delete id="deleteUser" parameterType="int">
deletes from user where id = #{id}
delete>
编写接口的UserMapperImpl实现类,增加查询业务的内部业务,增加add、delete业务
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
//增加一些操作
public List<User> selectUser() {
User user = new User(5, "小王", "185161");
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(5);
return mapper.selectUser();
}
//新增
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
//删除
public int deleteUser(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
}
}
测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。
声明式事务 : AOP
编程式事务 : 需要在代码中 , 进行事务的管理
声明式事务(交由容器管理事务)
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<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="addUser" propagation="REQUIRED"/>
<tx:method name="deleteUser" propagation="REQUIRED"/>
<tx:method name="selectUser" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
spring事务传播特性:
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.qia.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
aop:config>
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
userMapper.addUser(new User(7, "小王", "987654"));
}
思考:
为什么需要事务?
这一步实现了事务操作和java代码的解耦
配置文件要注意多空格的问题
ioc 控制反转 创建实例获取对象的方式变了(set)
aop 面向切面编程 横向扩展功能
什么是JavaConfig?
Spring JavaConfig是Spring社区的产品,他提供了配置Spring IOC容器的纯Java方法
javaConfig的作用
利用纯java开发在不用添加xml配置文件的情况下使用注解以此达到高效的开发
JavaConfig的优点
1.面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分使用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。
2.减少或者消除XML配置。基于依赖注入原则的外化配置的好处已经被证明。但是,许多开发人员不希望在XML和Java之间来回切换。javaconfig为开发人员提供了一种纯Java的方法来配置与XML配置概念相似的Spring容器。从技术角度来说,只使用javaconfig配置类来配置容器是可行的,但是实际开发中,很多场景都是javaconfig和xml配置共用是最方便,理想的。
3.类型安全和重构友好。javaconfig提供了一种类型安全的方法了来配置spring容器,由于Java5.0对泛型的支持,现在可以按类型而不是名称检索bean,不需要任何的强制转换或者基于字符串的查找。
1、mybatis-config.xml
typeAliases
setting
2、spring-dao.xml
dataSource
sqlSessionFactory
sqlSession(SqlSessionTemplate)
配置声明式事务(接入数据源)
(结合AOP实现事务的织入:通知+切入点)
配置事务通知
配置事务切入
<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/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.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=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
<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/qia/mapper/*.xml"/>
bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
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="addUser" propagation="REQUIRED"/>
<tx:method name="deleteUser" propagation="REQUIRED"/>
<tx:method name="selectUser" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.qia.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
aop:config>
beans>
3、application.xml
import
其他bean
学习xml配置文件以来,逐步实现解耦
在mybatis开始学习xml配置文件,实现数据库sql语言和dao层java代码的解耦
在spring学习xml配置文件,把mvc各层实现解耦,变成一个个的bean
学习java-config,实现注解开发,逐步减少xml配置文件代码,实现完全的纯 java 开发
本项目完整pom.xml
<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-StudyartifactId>
<groupId>com.qiagroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>spring-11-transactionartifactId>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.9.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
project>
<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-StudyartifactId>
<groupId>com.qiagroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>spring-11-transactionartifactId>
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-apiartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-webartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.11version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.7version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.23version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.9.1version>
dependency>
<dependency>
<groupId>commons-langgroupId>
<artifactId>commons-langartifactId>
<version>2.6version>
dependency>
<dependency>
<groupId>commons-dbcpgroupId>
<artifactId>commons-dbcpartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.11.0version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>commons-logginggroupId>
<artifactId>commons-loggingartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>javax.servlet.jspgroupId>
<artifactId>javax.servlet.jsp-apiartifactId>
<version>2.3.3version>
dependency>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>jstlartifactId>
<version>1.2version>
dependency>
<dependency>
<groupId>taglibsgroupId>
<artifactId>standardartifactId>
<version>1.1.2version>
dependency>
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
<version>2.0.1.Finalversion>
dependency>
<dependency>
<groupId>org.hibernate.validatorgroupId>
<artifactId>hibernate-validatorartifactId>
<version>8.0.0.Finalversion>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>2.0.14version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.9.1version>
dependency>
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
<version>2.0.1.Finalversion>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.24version>
dependency>
dependencies>
<build>
<finalName>ssmProjectfinalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-clean-pluginartifactId>
<version>3.1.0version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-resources-pluginartifactId>
<version>3.0.2version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.0version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-surefire-pluginartifactId>
<version>2.22.1version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-war-pluginartifactId>
<version>3.2.2version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-install-pluginartifactId>
<version>2.5.2version>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-deploy-pluginartifactId>
<version>2.8.2version>
plugin>
plugins>
pluginManagement>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>truefiltering>
resource>
resources>
build>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
project>