1、Spring
文章目录
简介:
spring:春天–> 给软件行业带来春天!
官网: https://spring.io
前情提要
导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
</dependency>
新建maven项目
2、IOC原型
老版
-
一个DAO层接口(
UserDao
) ,有三个实现类- 实现类(
UserDaoImplByMysql
) - 实现类(
UserDaoImplBySqlServer
) - 实现类(
UserDaoImplByOracle
)
- 实现类(
-
一个业务层接口(
UserService
)-
实现类(
UserServiceImpl
)—> 调用dao层,而调用哪个dao的实现类呢?用户今天说用mysql 的吧,明天又说用Oracle的吧,那么程序员每次都在这个实现类中修改dao的实现类
-
上面就会有很强的耦合性,不够灵活
于是有了IOC 的思想,这个实现类由用户 选择。
在业务层的实现类中,属性私有化,提供set方法 或者 构造函数中实例化dao实现类
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
测试
用那种方式去实现由用户决定
public class TestUserDao {
@Test
public void testUserDao(){
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDaoImplByOracle());
userService.getData();
}
}
- 之前,程序是写死的实现类,控制权就在程序员手中
- 现在,set注入后,程序不再主动,而是被动接受对象!
等等… 这种思想很多地方都见过,比如网页换个色调(白天,夜晚),
程序员只要写好 方案,供你选择
IOC 本质
控制反转IOC (Inversion of Control )是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI 只是IOC的另一种说法,。没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建有程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
新版
代码中只需要从业务实现类中 获取bean ,至于获取的是哪个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">
<!-- name:给bean起名字,下面代码中好获取-->
<!-- class : 全限定名类-->
<bean name="mysql" class="pojo.UserDaoImplByMysql"/>
<bean name="oracle" class="pojo.UserDaoImplByOracle"/>
<bean name="sqlserver" class="pojo.UserDaoImplBySqlServer"/>
<bean name="userService" class="service.UserServiceImpl">
<!-- property : 属性(set注入) name:属性名 ref(非基本类型):给属性赋值-->
<property name="userDao" ref="oracle"/> <!--这里上面已经给注册了,直接用,否则用全限定名-->
</bean>
</beans>
@Test
public void testUserDao(){
// 获取spring 上下文
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 上下文中获取bean 哪个bean ,后面的参数,就是配置文件bean 中的name
UserService userService = (UserService) context.getBean("userService");
userService.getData();
}
3、HelloSpring
无参
pojo
package pojo;
public class Hello {
private String str;
public void setStr(String str) { // ioc 依靠set注入
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
applicationContext.xml
注册pojo
<?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="hello" class="pojo.Hello">
<property name="str" value="spring"/> <!-- 里面的属性依靠set 注入,如果没有set方法,报错-->
</bean>
</beans>
测试
@Test
public void testHello(){
// 获取spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 我的的对象都在spring中管理了, 我们要用去里面取出来就可
Hello hello =(Hello) context.getBean("hello");
System.out.println(hello);
}
4、spring 配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aVFfNoxo-1630638870829)(D:\install\typora\images\image-20210819173608038.png)]
bean
alias
beans
description
import
alias
<!-- 好像很鸡肋的样子-->
<alias name="user" alias="xiaoMing"/>
bean
<!--
id : bean的唯一标识符
class : bean 对象对应的全限定名:包名+类名
name : 别名,可以取多个别名
scope : 默认单例
-->
<bean id="user" class="pojo.User" name="小明,XiaoMing">
<property name="name" value="xiaoMing"/>
<property name="password" value="123456"/>
</bean>
import
<import resource="bean1.xml"/>
<import resource="bean2.xml"/>
5、DI(依赖注入)
- 依赖:bean对象的创建依赖容器
- 注入:bean对象中的所有属性,由容器注入
构造器注入
public class User {
private String name;
public User(String name) { // 写了有参构造,那么默认无参构造就不存在了
this.name = name;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="pojo.Hello">
<property name="str" value="spring"/>
</bean>
<bean id="user" class="pojo.User">
<!-- 三种方式,可以混着用,但是不能对同一个属性赋值多次(idea也会提示报错),-->
<!-- 给有参构造器传递参数,name:参数名 value:值 这是最推荐的做法-->
<constructor-arg name="name" value="哈哈"/>
<!--索引(下标) 的方式赋值-->
<constructor-arg index="0" value="嘻嘻"/>
<!--参数类型的方式赋值,如果多个参数一样,就GG了, 所以不推荐-->
<constructor-arg type="java.lang.String" value="呵呵"/>
</bean>
</beans>
set注入
public class Student { // 这个实体类包含的很多种属性,在bean中一个个去实现属性值注入
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
}
<?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="pojo.Address">
<property name="address" value="湖南"/>
</bean>
<!-- student 各种属性 注入的方式-->
<bean id="student" class="pojo.Student">
......
......
</bean>
</beans>
普通值
<!-- 普通值注入 ,value-->
<property name="name" value="哈哈"/>
对象
<!-- bean注入 ,ref-->
<property name="address" ref="address"/>
数组
<!-- 数组注入 ,array-->
<property name="books">
<array>
<value>红楼梦</value>
<value>三国演义</value>
<value>水浒传</value>
<value>西游记</value>
</array>
</property>
list
<!-- list注入 ,list-->
<property name="hobbys" >
<list>
<value>敲代码</value>
<value>敲spring代码</value>
<value>敲Mybatis代码</value>
</list>
</property>
map
<!-- map注入 ,map-entry -->
<property name="card">
<map>
<entry key="身份证" value="123456"/>
<entry key="电话" value="12345678910"/>
</map>
</property>
set
<!-- set注入 set 里面也可以包含很多的,上面那些也可以包含其他类型,无线套娃-->
<property name="games">
<set>
<value>嘻嘻</value>
</set>
</property>
properties
<!-- properties注入 props-->
<property name="info">
<props>
<prop key="url">jdbc:mysql://</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
null
<!-- null 值注入 null-->
<property name="wife">
<null/>
</property>
注入的扩展方式
p命名空间 和 c命名空间的方式
p命名空间注入
property
注入属性的值
导入约束
xmlns:p="http://www.springframework.org/schema/p"
<bean id="user" class="pojo.User" p:name="哈哈" p:password="123456"/>
对比之前
<bean id="user" class="pojo.User">
<property name="name" value="哈哈"/>
<property name="password" value="123456"/>
</bean>
c命名空间注入
construct
对构造器参数注入
导入约束
xmlns:c="http://www.springframework.org/schema/c"
<bean id="user2" class="pojo.User" c:name="嘻嘻" c:password="123456"/>
对比之前
<bean id="user" class="pojo.User">
<constructor-arg name="name" value="哈哈"/>
</bean>
前提:有 有参数的构造方法,
二者无非就是更方便了 一丢丢,
bean的作用域
1.单例模式:(Spring默认)同一个对象
<bean id="user" class="pojo.User" scope="singleton"/>
2.原型模式:每次从容器中的获取的都是不同的对象
<bean id="user" class="pojo.User" scope="prototype"/>
其余的requset
, session
, application
要在web中才能使用到!
6、bean的自动装配
- 自动装配是spring满足bean依赖的一种方式!
- Spring会在上下文中自动寻找,并自动给bean装配属性!
spring的三种装配方式
- 在xml中显示的配置
- 在java中显示的配置
- 隐式的自动装配【重要】
byName自动装配
根据名字自动装配
根据的是set方法public void setDog(Dog d)
的set后缀的Dog
去匹配名字,如果能匹配左侧会出现spring的小叶子的。
<bean id="dog" class="pojo.Dog"/>
<bean id="cat" class="pojo.Cat"/>
// 以前的写法
<bean id="people" class="pojo.People">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
</bean>
// 自动装配的写法
<bean id="people" class="pojo.People" autowire="byName">
经过测试:匹配的是 bean 中的id 和 set方法后缀的名字
bean中的dog 要小写 pojo中的set后缀大小写都可(一般大写,再一般都是自动生成) 且单词要一致
byType自动装配
根据类型自动装配,
//ByType 自动装配的写法
<bean id="people" class="pojo.People" autowire="byType">
使用注解自动装配
jdk1.5支持的注解 spring2.5就注解
1、@Autowired
使用前提:
导入约束:context
配置注解支持:<context:annotation-config/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="dog" class="pojo.Dog"/>
<bean id="cat" class="pojo.Cat"/>
<bean id="people" class="pojo.People">
</bean>
</beans>
2.实体类自动装配
public class People {
@Autowired
private Dog d;
@Autowired
private Cat c;
}
@Autowired
- 直接在属性上使用,也可以在set方法上使用
- 都可以不用set方法了,
- 默认按类型,可以通过
@Qualifier("xxx")
指定名字匹配 - 前提:自动装配的属性在 IOC(spring)容器中
- 可以加参数
@Autowired(required = true)
可以为空
当类型没匹配上,可以配合使用@Qualifier
注解,value = "bean中的唯一id"
@Autowired
@Qualifier(value = "dog2")
private Dog d;
<bean id="dog" class="pojo.Dog"/>
<bean id="dog2" class="pojo.Dog"/> <!--此时有多个相同类型,单独使用Autowired报错,配合qualifier指定名字-->
<bean id="cat" class="pojo.Cat"/>
2、@Resource
用法和@Autowired
一样
可以直接在后面加name
属性,按byName匹配
public class People {
@Autowired
private Dog d;
@Resource(name = "cat")
private Cat c;
}
相同点:
- 都可以作用在属性,或者 set方法上
- 都是默认按
byType
匹配,没匹配成功都不会自动按名字,都需要指定名字去匹配
不同点:
- 使用spring的
@Autowired
左侧会有小叶子图标,代表能匹配,否则编辑器会提示错误
7、使用注解开发
在Spring4 之后,要使用注解开发, 必须要保证aop的包导入了
如使用我上面给的依赖,会自动附带aop的包一起导入的。
@Component
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="pojo"/>
<context:component-scan base-package="dao"/>
<context:component-scan base-package="service"/>
<context:annotation-config/>
</beans>
// 等价于 :<bean id="user" class="pojo.User"/> Component:组件
@Component
public class User {
}
衍生注解
- dao 【
@Repository
】 - service 【
@Service
】 - controller【
Controller
】
意义都和 @Component
(作用在pojo层)一样,注册bean 在IOC容器中。只不过用不同的名字标注更直观。
// 等价于 :
<bean id="user" class="pojo.User"/>
<bean id="dao" class="dao.UserDao"/>
<bean id="service" class="service.UserService"/>
<bean id="controller" class="controller.UserController"/>
Component:组件
@Value
属性值注入(说人话:给对象赋值)
public class User {
@Value("哈哈2")
public String name;
}
@Scope
作用域
// 原型 singleton:单例
@Scope("prototype")
public class User {
public String name="哈哈";
}
小结:
xml与注解的最佳实践:
- xml 管理bean
- 注解只负责属性的注入
8、使用java的方式配置spring
@Configuration
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
@Test
public void test(){
// 既然用的是java注解的方式配置的,就要用注解配置上下(AnnotationConfigApplicationContext)文去加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
User user = (User)context.getBean("getUser");
System.out.println(user);
}
}
@Configuration
声明当前类是个配置类
@Configuration
public class MyConfig {
}
相当于之前的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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
@Bean
这个方法是个bean
@Configuration
public class MyConfig {
@Bean
public User getUser(){
return new User();
}
}
相当于之前的bean
方法名是 bean的id 返回类型就是这个class的类型
<bean id="getUser" class="pojo.User"/>
User user = context.getBean("user");// 之前在这里getBean 能返回一个对象
@Import
@Import(MyConfig2.class)
public class MyConfig {}
使用这种纯java的配置方式,在SpringBoot 中随处可见!!
9、代理模式
静态代理
四种角色:
- 接口(抽象的租房):代理角色 和 被代理角色 实现同一个接口,
- 代理角色(中介):实现接口,调用代理角色需要被代理的功能(接口的功能),顺便做些附属的操作(看房子,签合同,收中介费)
- 被代理角色(房东):实现接口中 需要 被代理的功能(租房),(只要安心的出租房子就可,其他事情不用管,交给中介)
- 真实角色(你,租房的人):你找不到房东,你找中介去租房。
接口:
// 租房接口
public interface Rent {
void rent(); // 租房方法
}
代理角色(中介)
// 代理角色 实现租房功能
public class Proxy implements Rent{
private Host host;
public Proxy() {
host = new Host();
}
public Proxy(Host host) {
this.host = host;
}
@Override
public void rent() {
seeHouse(); // 中介带你看房子
host.rent(); // 调用房东的租房
hetong(); // 中介和你签租赁合同
}
public void seeHouse(){ System.out.println("中介带你看房子");}
public void hetong(){ System.out.println("签租赁合同");}
}
被代理角色:房东
// 房东 实现租房这个功能
public class Host implements Rent{
@Override
public void rent() {
System.out.println("我是房东,我要出租房");
}
}
客户:需要租房的人
// 客户 需要租房的人
public class Client {
public static void main(String[] args) {
Proxy proxy = new Proxy(); // 客户找到代理商 中介 new一个中介
proxy.rent(); // 让中介租房
}
}
动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口----JDK动态代理【现在使用】
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy(代理) ,InvocationHandle(调用处理程序)
// 租房接口
public interface Rent {
void rent(); // 租房方法
}
// 房东 实现租房这个功能
public class Host implements Rent {
@Override
public void rent() {
System.out.println("我是房东,我要出租房");
}
}
package demo2;
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);
}
// 处理代理实例 并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(target, args);
fare();
return result;
}
private void seeHouse(){
System.out.println("中介带你看房子");
}
private void fare(){
System.out.println("中介收你的中介费了");
}
}
package demo2;
public class Client {
public static void main(String[] args) {
// 真实角色
Host host = new Host();
// 代理角色 :动态生成
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(host);
Rent proxy = (Rent) pih.getProxy();// 这里的proxy 就是动态生成的
proxy.rent();
}
}
10、AOP
什么是AOP?
AOP(Aspect Oriented Programming):面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP在spring中的作用
提供声明式事务,允许用户自定义切面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DGbLNB89-1630638870833)(D:\install\typora\images\image-20210821113651959.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bEuKRizY-1630638870834)(D:\install\typora\images\image-20210821113853481.png)]
spring实现AOP【重点】
使用AOP织入,需要导入一个依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
方式一:使用spring的接口
接口
public interface UserService {
void add();
void delete();
}
实现类
@Component
public class UserServiceImpl implements UserService{
@Override
public void add() {
// sout("进入了add方法")
System.out.println("增加了一个用户");
}
@Override
public void delete() {
// sout("进入了delete方法")
System.out.println("删除了一个用户");
}
}
在这里想做一个日志输出的功能,输出调用了哪个方法。(类似注释的代码的功能)
但是如果这个实现类中有很多方法,每次都要去里面加输出语句很麻烦,而且有种修改了原始的代码的感觉,这种感觉非常不好,
所以在这里实现AOP(切面编程)
不修改源代码,只是增加日志输出功能.
日志类 实现 前置通知 接口
@Component
public class BeforeLog implements MethodBeforeAdvice {// 实现aop包中的前置切入
/*
* method: 要执行目标对象的方法
* objects : 参数
* o:目标对象
* */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
// 这里就是需要打印的日志信息
/*
o.getClass().getName() : 反射回来的类的名字(类名)
method.getName() : 代理执行的方法的名字(方法名)
*/
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
后置日志 :作用和前置 一样,只是一个在代理执行方法的前面,一个在后面
@Component
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,返回的结果为:"+returnValue);
}
}
applicationcontext
aop 配置
记得添加约束aop
<?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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 上面使用了@Component 注解,自动完成bean的注册,这里只需要扫描-->
<!-- 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="log"/>
<context:component-scan base-package="service"/>
<context:annotation-config/>
<!-- 配置aop-->
<aop:config>
<!-- 切入点 : expression:表达式 execution(要执行的位置)
要执行的位置:(* 类名.方法名(参数)) 方法名可以用*号省略, 参数用 .. 省略
切入到UserServiceImpl 下的所有方法
-->
<aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
方式二:自定义类实现AOP
自定义要切入的类
// 自定义的日志类
@Component
public class DiyLog {
public void before(){
System.out.println("==========执行前============");
}
public void after(){
System.out.println("==========执行后============");
}
}
aop配置
<?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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="log"/>
<context:component-scan base-package="service"/>
<context:annotation-config/>
<!--aop 配置-->
<aop:config>
<!--aop aspect:切面 ref 要切入的类-->
<aop:aspect ref="diyLog">
<!--aop pointcut:切入点 id:切入点的标识 expression: 要切入的位置-->
<aop:pointcut id="point" expression="execution(* service.*.*(..))"/>
<!--aop before :切入点前 method :要切入的方法 pointcut-ref:从哪个点切入-->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
</beans>
方式三:使用注解实现
自定义的类
package log;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AnnotationLog {
@Before("execution(* service.*.*(..))")
public void before(){
System.out.println("之前...");
}
@After("execution(* service.*.*(..))")
public void after(){
System.out.println("之后...");
}
}
aop 配置
<?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"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="log"/>
<context:component-scan base-package="service"/>
<context:annotation-config/>
<!-- aop 切面 自动代理 -->
<aop:aspectj-autoproxy/>
</beans>
11、整合Mybatis
需要的依赖
<?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.xhb</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-10-mybatis</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- spring 操作数据库的话,要spring-jdbc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
</project>
回忆Mybatis
- 编写实体类
- 编写接口
- 编写核心配置文件
- 编写mapper.xml
- 测试
Mybatis-spring
- 编写数据源配置
- sqlSessionFactory
- sqlSessionTemplate
- 给接口加实现类
- 实现类注册到spring中
spring-dao.xml
先来个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 ">
</beans>
dataSource(数据源)
之前连接数据库是用db.properties
然后在mybatis-config.xml
中 引入资源
DriverManagerDataSource 类
<!--DataSource : 使用spring的数据源 替换Mybatis的配置 数据源:c3p0 dbcp druid
我们这里使用spring提供的jdbc-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&userUnicode=true&charterEncoding=utf8&serverTimezone=UTC"/>
<property name="username" value="TR"/>
<property name="password" value="adminxhb"/>
</bean>
sqlSessionFactory
SqlSessionFactoryBean类 是mybatis.spring
包的 ,需要mybatis-spring
依赖
之前的sqlSessionFactory
在java 代码中
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);//加载资源流的方式加载Mybatis配置文件
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
现在 可以摒弃mybatis-config.xml
配置文件了,
<!-- sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定Mybatis配置文件
configuration : 引入配置文件 (当然Mybatis 的所有都能在spring中配置)
mapperLocations : 以前Mybatis中的mapper注册 ( <mappers> <mapper resource="mapper/UserMapper.xml"/></mappers>)
...
还有许多Mybatis的配置,别名(typeAlias),设置(Settings) 等等..... 都能在这里做
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/UserMapper.xml"/>
</bean>
SqlSessionTemplate
SqlSessionTemplate 无缝衔接之前的 SqlSession , 他两是同一个( Template 是模板的意思 。明白了吧)
<!-- SqlSessionTemplate 就是我们要使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 通过构造方法 注入参数sqlSessionFactory -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
回顾之前的sqlSession怎么获取
sqlSessionFactory.openSession();
完整代码
测试
public class TestMybatis {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("`applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.getUsers()) {
System.out.println(user);
}
}
}
spring-dao.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">
<!--DataSource : 使用spring的数据源 替换Mybatis的配置 c3p0 dbcp druid
我们这里使用spring提供的jdbc
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&userUnicode=true&charterEncoding=utf8&serverTimezone=UTC"/>
<property name="username" value="TR"/>
<property name="password" value="adminxhb"/>
</bean>
<!-- sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定Mybatis配置文件
configuration : 引入配置文件 (当然Mybatis 的所有都能在spring中配置)
mapperLocations : 以前Mybatis中的mapper注册
...
还有许多Mybatis的配置,别名(typeAlias),设置(Settings) 等等..... 都能在这里做
-->
<!-- 这里还引入了Mybatis的核心配置文件,只是为了证明他的存在感,其实可以完全用spring代替-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:mapper/UserMapper.xml"/>
</bean>
<!-- SqlSessionTemplate 就是我们要使用的sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 通过构造方法 注入参数sqlSessionFactory -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
applicationContext.xml
导入spring-dao.xml
它专心做它的事情(数据源,sqlSessionFactory sqlSessionTemplate)。这里导入即可
<?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="spring-dao.xml"/>
<!-- 注册bean -->
<bean id="userMapper" class="mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
接口
public interface UserMapper {
public List<User> getUsers(); // 获取所有用户
}
实现类
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUsers() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUsers();
}
}
之前Mybatis 是没有实现类的,这里有的原因是因为 之前的sqlSession 是在java工具类代码中创建的,现在在spring中获取,接口无法注入sqlSession对象,所以用了个实现类注入sqlSession对象
当然也可以不用实现类,直接获取sqlSession 也可以,那么测试代码就要改为
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
// UserMapper mapper = context.getBean("userMapper", UserMapper.class);
SqlSessionTemplate sqlSession = context.getBean("sqlSession", SqlSessionTemplate.class);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
for (User user : mapper.getUsers()) {
System.out.println(user);
}
就这么个区别,注释的是spring的写法,6,7行是之前Mybatis的写法(获取sqlSession 做事情)
区别不是很大,但是思想有些变化。 spring 写法的对象全是从spring 中获取的, 更加解耦。而Mybatis写法获取sqlSession,创建的mapper 是自己创建的,耦合性大一丢丢。
pojo
@Data
public class User {
private int id;
private String name;
private String pwd;
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from mybatis.user
</select>
</mapper>
扩展方式(SqlSessionDaoSupport)
继承 SqlSessionDaoSupport ,可以使用getSqlSession()
获取sqlSession 对象,不用通过spring注入,但是需要给SqlSessionDaoSupport
注入sqlSessionTemplate
实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> getUsers() {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class); //父类方法getSqlSession()
return mapper.getUsers();
}
}
<!-- 注册bean -->
<bean id="userMapper" class="mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSession"/>
</bean>
原理都是一样的,不一样的只是sqlSession
这个对象注入到``UserMapperImpl还是注入到
SqlSessionDaoSupport`
12、声明式事务
事务:要么都成功,要么都失败
ACID原则:
- 原子性
- 一致性
- 隔离性
- 持久性
声明式事务:AOP
编程式事务:需要在代码中,进行事务的管理
@Override
public int updateUser(User user) {
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.deleteUser(user.getId());
System.out.println(1/0);
mapper.addUser(user);
return 0;
}
这个修改用户功能:先删除原来的用户,再添加 (当然,实际开发不会这么做,为了举例)
看的出里面有个1/0 的异常,那么如果没事务的话,用户删除了,异常,后面的添加就执行不了了。
所以使用spring的声明式事务:
<!-- 配置声明事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 事务-->
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="addUser"/>
<tx:method name="deleteUser"/>
<tx:method name="updateUser"/>
</tx:attributes>
</tx:advice>
<!-- aop织入 事务-->
<aop:config>
<aop:pointcut id="userPointcut" expression="execution(* mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="userPointcut"/>
</aop:config>
还有 tx 的约束要加。。