补Spring笔记,第一次学习总是没有什么印象,不断的重复学习以及使用才能更好的掌握知识点。
循环依赖:指的是多个Bean之间的相互调用,A对象创建时,需要构造B对象,构造B对象时需要构建C对象,构建C对象时又依赖A对象
1. Spring Start Learn
1.1 spring简介:
Spring框架以interface 21框架为基础,经过重新设计,并不断丰富其内涵,2004年发布1.0版本,由音乐学博士Rod Johnson创建,其专业是音乐学并不是计算机。
Spring是一个开源的框架,是一个轻量级、非侵入式的框架。
1.2 Spring理念
使得现有技术更容易使用,自身集成了各种组件
其核心为控制反转(IOC)、面向切面(AOP)
支持事务的处理
1.3 学习新的web框架
SSM:spring + springMVC+Mybatis
1.4 相关网址
github:托管源码网址 使用的是Gradlew
官网:官网介绍
官方网址下载地址:官方下载地址
1.5 构建项目,使用Maven
<!--SpringMVC,引入该依赖会将spring的相关依赖引入-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--引入数据库相关的连接组件-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
Spring整合太多的框架以及引进过多的组件,产生的弊端是“配置过多”
1.6 Spring好处
方便解耦、简化开发
AOP编程的支持
声明式事务的支持
便于单元测试
继承各种框架
降低 javaAPI的使用难度
2. IOC
Ioc是一种设计思想,称之为控制反转,可以分解为控制和反转,IOC容器控制着对象的创建或者叫做外部资源的获取方式; 一般主动的new对象是在程序中,程序主动创建对象,IOC容器管理着对象,因此程序中的对象是被动的接受依赖对象,因此称之为反转
2.1 传统的耦合开发
传统的开发,面向接口的开发,数据层接口Dao的实现类DaoImpl在Service中手动创建,然后调用底层的数据操作方法将业务层数据持久化。
项目的分层截图:
传统开发流程
dao层
//dao层的接口
public interface UserDao {
void sayHello();
}
//dao层的实现类
public class UserDaoImpl implements UserDao {
@Override
public void sayHello() {
System.out.println("Hello Spring");
}
}
service层
//service接口
public interface UserService {
void sayHello();
}
//service层实现类,依赖dao层,耦合程度较高
public class UserServiceImpl implements UserService {
UserDao userdao = new UserDaoImpl();
@Override
public void sayHello() {
userdao.sayHello();
}
}
缺点是才层之间是一个高耦合程序,因此可以采用setter进行接耦合,对象的创建不在于程序本身而是在于用户传入的参数。
//对象的创建在于传入的参数不再是直接new一个对象,耦合性降低
private UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
2.2 IOC将对象的创建交给spring
IOC:Inversion of Control 控制反转,将对象的创建交给Spring去管理,实现了程序之间的解耦合。
在dao层有三种数据库的连接方式,不修改程序只修改配置文件完成对dao实例的选取。
创建一个普通的对象类
public class Customer {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
'}';
}
}
spring中的配置文件是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使用spring管理,其字段使用value进行赋值-->
<bean name="custom" class="com.itxing.pojo.Customer">
<property name="name" value="xing"></property>
</bean>
<!--将dao层的对象注入到spring容器中-->
<bean id="mysqlDao" class="com.itxing.dao.UserMysqlDaoImpl"></bean>
<bean id="oracleDao" class="com.itxing.dao.UserOracleDaoImpl"></bean>
<bean id="userService" class="com.itxing.service.UserServiceImpl">
<!--使用ref将对象与前面的对象进行关联,可以进行切换不用修改配置文件-->
<property name="userDao" ref="mysqlDao"></property>
<!-- <property name="userDao" ref="oracleDao"></property>-->
</bean>
</beans>
对普通的Bean进行测试,对象不再使用new进行创建将其交给spring
@Test
public void Mytest2(){
//获取spring上下文对象,根据上下文对象将spring中的bean创建
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Customer custom = (Customer)context.getBean("custom");
System.out.println(custom.toString());
}
2.3 IOC创建对象的方式
1.使用无参数的构造函数
<bean name="custom" class="com.itxing.pojo.Customer">
<property name="name" value="xing"></property>
</bean>
2.自定义参数的构造
public class Customer {
String name;
//带参构造函数
public Customer(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
'}';
}
}
自定义的带参数的构造函数有三种依赖注入的方式
<!--配置1:带参数的构造函数的spring配置,使用下标进行构造填充,从0开始-->
<bean name="custom1" class="com.itxing.pojo.Customer">
<constructor-arg index="0" value="dayangege"></constructor-arg>
</bean>
<!--配置2(推荐使用):带参数的构造函数的spring配置,使用name属性进行属性注入-->
<bean name="custom1" class="com.itxing.pojo.Customer">
<constructor-arg name="name" value="dayangege"></constructor-arg>
</bean>
<!--配置3(不推荐使用):带参数的构造函数的spring配置,类型匹配-->
<bean name="custom1" class="com.itxing.pojo.Customer">
<constructor-arg type="java.lang.String" value="dage"></constructor-arg>
</bean>
ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);
context.getBean(“userService”);
在getBean时,容器中管理的对象就已经初始化了。
3.spring的配置
3.1 配置别名
<bean name="custom1" class="com.itxing.pojo.Customer">
<constructor-arg name="name" value="dayangege"></constructor-arg>
</bean>
<alias name="custom1" alias="customer"></alias>
3.2 bean中属性介绍
bean的id:唯一标识符
bean的class属性:对象的权限定名
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将dao层的对象注入到spring容器中-->
<bean id="mysqlDao" class="com.itxing.dao.UserMysqlDaoImpl"></bean>
<bean id="oracleDao" class="com.itxing.dao.UserOracleDaoImpl"></bean>
<bean id="userService" class="com.itxing.service.UserServiceImpl">
<!--使用ref将对象与前面的对象进行关联-->
<property name="userDao" ref="mysqlDao"></property>
</bean>
</beans>
3.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--将其余的有效配置放置在总的xml文件中-->
<import resource="beans1.xml"></import>
</beans>
4. 依赖注入(DI)
4.1 依赖注入问题
动态的向某一个对象提供它所需要的其他对象。
1.谁依赖于谁?:应用程序依赖于IOC容器
2.为什么需要依赖?:应用程序需要IOC容器来提供对象需要的外部资源
3.谁注入了谁?:IOC容器注入应用程序某个对象,应用程序依赖的对象
4.注入了什么?:注入某个对象所需要的外部资源(对象、数据)
4.2 构造器注入
(分为空参数的构造器和带参数的构造器),前面的介绍
构造器注入的缺点:参数过多时,xml中的property项过于繁多,有的人喜欢构造器注入
4.3 setting注入(推荐)
相关的环境准备
实体类准备
public class Address {
String addr;
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Address{" +
"addr='" + addr + '\'' +
'}';
}
}
public class Student {
private String name; //普通字段
private Address addr;//引用类型字段
private String[] books;//数组类型
private List<String> hobby;//List集合类型
private Map<String,String> card;//Map集合类型
private Set<String> games;//Set集合类型
private String wife;//空值注入
private Properties info;//props注入
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddr() {
return addr;
}
public void setAddr(Address addr) {
this.addr = addr;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", addr=" + addr.toString() +
", books=" + Arrays.toString(books) +
", hobby=" + hobby +
", card=" + card +
", games=" + games +
", wife='" + wife + '\'' +
", info=" + info +
'}';
}
}
配置文件注入
8种不同的类型注入,一般的注入、引用类型、数组、List、Set、Map、Null、Properties
<?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="addr" class="com.itxing.pojo.Address">
<property name="addr" value="西安"></property>
</bean>
<bean id="student" class="com.itxing.pojo.Student">
<!--1.普通的属性注入-->
<property name="name" value="星星"></property>
<!--2.bean注入,当前bean的一个属性为另一个bean,即引用类型-->
<property name="addr" ref="addr"/>
<!--3.数组的注入方式-->
<property name="books">
<array>
<value>java入门到放弃</value>
<value>并发编程入门到放弃</value>
<value>javaweb入门到放弃</value>
<value>java网络编程入门到放弃</value>
</array>
</property>
<!--4.List集合的注入方式-->
<property name="hobby">
<list>
<value>听歌</value>
<value>跳舞</value>
<value>rap</value>
<value>篮球</value>
</list>
</property>
<!--5.Map集合的注入方式-->
<property name="card">
<map>
<entry key="身份证号" value="123456"></entry>
<entry key="银行卡号" value="789654"></entry>
</map>
</property>
<!--6.Set集合的注入方式-->
<property name="games">
<set>
<value>LOL</value>
<value>cs</value>
<value>cf</value>
</set>
</property>
<!--7.空值注入-->
<property name="wife">
<null></null>
</property>
<!--8.配置注入-->
<property name="info">
<props>
<prop key="学号">18062103101</prop>
<prop key="姓名">itxing</prop>
<prop key="年龄">25</prop>
</props>
</property>
</bean>
</beans>
测试注入结果
@Test
public void mytest4(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean实例
Student student = (Student) context.getBean("student");
//输出bean
System.out.println(student.toString());
}
4.4 其他方式
引入命名空间的原因在于使用bean的元素的属性进行元素的属性值设置,不再需要进行
p命名空间p命名空间注入讲解
c命名空间c命名空间注入讲解
xml中的bean的命名空间中引入两个命名空间的url
<beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property 必须有无参数的构造函数--> <bean id="addr" class="com.itxing.pojo.Address" p:addr="西安"></bean> <bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" ></bean> <!--c命名空间注入,c命名空间是使用构造器注入值的,不推荐使用,所提供的pojo必须有实参构造函数--> <bean id="addr1" class="com.itxing.pojo.Address" c:addr= "韩城"></bean> <bean id="stu02" class="com.itxing.pojo.Student" c:name="大雁" c:addr-ref="addr1"></bean> </beans>
注意事项:引入p命名空间需要的是一个空参数的构造函数,c命名空间是一个带参数的构造函数。
4.5 Bean的作用域
官方文档中显示有6个作用域的范围
作用域 | 描述 |
---|---|
singleton | 默认,IOC容器中只有一个实例对象 |
prototype | 原型模式,将单个bean定义的作用域限定为任意数量的实例对象 |
request | 将单个bean定义的作用域限定为Http请求的生命周期,每一个http请求都有一个单个的bean实例对象 |
session | 将单个bean定义的作用域限定为Http的整个session回话中,spring上下文中有效 |
application | 将单个bean定义的作用域限定为servletContext |
websocket | 将单个bean定义的作用域限定为的生命周期WebSocket |
自己设置对象的作用范围时,可以设置为singleton、prototype、request、session
作用范围:单例模式
<bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" scope="singleton"></bean>
//将bean的scope属性设置成一个singleton进行测试
@Test
public void mytest5(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student1 = (Student) context.getBean("stu02");
Student student2 = (Student) context.getBean("stu02");
System.out.println(student1==student2);
}
结果为:True
作用范围:原型模式
<bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" scope="prototype"></bean>
其余的作用范围在web开发时会用到。
//将bean的scope设置为一个prototype进行测试
@Test
public void mytest6(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student1 = (Student) context.getBean("stu02");
Student student2 = (Student) context.getBean("stu02");
System.out.println(student1==student2);
}
结果为:false
5.Bean的自动装配
自动装配是spring满足bean依赖的一种方式,spring会在上下文中自动寻找,并给bean装配属性
1.在xml中显示配置
2.在java中显示配置
3.隐式的自动装配bean
一个People类中依赖两个pojo对象Cat、Dog以及一个String属性,之前的手动配置需要对引用类型的对象使用ref指向bean的id,使用value指定属性的属性值。
手动装配时:
<bean id="dog" class="com.itxing.pojo.Dog"></bean>
<bean id="cat" class="com.itxing.pojo.Cat"></bean>
<bean id="people" class="com.itxing.pojo.People" >
<property name="name" value="xingzai"></property>
<property name="cat" ref="cat"></property>
<property name="dog" ref="dog"></property>
</bean>
5.1 byName自动装配
使用autowire=”byName”进行自动配置,实体类中的属性命名与spring上下文中的beanid相同
<bean id="dog" class="com.itxing.pojo.Dog"></bean>
<bean id="cat" class="com.itxing.pojo.Cat"></bean>
<!--autowire="byName" 自动装配,通过名字进行装配,寻找自己对象set方法后面对象的beanid,缺点:命名可能会与set中的命名不一致-->
<bean id="people" class="com.itxing.pojo.People" autowire="byName">
<property name="name" value="xingzai"></property>
</bean>
5.2 byType自动装配
使用autowire=”byType”进行自动装配时,spring上下文有唯一的实体类型,如果有两个以上的bean指向同一个类,则匹配失败
<bean id="dog" class="com.itxing.pojo.Dog"></bean>
<bean id="cat" class="com.itxing.pojo.Cat"></bean>
<!--autowire="byType" 会在容器上下文中寻找与自己属性类型相同的beanid,如果有两个以上的类型bean会出错-->
<bean id="people" class="com.itxing.pojo.People" autowire="byType">
<property name="name" value="xingzai"></property>
</bean>
5.3 使用注解自动装配
使用注解比XML更好吗?
答案:时情况而定,每种方法都有各自的优缺点,注释在声明中提供了很多上下文,使得配置更短,更简洁。xml擅长连接组件而不接触其源代码或重新编译他们。注解开发将配置分散,一定程度上难以管理。
需要在xml中添加context命名空间以及context约束
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" 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="dog" class="com.itxing.pojo.Dog"/>
<bean id="cat" class="com.itxing.pojo.Cat"/>
<bean id="people" class="com.itxing.pojo.People"/>
</beans>
Autowried注解,在pojo属性中声明Autowried注解可以简化xml配置以及可以省略相关的set方法。
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
public class People {
//AutoWired注解的requored属性设置为false,说明该对象可以为null,否则不允许为空
@Autowired(required = false)
@Qualifier(value = "cat")
private Cat cat;
@Autowired
@Qualifier(value = "dog")
private Dog dog;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
}
注解开发:(spring中的注解)
属性的注入时Autowired注解自动注入,使用Qualifier指定spring上下文中的beanid
@Autowired(required = false)
@Qualifier(value = “beanid”)
java中的注解也能够使用找到对应的bean,效率比spring的注解低。
public class People {
@Resource(name = "cat")
private Cat cat;
@Resource
private Dog dog;
}
Autowired和Resource的区别:
1.都是自动装配,都可以放在属性的字段上
2.Autowired通过byType的方式实现
3.Resource通过byname实现,如果没有匹配,则通过byType匹配,两个均不匹配时,异常。
6. 注解开发
spring4之后的注解开发必须导入aop的包,aop的约束以及aop的命名空间、context的命名空间以及context的约束。
6.1 bean
全部有spring管理的对象称之为一个bean
6.2 属性的注入
//组件注解
@Component
public class UserDemo {
//spring的注解
@Value("星仔")
public String name;
}
6.3 衍生注解
@Component 组件注解,表名该类是spring中的一个bean,在web开发过程中,会按照mvc三层架构分层
dao[@Repository]
service[@Service]
controller[@Controller]
四种注解功能作用相同只是为了表示不同层之间的注解,都是将该类放入spring容器中进行装配
6.4 自动装配(之前使用过)
需要引入context约束、context命名空间
<!--开启扫描注解-->
<context:component-scan base-package="com.itxing"/>
<context:annotation-config/>
6.5 作用域
@Component //该注解表示该类是一个单例模式 @Scope("singleton") public class UserDemo { @Value("星仔") public String name; }
6.6 xml与注解比较
xml更加万能,适用的场景较多,维护简单方便
注解:维护相对复杂
一般使用:
xml用来管理bean;
注解只负责完成属性的注入;
注解需要生效必须开启注解
<context:component-scan basepackage=“”/>
<context:annotation-config/>
7. 不使用配置文件
采用java的一个配置类,配置类中有公共权限的方法向外暴露需要管理的对象。
@Configuration
//开启扫描注解
@ComponentScan("com.itxing.pojo")
@Import(SpringConfig2.class)
public class ItXingConfig {
@Bean
public Customer getCustomer(){
return new Customer();
}
}
另外一个配置类,使用@Import注解能够将两个配置类合并为一个配置类
@Configuration
public class SpringConfig2 {
}
测试类,创建上下文对象的时候不使用ClassPathXmlApplicationContext对象读取配置文件来获取。
@Test
public void test2(){
//不适用配置文件去实现
ApplicationContext context = new AnnotationConfigApplicationContext(ItXingConfig.class);
Customer custumer = (Customer)context.getBean("getCustomer");
System.out.println(custumer.getName());
}
8. 代理模式
AOP底层使用的是代理模式,代理模式分为动态代理和静态代理
代理模式中的角色:代理、被代理对象、具体的事件
被代理对象专心的完成具体的事件,具体事件之外的事件由代理对象去完成
8.1 静态代理
代理模式的优缺点
好处:可以使得真实角色的操作更加纯粹,不需要关注公共的业务
公共业务交给代理对象完成,实现类业务分工
方便集中管理
缺点:一个真实角色有一个代理对象,代码量会加倍
1.公共接口
public interface Rent {
public void rent();
}
2.被代理对象
public class Host implements Rent{
@Override
public void rent() {
System.out.println("出售房子");
}
}
3.代理对象
public class ProxyHome implements Rent{
private Host host;
public ProxyHome(){}
public ProxyHome(Host host){this.host = host;}
@Override
public void rent() {
seeHouse();
host.rent();
signTong();
getfare();
}
//代理对象能够处理的时间
//代理对象 看房子
public void seeHouse(){
System.out.println("中介带客户看房");
}
//代理对象 收中介费
public void getfare(){
System.out.println("中介收费");
}
//代理对象 签合同
public void signTong(){
System.out.println("中介签合同");
}
}
4.访问者
public class ClientDemo {
public static void main(String[] args) {
Host host = new Host();
ProxyHome proxy = new ProxyHome(host);
proxy.rent();
}
}
8.2 动态代理
动态代理与静态代理的角色划分一致
动态代理的代理类是动态生成的,不是直接写好的
动态代理分为两大类:基于接口的动态代理、基于类的动态代理、字节码动态代理
接口的JDK动态代理
类:cglib动态代理
字节码实现:javasist
公共代理类工具类
调用setObj设置被代理对象
使用getProxy获取一个代理对象
调用被代理对象接口中的方法
测试实例:给增、删、改、查每个方法添加日志信息。
1.接口
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
2.具体实现类
public class UserserviceImpl implements UserService {
@Override
public void add() {
System.out.println("新增用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void select() {
System.out.println("查询用户");
}
}
3.公共的代理对象工具类
public class ProxyInvocationUtil implements InvocationHandler {
//被代理对象接口
private Object obj;
public void setObj(Object obj) {
this.obj = obj;
}
//使用Proxy获取一个代理独享
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader()
,obj.getClass().getInterfaces(),this);
}
//代理对象执行的方法
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
log(method.getName());
method.invoke(obj,objects);
return null;
}
//添加日志
public void log(String msg){
System.out.println("执行的是"+msg+"方法。");
}
}
4.客户端测试
public class Client {
public static void main(String[] args) {
//具体的被代理实例
UserserviceImpl userservice = new UserserviceImpl();
//创建代理对象工具类
ProxyInvocationUtil proxyUtil = new ProxyInvocationUtil();
//设置被代理对象
proxyUtil.setObj(userservice);
//获取代理对象
UserService proxy = (UserService)proxyUtil.getProxy();
proxy.add();
}
}
动态代理的好处:
1.可以使真实角色的操作更加纯粹,专心做自己的事情
2.公共的事件交给代理对象,实现了业务分工
3.方便集中管理
4.一个动态代理类代理的是一个接口,一般就是应对的一类业务
5.一个动态代理类可以代理多个类,只要是实现了相同的接口
9. AOP
Aop中的相关名词,对于跨越应用程序多个模块的功能或方法,即与业务逻辑无关的部分,日志、安全、缓存、事务。
切面(Aspect):横切关注点,被模块化的特殊对象,一个类
通知(Advice):切面必须要完成的工作,一个类的方法
目标(Target):被通知的对象
代理(Proxy):向目标对象应用通知之后创建的对象
切入点(PointCut):切面通知执行”地点”的定义
连接点(JointPoint):与切入点匹配的执行点
引入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一 使用springAPI
主要是springAPI的接口的实现
//MethodBeforeAdvice是spring的一个接口
public class Log implements MethodBeforeAdvice {
/*
* Method method 目标的方法
* Object[] objects 参数
* Object o 目标对象
* */
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"即将被执行");
}
}
配置文件的主要关注点在于切入点的配置
<bean id="userService" class="com.itxing.service.UserServiceImpl"/>
<bean id="log" class="com.itxing.log.Log"/>
<!--配置aop-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.itxing.service.UserServiceImpl.*(..))"></aop:pointcut>
<!--通知,该对应的bean实现了spring的核心api-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
方式二 自定义类实现AOP
一个类中有业务前的逻辑方法与业务后执行的逻辑方法,从而将该方法横切进去
public class DiyLogDemo {
public void before(){
System.out.println("=========前置方法========");
}
public void after(){
System.out.println("=========后置方法========");
}
}
自定义类文件首先关注的是切面
<!--业务方法前后执行的方法实现类-->
<bean id="diy" class="com.itxing.log.DiyLogDemo"/>
<!--配置aop-->
<aop:config>
<!--在于切面的配置-->
<aop:aspect ref="diy">
<aop:pointcut id="point" expression="execution(* com.itxing.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三 使用注解实现AOP
//Aspect配置切面的注解
//Before前置通知
//After后置通知
//Around环绕通知
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.itxing.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=======执行前执行======");
}
@After("execution(* com.itxing.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=======执行后执行======");
}
@Around("execution(* com.itxing.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint pjp){
System.out.println("=======事前环绕======");
try {
pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("=======事后环绕======");
}
}
使用注解aop时在xml中只需要开启aop注解代理支持,默认使用的是jdk动态代理
<!--Spring中加入切面以及切入点的bean-->
<bean id="annotationPointCut" class="com.itxing.log.AnnotationPointCut"/>
<!--开启aop的注解支持 JDK(默认) CGLIB-->
<aop:aspectj-autoproxy/>
10 整合Mybatis
之前学习的mybatis笔记Mybatis笔记
步骤:
1.导入相关的依赖
junit
mybatis
mysql数据库
spring相关
aop织入
mybatis-spring
2.编写配置
3.测试
<dependencies>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql相关-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!--mybatis相关-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--spring相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--aop支持-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<!--spring与mybatis整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
方式一: SqlSessionTemplate
spring的配置文件中配置数据源信息(之前是在mybatis中配置)、配置SqlSessionFactory、配置SqlSessionTemplate(为了获取sqlsession)。
自己获取sqlsession的进行测试
@Test
public void test() throws IOException {
String resource = "mybatis-config.xml";
//输入流
InputStream in = Resources.getResourceAsStream(resource);
//获取SqlSessionFactory
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);
//获取sqlSession
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user:users){
System.out.println(user);
}
}
将该对象的创建都交给Spring来管理
<!--1.导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--2.配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
</bean>
<!--3.配置SqlSessionTemplate只能使用构造器进行注入,-->
<bean id="SqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
sqlSessionTemplate只能使用构造器注入
自己对接口进行实现并将其注入spring容器中,提供在创建时使用
实现类
public class UserMapperImpl implements UserMapper {
public SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}
注入配置
<bean id="userMapper" class="com.itxing.mapper.UserMapperImpl">
<property name="SqlSession" ref="SqlSession"/>
</bean>
spring管理后的测试类
@Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapperImpl userMapper = (UserMapperImpl) context.getBean("userMapper");
List<User> users = userMapper.selectUser();
users.forEach(System.out::println);
}
方式二 实现类继承SqlSessionDaoSupport
配置的不同
<!--1.导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--2.配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"></property>
<!--添加mybatis配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
</bean>
继承SqlSessionDaoSupport类,该类的方法指定返回sqlsession对象处理数据
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
注册接口的实现类
<bean id="userMapper" class="com.itxing.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
11.声明式事务
事务:一些任务同时成功或者同时失败,确保数据的完整性和一致性
事务的ACID:
原子性:操作的指令要么全部执行成功,要么全部不执行
一致性:事务的执行使数据从一个状态转换成另一个状态,但是对于整个数据的完整性保持稳定
隔离性:当多个用户并发访问数据库时,一个事务不能被其他事务所干扰,多个并发的事务之间相互隔离
持久性:当事务正确完成后,数据的修改是永久的
事务并发导致的问题(没有隔离级别)
第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖
脏读:指的是一个事务处理过程里读取了另一个未提交事务中的数据
幻读:一个事务两次查询,第二次结果包含第一次没有或已经删除的数据,造成两次读取不一致,指的是另一个事务在两次查询中修改了数据。(数据的整体)
不可重复读:一个事务两次读取同一行数据,结果不同即另一个事务在两次读取数据中更新了数据
第二类丢失更新:是不可重复读的一种特例,两个事务读取同一行数据后都进行写操作,并提交,第一个事务的改变会丢失
事务的隔离级别:从低到高
Read Uncommitted:读未提交,允许读取另一个事务的数据 (都不能保证)
Read committed:读已提交,读取已经提交的事务的数据(解决脏读问题)
Repeatable read:可重复读,读取时不允许修改数据(解决脏读、不可重复读)
Serializable:串行化,效率低下(解决幻读、脏读、不可重复读)
spring声明式事务:AOP的支持
编程式事务:需要在代码中进行事务的管理
spring中事务的七种传播特性:
特性 | 描述 |
---|---|
REQUIRED | 需要,如果没有事务就新建一个 |
SUPPORTS | 支持,有事务就按事务提交,没有事务就非事务执行 |
MANDATORY | 支持当前事务,当前没有事务就抛出异常 |
REQUIRES_NEW | 新建事务,把当前的事务挂起 |
NEVER | 非事务进行,有事务抛出异常 |
NESTED | 支持当前事务,当前有事务嵌套事务,没有事务新建一个事务 |
NOT_SUPPORTED | 非事务执行,当前事务挂起 |
spring与mybatis项目整合使用的xml管理的配置文件,其中涉及到context约束和命名空间、aop约束和命名空间、tx事务的约束的命名空间、数据源datasource、工厂sqlSessionFactory、sqlSession、事务、aop的相关配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/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
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
">
<!--导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--配置sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"></property>
<!--添加mybatis配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
</bean>
<!--配置sqlSession,可选配置,也可以使用继承类SqlSessionDaoSupport实现-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--声明式事务 使用数据库事务-->
<bean id = "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>
<!--结合aop,执行数据库操纵时自动添加事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--指定的方法配置事务-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--将事务织入-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.itxing.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
mybatis中的相关配置,简洁了很多,大多数交给spring去管理和配置
<?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核心配置文件-->
<configuration>
<!--别名管理-->
<typeAliases>
<package name="com.itxing.pojo"/>
</typeAliases>
</configuration>