Spring
优点:
- Spring 是一个开源的、免费的框架(容器)
- Spring 是一个轻量级、非入侵式的框架
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务,对框架整合的支持
需要导入的 jar 包:
<!-- 导入这个包就直接把其它的也导进来了,包括 core、context、aop、bean... 的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
一、IOC控制反转
就是把创建对象的操作交给 spring 去做,可以做到解耦,增加程序的可扩展性。
如果不使用 IOC 的话,创建对象是这样创建的:
1. 传统的创建对象方式:
DAO层:
public interface UserDao {
void getUsers();
}
public class UserDaoImpl implements UserDao {
public void getUsers() {
System.out.println("获取用户列表!");
}
}
public class UserDaoMysqlImpl implements UserDao{
public void getUsers() {
System.out.println("获取 mysql 用户列表!");
}
}
public class UserDaoOracleImpl implements UserDao{
public void getUsers() {
System.out.println("获取 oracle 用户列表!");
}
}
service层:
public interface UserService {
void getUsers();
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
public void getUsers() {
userDao.getUsers();
}
}
Test类:
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
userService.getUsers();
}
这样的话,调用的是 UserDaoImpl 实现类的方法,如果要调用 UserDaoMysqlImpl 或 UserDaoOracleImpl 的方法,就需要在 service 层修改代码,把 private UserDao userDao = new UserDaoImpl()
改成 private UserDao userDao = new UserDaoMysqlImpl()
,这样造成各个模块之间极大的耦合,而且如果后期修改需求的话,还得修改源代码,灵活性极差,所以就需要 spring 的控制反转,通过配置文件创建对象,这样主动权就交给了用户手上,而不是程序员手上
2. IOC 创建对象:
2.1 无参构造创建:
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">
<bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl"/>
<bean id="teacherDaoImpl" class="com.nari.dao.TeacherDaoImpl"/>
<bean id="studentServiceImpl" class="com.nari.service.StudentServiceImpl">
<property name="studentDao" ref="studentDaoImpl"/>
</bean>
</beans>
DAO 层:
public interface StudentDao {
void getStudent();
}
public class StudentDaoImpl implements StudentDao{
@Override
public void getStudent() {
System.out.println("获取学生!");
}
}
public class TeacherDaoImpl implements StudentDao{
@Override
public void getStudent() {
System.out.println("获取老师!");
}
}
Service 层:
public interface StudentService {
void getStudent();
}
public class StudentServiceImpl implements StudentService{
@Setter
private StudentDao studentDao;
@Override
public void getStudent() {
studentDao.getStudent();
}
}
Test类:
@Test
public void getUser(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentServiceImpl studentServiceImpl = (StudentServiceImpl) applicationContext.getBean("studentServiceImpl");
studentServiceImpl.getStudent();
}
这样就通过配置文件创建了对象,当程序一运行,spring 就自动帮我们创建好了对象
2.2 有参构造创建:
<!-- 有参构造创建对象的三种方式 -->
<!-- 使用下标的方式 -->
<constructor-arg index="0" ref="studentDaoImpl"/>
<!-- 使用属性名称 -->
<constructor-arg name="studentDao" ref="studentDaoImpl"/>
<!-- 使用属性类型 -->
<constructor-arg type="com.nari.dao.StudentDao" ref="studentDaoImpl"/>
推荐使用属性名称的方式进行创建
二、spring 配置文件详解:
alias:别名(不是给类起全限定名,而是给 id 起)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl"/>
<bean id="studentServiceImpl" class="com.nari.service.StudentServiceImpl">
<property name="studentDao" ref="studentDaoImpl"/>
</bean>
<alias name="studentDaoImpl" alias="student"/>
</beans>
import:导入配置文件
主要是多人协作时用的,比如有一个主配置文件 applicationContent.xml,多个不同开发人员配置的文件 beans.xml,就可以在主配置文件中导入从配置文件
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
spring 会自动对配置文件进行整合,如果有相同的配置,spring 会自动选择其中一个
bean:创建对象
<bean id="studentDaoImpl" class="com.nari.dao.StudentDaoImpl" name="u1 u2,u3;u4"/>
name 属性也是别名,比 alias 标签强大多了,推荐使用,而且可以起多个别名,能想到的分隔符都可以处理
三、依赖注入
就是创建对象的方式,本质其实就是 set 注入,如果没有 set 方法,是无法进行注入的
依赖注入:
- 依赖:bean 对象的创建依赖于 spring 容器
- 注入:bean 对象中的所有属性,由容器进行注入
注入方式目前有三种:
3.1 构造器注入
有参构造注入
无参构造注入
3.2 其它方式注入
p 命名空间注入:p 就是 properties 属性注入
<bean id="address" class="com.nari.pojo.Address" p:address="山西"/>
c 命名空间注入:c 就是 constructor 构造器注入
<bean id="address" class="com.nari.pojo.Address" c:address="运城"/>
注意点:
-
使用之前需导入相关约束
-
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
3.3 set 注入(重要)
pojo 实体类:
@Data
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 String wife;
private Properties info;
}
beans.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">
<bean id="address" class="com.nari.pojo.Address">
<property name="address" value="山西"/>
</bean>
<bean id="student" class="com.nari.pojo.Student">
<!-- 1. 基本类型注入 -->
<property name="name" value="张三"/>
<!-- 2. 引用类型注入 -->
<property name="address" ref="address"/>
<!-- 3. 数组注入 -->
<property name="books">
<array>
<value>语文</value>
<value>数学</value>
<value>政治</value>
<value>英语</value>
</array>
</property>
<!-- 4. list 集合注入 -->
<property name="hobbys">
<list>
<value>看书</value>
<value>学习</value>
<value>谈恋爱</value>
</list>
</property>
<!-- 5. map 类型注入 -->
<property name="card">
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key="身份证" value="14273100114580"/>
<entry key="学生证" value="20210678"/>
<entry key="银行卡" value="12345812354531351352"/>
</map>
</property>
<!-- 6. set 类型注入 -->
<property name="games">
<set>
<value>LOL</value>
<value>BOB</value>
<value>COC</value>
</set>
</property>
<!-- 7. NULL 类型注入 -->
<property name="wife">
<null/>
</property>
<!-- 8. properties 类型注入 -->
<property name="info">
<props>
<prop key="url">java.lang.String</prop>
<prop key="driver">jdbc://localhost:5236/DAMENG</prop>
<prop key="username">root</prop>
<prop key="password">ruirui</prop>
</props>
</property>
</bean>
</beans>
四、Bean 的作用域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PLK2i8EA-1622863598370)(/home/yijie/.config/Typora/typora-user-images/image-20210418231354846.png)]
bean 标签中的 scope 属性,主要分为四个值:
- singleton:单例模式(全局共享)
- prototype:原型模式(非全局共享)
- request
- session
注意点:
- 单例模式,两个创建的对象的 hashcode 是一样的,单线程时使用
- 原型模式,两个创建的对象是不一样的,多线程时使用
五、自动装配 Bean
- 自动装配是 spring 满足 bean 依赖的一种方式
- spring 会在上下文中自动寻找,并自动给 bean 装配属性
在 spring 中,有三种装配的方式:
- 在 xml 中显式的配置:就是上面的注入
- 在 java 代码中显式装配
- 隐式的自动装配 bean「重要」
装配方式一: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">
<bean id="cat" class="com.nari.spring.pojo.Cat"/>
<bean id="dog" class="com.nari.spring.pojo.Dog"/>
<bean id="person" class="com.nari.spring.pojo.Person" autowire="byName">
<property name="name" value="张三"/>
</bean>
</beans>
通过 bean 标签的 autowire 属性,此属性有五个值:
1、byName:通过 bean 标签的 id 去匹配
2、byType:通过 bean 标签的 class 属性匹配
3、constructor:通过构造器匹配
4、default:通过默认的方式匹配,默认方式为 no
5、no:不进行自动装配
注意点:
- byName 方式必须保证 bean 的 id 唯一,id 不可省略
- byType 方式必须保证 bean 的 class 唯一,且 id 可以省略
装配方式二:注解
1、@Autowired
public class Person {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String 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"
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.nari.spring.pojo.Cat"/>
<bean id="dog" class="com.nari.spring.pojo.Dog"/>
<bean id="person" class="com.nari.spring.pojo.Person"/>
</beans>
public class SpringTest {
@Test
public void DemoTest(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = applicationContext.getBean("person",Person.class);
person.getDog().shout();
person.getCat().shout();
}
}
注意事项:
-
需要导入相关约束:导入 context 约束
-
需要开启注解支持
-
@Autowired 注解有且仅有一个属性 required
public @interface Autowired { boolean required() default true; }
如果 required 设置为 false,表示这个对象可以为 null
public class Person { @Autowired(required = false) private Cat cat; }
-
@Autowired 注解可以放到 set 方法上,和放在属性上的效果是一样的
@Autowired public void setCat(Cat cat) { this.cat = cat; }
-
如果使用 @Autowired 注解,可以省略 set 方法,但是 get 方法不可省略
-
@Autowired 默认是通过 byName 实现自动装配,如果名字匹配不上,就通过 byType 匹配
2、@Qualifier
如果 @Autowired 的自动装配环境比较复杂,有多个类型相同且 id 不同的 bean 时,可以搭配使用 @Qualifier 注解,去指定一个 bean 去装配
<context:annotation-config/>
<bean id="cat111" class="com.nari.spring.pojo.Cat"/>
<bean id="cat11" class="com.nari.spring.pojo.Cat"/>
<bean id="dog111" class="com.nari.spring.pojo.Dog"/>
<bean id="dog11" class="com.nari.spring.pojo.Dog"/>
public class Person {
@Autowired
@Qualifier(value = "cat11")
private Cat cat;
@Autowired
@Qualifier(value = "dog111")
private Dog dog;
}
3、@Resource
此注解是 java 里自带的,不属于 spring,但是功能和 @Autowired 一样的,也是用于实现自动装配,它相当于是 @Autowired 和 @Qualifier 的集合体
public class Person {
@Autowired
@Qualifier(value = "cat11")
private Cat cat;
@Resource(name = "dog111")
private Dog dog;
}
该注解也是先通过属性名匹配,匹配不到的话在通过类型匹配
4、@Nullable
该注解放到属性上,表示该属性可以为 null 值
public Person(@Nullable String name) {
this.name = name;
}
六、使用注解开发
在 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:component-scan base-package="com.nari"/>
<context:annotation-config/>
</beans>
bean注入
相当于:
<bean id="user" class="com.nari.pojo.User">
@Component
public class User {
public String name;
}
属性注入
相当于:
<property name="name" value="张三"/>
@Component
public class User {
@Value(value = "张三")
public String name;
}
注意:
- 被注入的属性需是 public 的,否则获取不到属性
- 使用注解,可以省略属性的 get、set 方法,如果用 xml 配置的话,必须有 set 方法
- 使用注解需要如果不指定 bean 名称,默认是首字母小写的类名,也可自定义 bean 名
@Component 的衍生注解
@Repository、@Service、@Controller
分别对应 dao层、service层、controller层,其效果等同于 @Component,一个意思
作用域
@Component("user")
@Scope(value = "singleton")
public class User {
@Value(value = "张三")
public String name;
}
xml 与注解的区别
- xml 更加万能,适用于各种场景,维护简单
- 注解不是自己的类,使用不了,维护复杂
- 最好的办法是:xml 用于管理 bean,注解用于属性的注入
七、基于 java 替代 xml 配置:AppConfig
完全抛弃 xml 的配置,使用 java 代码代替,不用创建 xml 配置文件
1、创建 java 配置类
@Configuration
@ComponentScan(value = "com.nari.pojo")
public class UserConfig {
@Bean
public User getUser(){
return new User();
}
}
- @Configuration 表示该类为一个 java 配置类
- @ComponentScan 扫描哪个包或类的注解
- 新建一个 pojo 的返回值类型的方法
2、创建 pojo 类
@Data
@Component
public class User {
@Value(value = "123123")
private String name;
}
3、进行测试
public class UserTest {
@Test
public void UserDemoTest(){
ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
User getUser = context.getBean("getUser", User.class);
System.out.println(getUser.getName());
}
}
- 需要创建 AnnotationConfigApplicationContext 对象,参数为 java 配置类
- getBean 是通过配置类的方法名称得到 pojo 对象
八、代理模式
1、静态代理
好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务交给了代理角色,实现了业务的分工
- 公共业务在发生扩展的时候,方便集中管理
坏处:
- 一个真实的角色就需要创建一个代理角色,代码量会变多
代码案例
UserService:
public interface UserService {
public void add();
public void delete();
}
UserServiceImpl: 真实角色
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("调用了 add 方法");
}
@Override
public void delete() {
System.out.println("调用了 delete 方法");
}
}
UserProxy:
public class UserProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
public void log(String msg){
System.out.println("[Debug] 此处增加了" + msg + "日志!!");
}
}
Test:
public class ClientDemo {
@Test
public void proxyTest(){
//代理之前
UserServiceImpl userService = new UserServiceImpl();
userService.add();
userService.delete();
//代理之后
UserProxy userProxy = new UserProxy();
userProxy.setUserService(userService);
userProxy.add();
userProxy.delete();
}
}
代码详解
- UserService 是一个抽象接口,限制了实现功能的具体方法
- UserServiceImpl 是接口的实现类,实现了功能的具体细节
- UserProxy 是代理类,因为要在不改变原有代码的基础上,增加新的功能,所以还是需要实现接口,所以新的功能的具体实现细节,可以定义在代理类中,然后在原有功能的重写方法里,加上新的功能方法,然后在调用原有实现类的方法,以追加新的功能
2、动态代理
代码案例
User
// 公共接口类
public interface User {
void add();
void query();
}
UserImpl
// 真实的角色
public class UserImpl implements User{
@Override
public void add() {
System.out.println("增加了一个用户...");
}
@Override
public void query() {
System.out.println("查询所有用户...");
}
}
ProxyInvocationHandler
// 代理类
public class ProxyClassDemo implements InvocationHandler {
private Object target;
public void setUser(Object target) {
this.target = target;
}
// 生成一个代理类
Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 执行真实角色的方法,并返回执行结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
other(method.getName());
return result;
}
// 添加日志功能
public void other(String methodName){
System.out.println("执行了 " + methodName + " 方法...");
}
}
Test
public class ClientDemo {
public static void main(String[] args) {
// new 一个真实角色
Host host = new Host();
// 生成代理类
ProxyClassDemo proxyClassDemo = new ProxyClassDemo();
// 传入真实角色的类对象
proxyClassDemo.setUser(host);
// 强转为公共接口的类型
Rent proxy = (Rent) proxyClassDemo.getProxy();
// 调用真实对象的方法
proxy.money();
proxy.rent();
}
}
代码详解
-
动态代理主要分为两大类:
-
基于接口的动态代理
- JDK 动态代理 [ 在这里使用 ]
-
基于类的动态代理
- cglib
-
基于 java 字节码实现:
- javasist
-
-
动态代理主要需要用到两个类:
- Proxy:生成代理类
- InvocationHandler:调用处理程序
-
ProxyInvocationHandler 类的方法详解:
- getProxy():生成代理类,通过 Proxy.newProxyInstance() 静态方法动态获得代理类
- Proxy.newProxyInstance() :
- 参数一:当前类的类加载器,通过 this.getClass().getClassLoader() 来获取
- 参数二:公共接口,通过真实角色的 getClass() 的 getInterfaces() 来获取
- 参数三:InvocationHandler,该类本身,因为该类实现了 InvocationHandler 接口
- Proxy.newProxyInstance() :
- invoke():调用真实角色的类方法,关返回执行结果
- 直接调用 method.invoke() 方法,并返回执行结果即可
- invoke():
- 参数一:公共接口类
- 参数二:args
- invoke():
- 直接调用 method.invoke() 方法,并返回执行结果即可
- getProxy():生成代理类,通过 Proxy.newProxyInstance() 静态方法动态获得代理类
九、AOP [ 重要 ]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDmlsoTc-1622863598372)(/home/yijie/.config/Typora/typora-user-images/image-20210516224926804.png)]
在 spring 中实现 AOP 的三种方式
切面类:就是要实现扩展方法的类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHOfcegG-1622863598375)(/home/yijie/.config/Typora/typora-user-images/image-20210516225450025.png)]
-
不管以下哪三种方式,都需要开启 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" 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"> </beans>
1.1 基于 spring api 实现 aop
applicationContext.xml:
<bean id="userService" class="com.nari.demo1.service.UserServiceImpl"/>
<bean class="com.nari.demo1.log.BeforeLog" id="beforeLog"/>
<bean class="com.nari.demo1.log.AfterLog" id="afterLog"/>
<aop:config>
<!-- 设置切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.nari.demo1.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
AfterLog:实现 AfterAdvice 或 AfterReturningAdvice 接口,区别就是一个有方法执行后的返回值
public class AfterLog implements AfterReturningAdvice {
/**
*
* @param returnValue 方法的执行结果
* @param method 要执行的真实类的方法
* @param args 方法参数
* @param target 公共接口
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + " 执行了 " + method.getName() + " 方法,返回结果:" + returnValue);
for (int i = 0; i < 60; i++) {
System.out.print("=");
}
System.out.println();
}
}
BeforeLog:
public class BeforeLog implements MethodBeforeAdvice {
/**
*
* @param method 要执行的真实类的方法
* @param args 方法的参数
* @param target 公共接口
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() + " 类执行了 " + method.getName() + " 方法");
}
}
注意:
- 不管是 AfterAdvice、AfterReturningAdvice 或 MethodBeforeAdvice 或 BeforeAdvice,都是继承了 Advice 类
UserServiceImpl:
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户...");
}
@Override
public void delete() {
System.out.println("删除了一个用户...");
}
}
Test:
public class AOPDemo1 {
@Test
public void Demo1Test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.add();
userService.delete();
userService.select();
}
}
1.2 通过自定义类实现 aop
applicationContext.xml:
<!-- 注册 bean -->
<bean id="addLog" class="com.nari.demo2.log.AddLog"/>
<bean id="bookService" class="com.nari.demo2.service.BookServiceImpl"/>
<!-- 配置 aop -->
<aop:config>
<!-- 配置切面,ref 是自定义的切面类 -->
<aop:aspect ref="addLog">
<!-- 设置切入点 -->
<aop:pointcut id="pointcut" expression="execution(* com.nari.demo2.service.BookServiceImpl.*(..))"/>
<!-- 配置切入方法 -->
<aop:before method="beforeLog" pointcut-ref="pointcut"/>
<aop:after method="afterLog" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
AddLog:
public class AddLog {
public void beforeLog(){
System.out.println("在方法执行之前添加了一段日志...");
}
public void afterLog(){
System.out.println("在方法执行之后添加了一段日志...");
}
}
BookServiceImpl:
public class BookServiceImpl implements BookService{
@Override
public void addBook() {
System.out.println("增加了一本图书...");
}
@Override
public void deleteBook() {
System.out.println("删除了一本图书...");
}
}
Test:
public class AOPDemo2 {
@Test
public void Demo2Test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
BookService bookService = applicationContext.getBean("bookService", BookService.class);
bookService.addBook();
bookService.deleteBook();
}
}
1.3 通过注解方式实现 aop
applicationContext.xml:
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.nari.demo2"/>
<context:annotation-config/>
annotationPointCut:
@Aspect
@Component
public class AnnotationPointCut {
@Before("execution(* com.nari.demo2.service.BookServiceImpl.*(..))")
public void beforeLog(){
System.out.println("方式三:方法执行前...");
}
@After("execution( * com.nari.demo2.service.BookServiceImpl.*(..))")
public void afterLog(){
System.out.println("方式三:方法执行后...");
}
@Around("execution(* com.nari.demo2.service.BookServiceImpl.*(..))")
public void aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方式三:环绕前...");
joinPoint.proceed();
System.out.println("方式三:环绕后...");
System.out.println(joinPoint.getKind());
Signature signature = joinPoint.getSignature();
System.out.println(signature.getName());
}
}
- joinPoint.proceed() 执行具体方法
Test:
public class AOPDemo3 {
@Test
public void demo3Test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext3.xml");
BookService bookService = applicationContext.getBean("bookService", BookService.class);
bookService.deleteBook();
}
}
十、整合 mybatis
- 先导入相关 jar 包
- 编写 pojo 实体类
- 编写接口
- 编写 mapper 配置文件
- 编写 spring 配置文件
- 编写接口实现类,在实现类中使用 sqlSession 调用方法
需要导入的依赖包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!-- aop 相关依赖 jar 包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<!-- 整合 mybatis 相关依赖包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.6</version>
</dependency>
spring-usermapper.xml:
<!-- dataSource -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="jdbc:mariadb://localhost:3306/mybatis?characterEncoding=UTF-8&useUnicode=true"/>
<property name="driverClassName" value="org.mariadb.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="ruirui"/>
</bean>
<!-- sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 映射 Mapper 配置文件 -->
<property name="mapperLocations" value="classpath:com/nari/mapper/UserMapper.xml"/>
</bean>
<!-- sqlSession -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!-- 注册 userMapper 接口实现类 bean,将 sqlSession 注入进去 -->
<bean id="userMapper" class="com.nari.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
userMapperImpl:
@Setter
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
Test:
public class MybatisDemo1 {
@Test
public void test1(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-usermapper.xml");
UserMapper userMapper = applicationContext.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
十一、事务
特点:要么都成功,要么都失败,确保数据的完整性和一致性
事务的ACID原则:
- 原子性
- 事务的最小单位,无法在进行分割,事务中的操作要么都发生,要么都不发生
- 一致性
- 事务提交前后的数据的完整性必须保持一致,即事务完成后,符合逻辑运算
- 隔离性
- 保证多个业务操作同一个资源的时候,是隔离的,A事务的操作不影响B事务的操作
- 持久性
- 事务一旦提交,无论发生什么情况,数据的结果不会被影响,被持久化到存储器中
1、注解方式整合 mybatis
1.1 开启注解支持以及注解扫描
<context:component-scan base-package="com.narii"/>
<context:annotation-config/>
1.2 使用注解
@Data
@Component("userMapper")
public class UserMapperImpl implements UserMapper {
@Autowired
@Qualifier("sqlSession")
private SqlSessionTemplate sqlSession;
@Override
public List<UserPojo> queryUser() { ... }
2、通过切面方式添加事务
<!-- 开启声明式事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 使用 spring 整合事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 配置事务的传播特性 -->
<tx:attributes>
<!-- 给所有方法添加上事务 -->
<!-- 事务的传播特性有很多种,REQUIRED 是默认的,即如果没有事务就创建一个事务 -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置 aop -->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.narii.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>