狂神说spring
1.spring
1.1 简介
-
spring:春天
-
2002年,首次推出了spring框架的雏形: interface21框架
-
spring框架以inferface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版
-
spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
-
ssh:struct2+spring+hibernate
-
SSM:springmvc+spring+mybatis
GitHub:https://github.com/spring-projects/spring-framework
spring maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- 整合mybatis所需要的包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.2 优点
- spring是一个开源的免费的框架
- 轻量级的非入侵式的框架
- 控制反转(IOC)和面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
spirng就是一个轻量级的控制反转(IOC)和面向切面编程的框架(AOP)
1.3 组成
七大模块
1.4 拓展
现在的java开发,说白了就是基于java开发
- springboot
- 一个快速开发的框架
- 基于springboot可以快速开发单个微服务
- 约定大于配置
- springcloud
- springcloud是基于springboot实现的
- springsecurity
- 安全控制
现在大多数公司都在使用springboot进行快速开发,学习springboot的前提是完全掌握spring和springmvc!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐.
2.IOC理论
- 原来的过程
- 接口
- 实现类
- 业务接口
- 业务实现类
private UserDao userDao = new UserDaoImpl();
UserDao每多一个功能实现类,我们都需要调整底层code
在之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求改源代码!如果程序代码量很大,成本会十分昂贵!
我们使用一个set接口实现(已经发生了革命性的变化)
public void setUserDao(UserDao userdao) {
this.userDao = userdao;
}
- 之前程序是主动创建对象!控制权在程序员手上!
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象.
- 这就是控制反转
这种思想从本质上解决了问题,程序员不用再去管理对象的创建了.系统的耦合性大大降低了,可以更加专注的在业务的实现上.这是IOC的原型
之前和使用set注入之后
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iSJ3R8in-1632825335663)(狂神说spring.assets/image-20210918153443536.png)]
2.1 IOC本质
IOC是一种设计思想,DI是IOC的一种实现方式
所谓控制反转,就是**获得依赖对象的方式反转了**
IOC是spring框架的核心内容
控制反转是一种通过描述(XML或者注解),并通过第三方去生产获取特定对象的方式.在spring中实现控制反转的是IOC容器,其实现方式是依赖注入(Dependecy Injection,DI)
3.HelloSpring
3.1 测试样例
- pojo
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hello {
private String name;
}
- beans.xml
<?xml version="1.0" encoding="UTF8"?>
<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">
<!--
使用spirng来创建,在spring中这些都成为bean
类型 变量名 = new 类型();
Hello hello = new Hello();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值
赋值的两种参数
value:一般类型和string类型
ref:引用类型
-->
<bean id="hello" class="pojo.Hello">
<property name="name" value="spring"></property>
</bean>
</beans>
- test
@Test
public void test01(){
/*
* 获取spring的上下文对象
*/
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
/*
* 我们的对象现在都在spring中管理了,我们要使用,直接从里面获取就可以了
* */
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);
}
问题
- hello对象是谁创建的?
spring
- hello对象的属性是怎么设置的
spring容器设置的
这个过程就叫控制反转
- 控制:传统应用程序的对象是由程序本身创建的,使用spring之后,对象是spring创建的
- 反转:程序本身不创建对象,变成了被动接受对象
- 依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接收.
所谓IOC:对象由spring来创建,管理,装配.
4.IOC创建对象的方式
-
使用无参构造方法创建对象,默认!
-
假设我们使用有参来构造
- 下标赋值
<bean class="pojo.User" id="User"> <!--<property name="name" value="kuquan"></property>--> <!--有参构造,下标赋值--> <constructor-arg index="0" value="kuquan"></constructor-arg> </bean>
- 类型赋值
<bean class="pojo.User" id="User"> <!--有参构造,通过类型创建,不推荐--> <constructor-arg type="java.lang.String" value="kuquan"></constructor-arg> </bean>
- 直接通过参数名来设置
<bean class="pojo.User" id="User"> <!--有参构造,参数名获取--> <constructor-arg name="name" value="kuquan"></constructor-arg> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经实例化了.
5.spring配置
5.1 别名
<bean class="pojo.User" id="User">
<constructor-arg name="name" value="kuquan"></constructor-arg>
</bean>
<alias name="User" alias="test"></alias>
通过bean本来的名字和别名都能获取到对象
<alias name="User" alias="test"></alias>
alias中的name取注册过的bean的id
5.2 Bean配置
<bean class="pojo.User" id="User" name="user">
<constructor-arg name="name" value="kuquan"></constructor-arg>
</bean>
- id:bean的唯一标识符,也就是相当于对象名
- class:bean对象所对应的全限定名
- name:起别名,而且同时取多个别名(多个别名时可以用 逗号or空格or分号 分割)
5.3 import
一般用于团队开发使用,它可以将多个配置文件,导入合并为一个
假设现在项目中由多人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的beans.xml合并为一个总的!
<import resource="beans1.xml"></import>
<import resource="beans2.xml"></import>
<import resource="beans3.xml"></import>
使用的时候直接使用总的配置文件就可以了
6.依赖注入
6.1 构造器注入
前面说过了
<bean class="pojo.User" id="User">
<!--有参构造,参数名获取-->
<constructor-arg name="name" value="kuquan"></constructor-arg>
</bean>
6.2 Set方式注入[重点]
- 依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象种的所有属性,由容器来注入!
[环境搭建]
- 复杂类型
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String address;
}
- 真实测试对象
package pojo;
import com.sun.xml.internal.ws.api.ha.StickyFeature;
import javafx.scene.effect.SepiaTone;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
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;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String, String> card;
private Set<String> games;
private Properties info;
private String wife;
}
- beans.xml
<?xml version="1.0" encoding="UTF8"?>
<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">
</beans>
- 完善注入信息
<?xml version="1.0" encoding="UTF8"?>
<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="pojo.Address">
<property name="address" value="洛阳"></property>
</bean>
<bean id="Student" class="pojo.Student">
<!--第一种:普通值注入,直接使用value赋值-->
<property name="name" value="kuquan"></property>
<!--第二种:bean注入,使用ref-->
<property name="address" ref="address"></property>
<!--数组注入-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!--list注入-->
<property name="hobbys">
<list>
<value>睡觉</value>
<value>写代码</value>
<value>看电影</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="身份证" value="123"></entry>
<entry key="银行卡" value="123"></entry>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>tokens</value>
<value>COC</value>
</set>
</property>
<!--空值注入-->
<property name="wife">
<null></null>
</property>
<!--properties-->
<property name="info">
<props>
<prop key="class">1</prop>
<prop key="grade">3</prop>
</props>
</property>
</bean>
</beans>
- 测试类
package demo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Address;
import pojo.Student;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("Student");
/* Address address = (Address) context.getBean("address");
System.out.println(address);*/
System.out.println(student);
}
}
- 结果
6.3 拓展方式注入
不能直接使用,需要导入然后再进行使用
- p命名空间注入
<?xml version="1.0" encoding="UTF8"?>
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的指,只适用于简单的值-->
<bean id="user" class="pojo.User" p:name="kuquan">
</bean>
</beans>
- c命名空间注入
<?xml version="1.0" encoding="UTF8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!--c命名空间注入,主要是简化构造器注入,construct-->
<bean id="user" class="pojo.User" c:name="余生"></bean>
</beans>
6.4 bean的作用域
Scope | Description |
---|---|
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
prototype | Scopes a single bean definition to any number of object instances. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext . |
session | Scopes a single bean definition to the lifecycle of an HTTP Session . Only valid in the context of a web-aware Spring ApplicationContext . |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket . Only valid in the context of a web-aware Spring ApplicationContext . |
- 单例模式,默认就是单例模式
- 原型模式:每次从容器中get的时候都会产生一个新对象
<bean id="user" class="pojo.User" c:name="余生" scope="prototype"></bean>
- 其余的request,session等,这些只能在web开发中用到.
7.bean的自动装配
- 自动装配bean是spring满足bean依赖的一种方式
- spring会在上下文中自动寻找,并自动给bean装配属性!
在spring中三种装配的方式
- 在xml中显示配置
- 在java中显示配置
- 隐式的自动装配bean[重要]
7.1 测试
- 环境搭建(一个人有两只宠物)
7.2 ByName自动装配
<!--
byname:会自动在容器上下文中寻找和自己set方法中值对应的beanid
-->
<bean id="people" class="pojo.People" autowire="byName">
<property name="name" value="余生"></property>
</bean>
byname的时候,需要保证所有bean的id唯一,并且这个bean的id需要和自动注入bean的属性的set方法中名称一致
7.3 ByType自动装配
<bean id="cat" class="pojo.Cat"></bean>
<bean id="dog" class="pojo.Dog"></bean>
<!--
byname:会自动在容器上下文中寻找和自己set方法中值对应的beanid
bytype:会自动在容器上下文中寻找和自己对象属性类型相同的bean
-->
<bean id="people" class="pojo.People" autowire="byType">
<property name="name" value="余生"></property>
</bean>
bytype的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入bean的属性的类型一致
7.4 使用注解实现自动装配
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.
要用注解,须知
- 导入约束 context约束
xmlns:context="http://www.springframework.org/schema/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
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
- 直接在属性上使用即可
- 可以在set方法上用
- 使用autowired我们可以不用编写set方法,前提是自动装配的属性在ioc容器中存在,且符合名字byname
@Nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired {
boolean required() default true;
}
@Autowired(required = false)
说明这个对象可以为null,否则不能为null
@Qualifier(value = "xxx")
当属性在ioc容器中存在多个的时候,可以用该注解来指定注入的bean,xxx为对应bean的id
@Resource注解
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
@Resource(name = "cat")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
小结:@Resource和 @Autowired的区别
- 都是用来自动装配的,都可以放在字段上
- @Autowired先通过bytype方式来实现,然后通过byname,而且必须要求对象存在
- @Resource默认通过byname来实现,如果找不到名字,则通过bytype来实现!如果都找不到就报错
8.使用注解开发
在spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入context包,增加注解的支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
</beans>
8.1 bean
8.2 属性注入
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
// 相当于property赋值
@Value("kuquan")
private String name;
}
/*
* 等价于:<bean id="user" class="pojo.User"></bean>
* @Component:组件
* */
8.3 衍生的注解
@Component有几个衍生注解,在web开发中,会按照mvc三层架构分层
- dao [@Repository]
- service [@Service]
- controller [@Controller]
这四个注解功能是一样的,都是把某个类注册到spring容器中,装配bean
8.4 自动装配
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
@Resource(name = "cat")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
private String name;
}
8.5 作用域
@Scope("prototype")
public class User {
// 相当于property赋值
@Value("kuquan")
private String name;
}
8.6 小结
xml与注解
- xml更加万能,适用于任何场合!维护简单方便
- 注解,不是自己的类用不了,维护相对复杂!
xml与注解最佳实践
- xml用来管理bean
- 注解只负责属性的注入
- 我们在使用的过程中,只需要注意一个问题,我们必须要注解生效,需要扫描包
<!--扫描指定的包,包下的注解就会生效-->
<context:component-scan base-package="pojo"></context:component-scan>
<context:annotation-config></context:annotation-config>
9.使用java方式配置spring
完全不使用spring的xml配置,全权交给java来做
javaConfig是spring的一个子项目,在spring4之后成为了一个核心功能
实体类
package pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {
@Value("kuquan")
private String name;
}
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;
import pojo.User;
/*
* 这个也会别spring容器托管,注册到容器里,因为他本身就有一个@Component
* @Configuration表示这是一个配置类,就和我们之前看到的beans.xml一样
* @Import(xxx.class)导入其他的配置类
* */
@Configuration
@Import(xxx.class)
public class AppConfig {
/*
* 注册一个bean,就相当于我们之前写的一个bean标签
* 这个方法的名字,就相当于bean标签中的id
* 这个方法的返回值,就相当于bean标签中的class属性
* */
@Bean
public User getUser(){
return new User();
}
}
package demo;
import config.AppConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import pojo.User;
public class MyTest {
@Test
public void test01(){
/*
* 如果完全使用配置类去做,我们就只能通过AnnotationConfigApplicationContext来获取容器,通过配置class属性加载
* */
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
}
这种纯java在springboot中随处可见.
10.代理模式
为什么要学习代理模式呢?
因为这就是spring的AOP底层.
代理模式的分类
- 静态代理
- 动态代理
10.1 静态代理
角色分析
- 抽象角色:一般使用抽象类或者接口来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后.我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤
- 接口
/*
* 租房
* */
public interface Rent {
public void rent();
}
- 真实角色
package kuquan.demo1;
/*
* 房东
* */
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
- 代理角色
package kuquan.demo1;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Proxy implements Rent{
private Host host;
@Override
public void rent() {
seeHouse();
host.rent();
hold();
getMoney();
}
/*
* 看房
* */
public void seeHouse(){
System.out.println("中介带你看房");
}
/*
* 签合同
* */
public void hold(){
System.out.println("签合同");
}
/*
* 收中介费
* */
public void getMoney(){
System.out.println("收中介费");
}
}
- 客户端访问
package kuquan.demo1;
public class Client {
public static void main(String[] args) {
// 房东租房子
Host host = new Host();
// 代理,中介帮房东租房子,但是呢?代理一般有一些附属操作
Proxy proxy = new Proxy(host);
// 你不用面对房东,直接找中介租房即可
proxy.rent();
}
}
代理模式的好处
- 可以使得真实角色的操作更加纯粹!不用再去关注一些公共的业务
- 公共也就交给了代理!实现了业务分工!
- 公共业务发生拓展的时候方便集中管理
缺点
- 一个真实的角色就会产生一个代理角色,代码量翻倍,开发效率变低
10.2 静态代理再理解
样例:08-demo2
10.3 动态代理
- 动态代理和静态代理的角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
- 基于接口:jdk代理
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy(代理), InvocationHandler:调用处理程序
动态代理的好处:
- 可以使得真实角色的操作更加纯粹!不用再去关注一些公共的业务
- 公共也就交给了代理!实现了业务分工!
- 公共业务发生拓展的时候方便集中管理
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类,可以带多个类,只要是同一个接口即可
动态代理通用类
package kuquan.demo4;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 等会我们会用这个类动态生成代理类
* */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class proxyInvocationHandler implements InvocationHandler {
/*
* 被代理的接口
* */
private Object target;
/*
* 生成得到代理类
* */
public Object getProxy(){
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
return o;
}
/*
* 处理代理实例,并返回结果
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 拓展操作的位置
* */
/*
* 动态代理的本质,就是使用反射机制实现
* */
Object result = method.invoke(target, args);
/*
* 拓展操作的位置
* */
return result;
}
/*
* 需要执行的拓展
* */
}
11.AOP
11.1 什么是AOP?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2 AOP在spring中的应用
提供声明式事务:允许用户自定义切面
- 横切关注点:跨越应用程序的多个模块的功能或功能.即是,与我们业务逻辑无关的,但是我们需要关注的部分就是横切关注点.如日志,安全,缓存,事务等等…
- 切面(ASPECT):横切关注点被模块化的对象.即,它是一个类
- 通知(Advice):切面必须要完成的工作.即,它是类的一个方法.
- 目标(Target):被通知对象
- 代理(Proxy):向目标对象通知后创建的对象.
- 切入点(PointCut):切面通知执行的"地点"的定义.
- 连接点(JointPoint):与切入点匹配的执行点.
11.3 使用spring实现AOP
AspectJ织入
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用spring的API接口
注册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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config></context:annotation-config>
<!--配置aop:导入aop的约束-->
<aop:config>
<!--切入点-execution:执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* kuquan.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
</beans>
方式二:使用自定义类来实现AOP[主要是切面]
这种方式比较简单,但是功能也比较弱
类
package kuquan.diy;
public class DiyPointCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
System.out.println("方法执行后");
}
}
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config></context:annotation-config>
<!--注册bean-->
<bean id="userService" class="kuquan.service.UserServiceImpl"></bean>
<bean id="log" class="kuquan.log.Log"></bean>
<bean id="afterLog" class="kuquan.log.AfterLog"></bean>
<!--方式二:自定义类-->
<bean id="diy" class="kuquan.diy.DiyPointCut"></bean>
<aop:config>
<!--自定义切面,ref要引用的类-->
<aop:aspect ref="diy">
<!--切入点-->
<aop:pointcut id="point" expression="execution(* kuquan.service.UserServiceImpl.*(..))"/>
<!--通知-->
<aop:before method="before" pointcut-ref="point"></aop:before>
<aop:after method="after" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
</beans>
方式三:使用注解实现
package kuquan.diy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/*
* 使用注解实现AOP
* */
@Aspect
public class AnnotationPointCut {
@Before("execution(* kuquan.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("before");
}
}
// 然后再ioc容器中注册bean即可
12.整合Mybatis
步骤
- 导入相关的jar包
- junit
- mybatis
- mysql数据库
- spring相关
- aop织入
- mybatis-spring[new]
- 编写配置文件
- 测试
依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</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>
12.1 回忆mybatis
编写实体类
编写核心配置文件
编写接口
编写mapper.xml
测试
12.2 mybatis-spring
- 编写数据源配置
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="123"></property>
</bean>
- 创建sqlSessionFactory
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--绑定mybatis配置文件-->
<property name="configLocation" value="mybatis-config.xml"></property>
</bean>
- 创建SqlSessionTemplate
<!--SqlSessionTemplate:就是我们使用的sqlSession-->
<bean id="selSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入sqlSessionFactory,因为没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
</bean>
-
将这部分剥离出来单独作为spring-dao.xml
-
与mybatis不同的是,我们需要给mapper一个实现类
package kuquan.mapper;
import kuquan.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
public class UserMapperImpl implements UserMapper{
/*
* 我们所有的操作都使用sqlSessionTemplate
* */
@Autowired
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectAll() {
return sqlSession.getMapper(UserMapper.class).selectAll();
}
}
然后将其注入ioc容器中(参数使用注解的方式注入了)
<bean class="kuquan.mapper.UserMapperImpl" id="userMapper"></bean>
测试
@Test
public void test03(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 注入传入的是userMapper接口的实现类的id,然后传入UserMapper的class
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
List<User> userList = userMapper.selectAll();
for (User user : userList) {
System.out.println(user);
}
}
13.声明式事务
13.1 事务
把一组业务当作一个业务来做;要么都成功,要么都失败
要么都成功,要么都失败
事务在项目开发中,十分重要,涉及到数据一致性问题!不容马虎!
保证完整性和一致性
13.2 事务的ACID原则
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,要保证业务是相互隔离的,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会被印象,都被持久化地写到存储器中
13.3 spring中的事务管理
-
声明式事务:AOP
-
编程式事务:需要在代码中进行事务的管理
-
spring中开启事务
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
- 通过AOP方式实现事务织入
<!--结合AOP实现事务织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性 propagation-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"></tx:method>
<tx:method name="update" propagation="REQUIRED"></tx:method>
<tx:method name="query" read-only="true"></tx:method>
<tx:method name="*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
- 配置事务切入
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* kuquan.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"></aop:advisor>
</aop:config>
这样就在不修改代码的情况下添加了事务
思考:为什么需要事务?
-
如果不配置事务,可能存在数据提交不一致的情况
-
如果我们不在spring中配置声明式事务,就需要在代码中手动配置
apper userMapper = context.getBean(“userMapper”, UserMapper.class);
List userList = userMapper.selectAll();for (User user : userList) {
System.out.println(user);
}
}
## 13.声明式事务
### 13.1 事务
> 把一组业务当作一个业务来做;要么都成功,要么都失败
>
> 要么都成功,要么都失败
>
> 事务在项目开发中,十分重要,涉及到数据一致性问题!不容马虎!
>
> 保证完整性和一致性
### 13.2 事务的ACID原则
* 原子性
* 一致性
* 隔离性
* 多个业务可能操作同一个资源,要保证业务是相互隔离的,防止数据损坏
* 持久性
* 事务一旦提交,无论系统发生什么问题,结果都不会被印象,都被持久化地写到存储器中
### 13.3 spring中的事务管理
* 声明式事务:AOP
* 编程式事务:需要在代码中进行事务的管理
* spring中开启事务
```xml
<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>
- 通过AOP方式实现事务织入
<!--结合AOP实现事务织入-->
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性 propagation-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"></tx:method>
<tx:method name="update" propagation="REQUIRED"></tx:method>
<tx:method name="query" read-only="true"></tx:method>
<tx:method name="*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
- 配置事务切入
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* kuquan.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"></aop:advisor>
</aop:config>
这样就在不修改代码的情况下添加了事务
思考:为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况
- 如果我们不在spring中配置声明式事务,就需要在代码中手动配置
- 事务在项目开发中十分重要,涉及到数据完整性和一致性