Spring
-
简介:
- spring:春天—> 为软件行业带来了春天!
- 2002:首次推出了spring框架的雏形,interface21框架
- Spring框架以interface21框架为基础,经过重新设计,并不断的丰富器内涵,于2004年3月24日发布了1.0正式版。
- Rod Johnson Spring Framework的创始人,著名作者,悉尼大学的博士,专业并不是计算机,而是音乐学。
- spring理念:使现有的技术更加容易使用,本身是一个大杂烩!整合了现有的技术框架
- SSH:Strut2 + spring + Hibernate
- SSM:SpringMvc + Spring + Mybatis
- 官网
- 官方下载链接
- github地址
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.5</version> </dependency>
-
优点:
- spring是一个免费的开源的框架(容器)
- 轻量级、非入侵式的框架
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,对框架整合的支持!
总结:spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
-
组成:
-
扩展:
在spring的官网有这个介绍,现在化的java的开发,就是基于spring的开发!- springBoot:
一个快速开发的脚手架
基于springBoot可以快速的开发单个微服务
约定大于配置 - springCloud:
是基于springBoot实现的
弊端:发展太久以后,违背了原来的理念,配置十分繁琐,人称 “配置地域”
- springBoot:
IOC理论推导
- UserDao接口
public interface UserDao { void getUser(); }
- UserDaoImpl接口
public class UserDaoImpl implements UserDao{ public void getUser() { System.out.println("默认用户配置"); } }
- UserService接口
public interface UserService { void getUser(); }
- UserServicerImpl接口
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码,如果程序的代码量特别大,修改一次的代价十分昂贵,我们这里就可以根据Set接口实现,这样就发生了革命性的变化。
public class UserServiceImpl implements UserService {
private UserDao userDao ;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getUser();
}
}
- 之前程序是主动创建对象,控制权是在程序员手上!
- 使用了set 注入后,程序不再具有主动性,而是被动的接收对象
这种思想从本质上解决了问题,我们程序员不再用去管理对象的创建了!系统的耦合性大大降低,可以更加专注的在业务的实现上!这是IOC的原型!
IOC的本质
控制反转Ioc,是一种设计思想,DI(依赖注入)是实现IOC的一种方式,也有人认为DI只是Ioc的另一种说法。没有Ioc的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给了第三方,个人认为所谓的控制反转就是:获得依赖对象的方式反转了!
采用xml的方式配置bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(xml或者注解)并通过第三方去生产,或获取特定对象的方式。在spring中实现控制反转的对象是Ioc容器,其实现方法是依赖注入(Dependency Injection,DI)
Hello Spring
- pojo
public class User { private String name; public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } }
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="user" class="top.ydjsjy.pojo.User"> <property name="name" value="ydj"/> </bean> </beans>
- 测试
public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); Object user = context.getBean("user"); System.out.println(user.toString()); }
- 上述代码中并没有new对象,说明了user对象就是spring创建的
- user对象的属性是由Spring容器设置的。
- 控制反转的过程:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
反转:程序本身不创建对象,而变成被动的接对象
依赖注入:就是利用Set方法来进行注入的。
IOC是一种编程的思想,由主动编程变成被动的接收。
现在我们彻底的不用在程序中去改动了,要实现不通的操作,只需要在xml配置文件中进行修改,所谓的IoC,一句话说就是:对象由Spring创建、管理、装配!
IOC创建对象的方式
- 使用无参构造对象,默认方式!
<bean id="user" class="top.ydjsjy.pojo.User"> <property name="name" value="ydjs"/> </bean>
- 使用有参构造对象
- 下标赋值
<bean id="user" class="top.ydjsjy.pojo.User"> <constructor-arg index="0" value="ydj"/> </bean>
- 类型赋值
<bean id="user" class="top.ydjsjy.pojo.User"> <constructor-arg type="java.lang.String" value="ydj"/> </bean>
- 参数名称赋值
<bean id="user" class="top.ydjsjy.pojo.User"> <constructor-arg name="name" value="ydj22"/> </bean>
- 下标赋值
总结:在配置文件加载的时候,容器中管理的对象都已经被初始化了!
Spring配置
-
别名:
<!-- 别名,如果配置了别名,可以使用这个别名来获取到对象--> <alias name="user" alias="userNew"/>
-
Bean的配置
<!-- id:bean唯一的标识 class:bean对象所对应的全限定名,包名+类名 name:别名,可以取多个别名,多个别名中间可以以各种符号分割,比较强大,所以一般用这个取别名,不使用alias来取别名 --> <bean id="user" class="top.ydjsjy.pojo.User" name="u1,u2 u3;u4"> <constructor-arg name="name" value="ydj22"/> </bean>
-
Import
一般用于团队开发使用,他可以将多个配置文件导入合并为一个!
假设项目有多个人开发,每个人有自己的类进行开发,不同的类需要注册在不通的bean中,我们可以利用import将所有人的beans.xml合并为总的一个!使用的时候用总的一个就行了!- 张三.xml
- 王五.xml
- 李六.xml
- applicationContext.xml
<import resource="wangwu.xml"/> <import resource="liliu.xml"/> <import resource="zhangsan.xml"/>
DI依赖注入
-
构造器注入
如上!! -
Set方式注入【重点】
- 依赖注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
- 环境搭建
- 复杂类型
public class Address { private String add; public String getAdd() { return add; } public void setAdd(String add) { this.add = add; } @Override public String toString() { return "Address{" + "add='" + add + '\'' + '}'; } }
- 真实测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> game; 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 http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="address" class="top.ydjsjt.pojo.Address"> <property name="add" value="Beijing"/> </bean> <bean id="student" class="top.ydjsjt.pojo.Student"> <!--普通值注入--> <property name="name" value="ydj"/> <property name="address" ref="address"/> </bean> </beans>
- 测试类
public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getAddress()); }
- 完善注入信息
<?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="top.ydjsjt.pojo.Address"> <property name="add" value="Beijing"/> </bean> <bean id="student" class="top.ydjsjt.pojo.Student"> <!--普通值注入--> <property name="name" value="ydj"/> <!--第二种,bean注入--> <property name="address" ref="address"/> <!-- 数组--> <property name="books"> <array> <value>西游记</value> <value>三国演义</value> <value>水浒传</value> </array> </property> <!-- list--> <property name="hobbys"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <!-- map--> <property name="card"> <map> <entry key="邮政" value="1234567890"/> <entry key="光大" value="1234"/> </map> </property> <!-- Set--> <property name="game"> <set> <value>lol</value> <value>cf</value> </set> </property> <!-- null--> <property name="wife"> <null/> </property> <property name="info"> <props> <prop key="2021年">学习spring</prop> <prop key="2021年">学习Mybatis</prop> </props> </property> </bean> </beans>
- 复杂类型
- 依赖注入
-
拓展方式注入
我们可以使用p命名空间和c命名空间注入使方式如下:
<?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:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- P命名空间注入,可以直接注入属性的值:property,通过无参构造方法进行注入--> <bean id="user" class="top.ydjsjt.pojo.User" p:name="ydj" p:age="123" p:address-ref="address"/> <!-- C命名空间注入,通过有参构造器注入--> <bean id="userTwo" class="top.ydjsjt.pojo.User" c:name="sjy" c:age="321" c:address-ref="address"/> </beans>
注意点:
P命名空间和C命名空间不可以直接使用,需要导入xml约束!xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
-
bean的作用域
- 单例模式(Singleton)【spring的默认机制】:
显示的设置为单例。但是idea会提示你删除它,因为默认就已经是单例模式了<bean id="user" class="top.ydjsjt.pojo.User" p:name="ydj" p:age="123" p:address-ref="address" scope="singleton"/>
- 原型模式(Prototype)
每次从容器中get的时候,都会产生一个新的对象<bean id="user" class="top.ydjsjt.pojo.User" p:name="ydj" p:age="123" p:address-ref="address" scope="prototype"/>
- 其余request、session、application、websocket都是在web开发中使用的!
- 单例模式(Singleton)【spring的默认机制】:
Bean的自动装配
自动装配是spring满足bean依赖的一种方式!
spring会在上下文中自动寻找,并自动给出bean装配属性!
在spring中有三种自动装配的方式:
1、在spring中显示的配置
2、在java中显示的配置
3、隐式的自动装配bean【重要】
-
环境搭建:一个人有两个宠物
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="cat" class="top.ydjsjy.pojo.Cat"/> <bean id="dog" class="top.ydjsjy.pojo.Dog"/> <bean id="people" class="top.ydjsjy.pojo.People"> <property name="name" value="ydj"/> <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> </bean> </beans>
// dog实体类 public class Dog { public void call(){ System.out.println("wang"); } } //猫实体类 public class Cat { public void call() { System.out.println("miao"); } } //人实体类 public class People { private Cat cat; private Dog dog; private String name; }
-
使用ByName实现自动装配
以下代码中并没有去指定人的实体类中的dog和cat 但是spring帮我们自动装配了,如果名称不一致的话,使用ByName是无法实现自动装配的。<!--会在容器上下文中自动查找和自己对象set方法后面的值对应的beanid--> <bean id="people" class="top.ydjsjy.pojo.People" autowire="byName"> <property name="name" value="ydj"/> <!-- <property name="cat" ref="cat"/>--> <!-- <property name="dog" ref="dog"/>--> </bean>
-
使用ByType实现自动装配
必须保证类型是全局唯一!而且如果使用byType的话,bean的id可以省略<bean class="top.ydjsjy.pojo.Cat"/> <bean class="top.ydjsjy.pojo.Dog"/> <!--会在容器上下文中自动查找和自己对象属性类型相一致的bean--> <bean id="people" class="top.ydjsjy.pojo.People" autowire="byType"> <property name="name" value="ydj"/> <!-- <property name="cat" ref="cat"/>--> <!-- <property name="dog" ref="dog"/>--> </bean>
-
小结:
1、使用ByName的时候,必须保证bean的id全局唯一,并且这个bean需要和自动注入属性的set方法的值一致!
2、使用ByType的时候,必须保证bean的class全局唯一,并且这个bean需要和自动注入属性的类型一致! -
使用注解实现自动装配
jdk1.5支持的注解,spring从2.5就开始支持注解了!
The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML.
要使用注解:
1、导入约束:
2、配置注解的支持<?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方法上使用!
可以不编写set方法,前提是这个自动装配的的属性在IOC(Spring)容器中存在且符合名称!@Nullable 字段标记了这个注解,表示这个字段可以为null!
public @interface Autowired { boolean required() default true; }
如果显示的定义了autowired的required属性为false,说明这个对象可以为null,否则不允许为null!
@Autowired(required = false)
如果@Autowired的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成时,我们可以使用【@Qualifier(value = “cat”)】来配置@Autowired的使用,指定一个唯一的bean对象注入!
public class People { @Autowired(required = false) @Qualifier(value = "cat") private Cat cat; @Autowired private Dog dog; private String name; }
@Resource:
public class People { @Resource(name = "cat") private Cat cat; private Dog dog; private String name; }
小结:
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段名上!
@Autowired是通过byType来实现的,而且必须要求这个对象存在!
@Resource默认是通过byName来实现,如果找不到名字,则会去使用byType实现,如果两个都匹配不到的话,就报错。
执行的顺序不同:
使用注解开发
在spring4之后,要使用注解开发,必须要保证aop的包正常导入!
使用注解需要导入context的约束,增加注解的支持!
-
bean
-
属性如何注入
@Component //@Component:组件,放在类上,说明这个类被spring托管了,就是bean public class User { //使用value注解来为属性注入值! @Value("yyydddjjj") public String name; }
-
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
- dao【@Repostory】
- service 【@Service】
- controller【@Controller】
这四个注解的功能是一样的,都是代表将某个类注册到我们的spring容器中,装配Bean -
自动装配
@Autowired:自动装配,通过name 和 Type 如果Autowired不能唯一自动装配属性,则需要@Qualifier(value="xxx") @Nullable:字段标记了这个注释,则说明这个字段可以为空 @Resource:java的自动装配,通过名字和类型!
-
作用域
@Scope@Component @Scope(value = "singleton") //@Component:组件,放在类上,说明这个类被spring托管了,就是bean public class User { @Value("yyydddjjj") public String name; }
-
小结:
xml与注解:
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己的类使用不了,维护相对复杂
最佳实现:
xml用来管理bean,注解只负责属性的注入,我们在试用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解支持!<context:annotation-config/> <!--指定扫描的包,只有这个包下的注解会生效!--> <context:component-scan base-package="top.ydjsjy.pojo"/>
使用java的方式配置Spring
我们现在要完全不是用spring的xml 配置,全权交给java来做!
JavaConfig是Spring的一个子项目!在Spring4之后,他变成了核心功能!
实体类:
//代表这个类被spring接管了,注册到了容器中!
@Component
public class User {
@Value("ydj")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置文件:
// 本身就是一个配置,这个也会被spring托管,因为它也是Component,一个配置类
@Configuration
@ComponentScan("top.ydjsjy.pojo")
@Import(UserConfigTwo.class)
public class UserConfig {
//注册一个bean,就相当于之前的配置个bean,方法名就是xml中的id,返回值就是class
@Bean
public User getUser() {
return new User();
}
}
测试类:
public class myTest {
@Test
public void getu() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
User getUser = context.getBean("getUserTwo", User.class);
System.out.println(getUser);
}
}
代理模式
为什么要学习代理模式?这既是springAOP的底层,面向切面编程!
代理模式的分为静态代理和动态代理
-
静态代理
角色分析:- 抽象角色:一般会使用接口和抽象类来实现
- 真是角色:被代理的角色
- 代理角色:代理真实的角色,代理真实角色后,我们一般会做一些附属操作。
- 客户:访问代理对象的人!
静态代理模式的好处:
- 可以使角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共业务交给了代理角色,实现了业务的分工!
- 公共业务发生扩展的时候,方便了集中管理
静态代理模式的缺点:
一个真实角色就会产生一个代理角色,代码量翻倍,代码实现:
- 接口
//租房接口 public class landlord implements Rent{ public void rentRoot() { System.out.println("房东要出租房子"); } }
- 真实角色
//房东 public class landlord implements Rent{ public void rentRoot() { System.out.println("房东要出租房子"); } }
- 代理角色
public class ZiRoom implements Rent { private landlord lan; public ZiRoom() { } public ZiRoom(landlord lan) { this.lan = lan; } public void seeRoom() { System.out.println("带租客看房"); } public void rentRoot() { seeRoom(); lan.rentRoot(); } }
- 客户端访问代理角色
public class People { public static void main(String[] args) { landlord landlord = new landlord(); ZiRoom ziRoom = new ZiRoom(landlord); // ziRoom.seeRoom(); ziRoom.rentRoot(); } }
AOP的实现机制:
代码二次实现:- 接口
public interface UserDao { void add(); void delete(); void update(); void select(); }
- dao(真实对象)
public class UserDaoImpl implements UserDao { public void add() { System.out.println("add了数据"); } public void delete() { System.out.println("delete了数据"); } public void update() { System.out.println("update了数据"); } public void select() { System.out.println("select了数据"); } }
- 代理对象
public class proxy implements UserDao { private UserDaoImpl userService; public void setUserService(UserDaoImpl 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 select() { log("select"); userService.select(); } public void log(String status) { System.out.println(status + " log"); } }
- 客户端
public class client { public static void main(String[] args) { UserDaoImpl userService = new UserDaoImpl(); proxy proxy = new proxy(); proxy.setUserService(userService); proxy.add(); proxy.delete(); proxy.select(); proxy.update(); } }
-
动态代理
动态代理和静态代理的角色是一致的
动态代理的代理类是动态生成的,不是我们写好的!
动态代理也分为两大类:基于接口—JDK动态代理,基于类的动态代理—cjlib,通过java字节码也可以实现:javasist!
我们需要了解两个类:Proxy:代理类,InvocationHandler:调用处理程序动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共也就交给了代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般代理的都是一类的业务!
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!代码实现:
public interface UserService { void select(); void delete(); void update(); void add(); }
public class UserServiceImpl implements UserService { public void select() { System.out.println("查询数据"); } public void delete() { System.out.println("删除数据"); } public void update() { System.out.println("更新数据"); } public void add() { System.out.println("增加数据"); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class dynamicProxy 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); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); return method.invoke(target, args); } public void log(String msg) { System.out.println("日志打印:" + msg); } }
public class Client { public static void main(String[] args) { UserServiceImpl userService = new UserServiceImpl(); dynamicProxy dynamicProxy = new dynamicProxy(); dynamicProxy.setTarget(userService); UserService proxy = (UserService) dynamicProxy.getProxy(); proxy.delete(); } }
AOP
-
什么是AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
-
AOP在Spring中的作用:
提供申明事务,允许用户自定义切面- 横切关注点:跨越应用程序多个模块的方法或功能,即使与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如:日志、安全、缓存、事务等
- 切面(ASPECT):横切关注点被模块化的特殊对象。即:它是一个类
- 通知(Advice):切面必须要完成的工作。即:它是类中的一个方法
- 目标(Target):被通知的对象。
- 代理(Proxy):向目标应用通知后创建的对象。
- 切入点(PointCut):切面通知执行的“地点”的定义。
- 连接点(JoinPoint):与切入点匹配的执行点。
SpringAOP中,通过Advice定义横切逻辑,Spring中支持五种类型的Advice
即AOP在不改变原有代码的逻辑下,去增加新的功能!
-
使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
- 使用Spring的API接口实现【主要面向spring接口实现】
public interface UserService { void select(); void update(); void delete(); void add(); }
public class UserServiceImpl implements UserService { public void select() { System.out.println("查询"); } public void update() { System.out.println("更新"); } public void delete() { System.out.println("删除"); } public void add() { System.out.println("增加"); } }
//后置 public class AfterLog implements AfterReturningAdvice { public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName() + " " + returnValue); } } //前置 public class BefoLog implements MethodBeforeAdvice { public void before(Method method, Object[] args, Object target) throws Throwable { assert target != null; System.out.println(target.getClass().getName() + " 被执行了"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userserviceImpl" class="top.ydjsy.Service.UserServiceImpl"/> <bean id="afterlog" class="top.ydjsy.Log.AfterLog"/> <bean id="beforlog" class="top.ydjsy.Log.BefoLog"/> <!-- 配置AOP,需要导入aop的约束--> <aop:config> <!-- 切入点:表达式,expression(要执行的位置! * * * * *)--> <aop:pointcut id="agterpoint" expression="execution(* top.ydjsy.Service.UserServiceImpl.*(..))"/> <!-- 执行环绕增强--> <aop:advisor advice-ref="afterlog" pointcut-ref="agterpoint"/> <aop:advisor advice-ref="beforlog" pointcut-ref="agterpoint"/> </aop:config> </beans>
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); UserService bean = context.getBean(UserService.class); bean.add(); } }
- 自定义实现AOP【主要是切面定义】
public interface UserService { void select(); void update(); void delete(); void add(); } public class UserServiceImpl implements UserService { public void select() { System.out.println("查询"); } public void update() { System.out.println("更新"); } public void delete() { System.out.println("删除"); } public void add() { System.out.println("增加"); } }
public class diyPoint { public void before(){ System.out.println("===========开始==========="); } public void after(){ System.out.println("===========结束==========="); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="userserviceImpl" class="top.ydjsy.Service.UserServiceImpl"/> <bean id="afterlog" class="top.ydjsy.Log.AfterLog"/> <bean id="beforlog" class="top.ydjsy.Log.BefoLog"/> <!-- 配置AOP,需要导入aop的约束--> <!-- <aop:config>--> <!-- <!– 切入点:表达式,expression(要执行的位置! * * * * *)–>--> <!-- <aop:pointcut id="agterpoint" expression="execution(* top.ydjsy.Service.UserServiceImpl.*(..))"/>--> <!-- <!– 执行环绕增强–>--> <!-- <aop:advisor advice-ref="afterlog" pointcut-ref="agterpoint"/>--> <!-- <aop:advisor advice-ref="beforlog" pointcut-ref="agterpoint"/>--> <!-- </aop:config>--> <bean id="diypoint" class="top.ydjsy.Log.diyPoint"/> <aop:config> <aop:aspect id="diy" ref="diypoint"> <aop:pointcut id="point" expression="execution(* top.ydjsy.Service.UserServiceImpl.*(..))"/> <aop:after method="after" pointcut-ref="point"/> <aop:before method="before" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>
- 使用注解实现
@Aspect public class annotation { @Before("execution(* top.ydjsy.Service.UserServiceImpl.*(..))") public void before() { System.out.println("==========begin============"); } @After("execution(* top.ydjsy.Service.UserServiceImpl.*(..))") public void after() { System.out.println("===========agter==========="); } @Around("execution(* top.ydjsy.Service.UserServiceImpl.*(..))") public void huanrao(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); System.out.println(jp.getSignature()); Object proceed = jp.proceed(); //执行方法 System.out.println("环绕后"); } }
<bean id="annotation" class="top.ydjsy.Log.annotation"/> <aop:aspectj-autoproxy/>
- 使用Spring的API接口实现【主要面向spring接口实现】
整合mybatis
步骤
-
导入相关jar包:
- junt
- mybatis
- mysql数据库
- spring相关
- aop织入包
- mybatis-spring
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>study-spring</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>spring-mybatis</module> </modules> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.5</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> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.18</version> </dependency> </dependencies> <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build> </project>
回忆mybatis
- 编写实体类
- 编写核心配置文件
- 编写接口
- 编写Mapper.xml
- 测试
Mybatis-spring
-
编写数据源配置
<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?test&useSSL=true&useUnicode=true&serverTimezone=GMT&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="7348622"/> </bean>
-
sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--在这里可以配置mybatis的所有配置,但是也可以引入mybatis的配置文件,我这里是引入配置文件, 一般习惯是将mybatis的别名和设置放到mybatis的配置文件中,不容易混乱--> <property name="configLocation" value="classpath:mybatis.xml"/> <!--下面这个参数是注册mapper,之前是我们写在mybatis的配置文件中,但是用了spring以后,就在spring这里引入了--> <property name="mapperLocations" value="classpath:top/ydjsjy/Dao/*.xml"/> </bean>
-
sqlSessionTemplate
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean>
-
需要给借口加实现类,第二种是继承SqlSessionDaoSupport类。
public interface PeopelMapper { List<People> getPeople(); }
import org.mybatis.spring.SqlSessionTemplate; import top.ydjsjy.pojo.People; import java.util.List; public class PeopelMapperImpl implements PeopelMapper { private SqlSessionTemplate sqlsession; public void setSqlsession(SqlSessionTemplate sqlsession) { this.sqlsession = sqlsession; } public List<People> getPeople() { PeopelMapper mapper = sqlsession.getMapper(PeopelMapper.class); return mapper.getPeople(); } }
package top.ydjsjy.service; import org.mybatis.spring.support.SqlSessionDaoSupport; import top.ydjsjy.pojo.User; import java.util.List; public class UserServiceImpl extends SqlSessionDaoSupport implements UserService { public List<User> select() { UserService mapper = getSqlSession().getMapper(UserService.class); return mapper.select(); } public void update() { } public void delete() { } public void inseart() { } }
-
将自己写的实现类,注入到Spring中
<bean id="peopelimpl" class="top.ydjsjy.Dao.PeopelMapperImpl"> <property name="sqlsession" ref="sqlSession"/> </bean>
-
测试是否可用
@Test public void getPeopel() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); PeopelMapper peopelimpl = context.getBean("peopelimpl", PeopelMapper.class); for (People person : peopelimpl.getPeople()) { System.out.println(person); } }
-
最后总的配置文件如下:
PeopelMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="top.ydjsjy.Dao.PeopelMapper"> <select id="getPeople" resultType="people"> SELECT * FROM bbs.peole; </select> </mapper>
ApplicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <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?test&useSSL=true&useUnicode=true&serverTimezone=GMT&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="7348622"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!--在这里可以配置mybatis的所有配置,但是也可以引入mybatis的配置文件,我这里是引入配置文件, 一般习惯是将mybatis的别名和设置放到mybatis的配置文件中,不容易混乱--> <property name="configLocation" value="classpath:mybatis.xml"/> <!--下面这个参数是注册mapper,之前是我们写在mybatis的配置文件中,但是用了spring以后,就在spring这里引入了--> <property name="mapperLocations" value="classpath:top/ydjsjy/Dao/*.xml"/> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="peopelimpl" class="top.ydjsjy.Dao.PeopelMapperImpl"> <property name="sqlsession" ref="sqlSession"/> </bean> </beans>
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="logImpl" value="LOG4J"/> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="top.ydjsjy.pojo"/> </typeAliases> </configuration>
log4j.properties
log4j.rootLogger=DEBUG,console,file log4j.additivity.org.apache=true log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.Threshold=DEBUG log4j.appender.console.ImmediateFlush=true log4j.appender.console.Target=System.err log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.Threshold=DEBUG log4j.appender.file.ImmediateFlush=true log4j.appender.file.Append=true log4j.appender.file.File=./log/dj log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n #log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender #log4j.appender.im.host = mail.cybercorlin.net #log4j.appender.im.username = username #log4j.appender.im.password = password #log4j.appender.im.recipient = corlin@cybercorlin.net #log4j.appender.im.layout=org.apache.log4j.PatternLayout #log4j.appender.im.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
声明式事务
回顾事务
- 把一组业务当成一个业务来做,要么都成功,要么都失败!
- 事务在项目开发中十分重要,涉及到数据一致性的问题,不能马虎!
- 确保完整性和一致性!
事务的ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不能再被影响!被持久化的写入到存储中!
spring中的事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中进行事务的管理,比如try_catch,若中途报错则回滚!
为什么需要事务?
- 如果不配置事务,可能存在数据提交不一致的情况;
- 如果我们不在spring中配置声明式事务,我们就需要在代码中手动配置事务!
- 事务在项目的开发中,十分重要,涉及到数据的一致性和完整性问题!
相关代码:
- pojo
@Data @AllArgsConstructor public class User { private String id; private String name; private String pwd; }
- Mapper接口:
import top.ydjsjy.pojo.User; import java.util.List; public interface UserMapper { List<User> select(); int update(User user); int delete(String id); int add(User user); }
- Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="top.ydjsjy.mapper.UserMapper"> <select id="select" resultType="user"> SELECT * FROM test.user; </select> <insert id="add" parameterType="user"> INSERT INTO test.user (id, name, pwd) VALUES (#{id}, #{name}, #{pwd}); </insert> <delete id="delete"> DELETE FROM test.user WHERE id = #{id} </delete> </mapper>
- UserMapperImpl
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper { public List<User> select() { UserMapper mapper = getSqlSession().getMapper(UserMapper.class); add(new User("10", "yyy", "212dd")); delete("8"); return mapper.select(); } public int update(User user) { return getSqlSession().getMapper(UserMapper.class).update(user); } public int delete(String id) { return getSqlSession().getMapper(UserMapper.class).delete(id); } public int add(User user) { return getSqlSession().getMapper(UserMapper.class).add(user); } }
- 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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://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 https://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?test&useSSL=true&useUnicode=true&serverTimezone=GMT&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="7348622"/> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis-conf.xml"/> <property name="mapperLocations" value="classpath:top/ydjsjy/mapper/*.xml"/> </bean> <bean id="userserviceimpl" class="top.ydjsjy.mapper.UserMapperImpl"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> <bean id="after" class="top.ydjsjy.mapper.aft"/> <bean id="bef" class="top.ydjsjy.mapper.bef"/> <aop:config> <aop:pointcut id="point" expression="execution(* top.ydjsjy.mapper.UserMapperImpl.*(..))"/> <aop:advisor advice-ref="after" pointcut-ref="point"/> <aop:advisor advice-ref="bef" pointcut-ref="point"/> </aop:config> <!--配置声明事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- <constructor-arg ref="dataSource"/>--> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- <tx:method name="add" propagation="REQUIRED"/>--> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="txpoint" expression="execution(* top.ydjsjy.mapper.UserMapperImpl.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txpoint"/> </aop:config> </beans>
- mybatis-conf.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="logImpl" value="LOG4J"/> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <package name="top.ydjsjy.pojo"/> </typeAliases> </configuration>