Spring原码讲解
Spring核心讲解
1、Spring框架概述
Spring是轻量级的开源的JavaEE框架
Spring可以解决企业应用开发的复杂性
Spring有许多组成部分 核心IOC和AOP
IOC:控制反转 把创建对象的过程交给Spring进行管理
AOP: 面向切面 不修改源代码进行功能增强
- 方便解耦 简便开发
- AOP编程支持
- 方便unit测试
- 可以方便和其他框架进行集合
- 支持事务处理以及降低API的开发难度
2、入门案例
1、下载Spring5
2、选择地址进行下载
3、复制这个地址
下载地址https://repo.spring.io/ui/native/release/org/springframework/spring
4、源码解压
JAR包分为三类:
- .jar基本jar包
- .javadoc 文档包
- .source源代码包
这里我们主要研究四个
- Beans 对应IOC
- Core 对应IOC
- Context 对应上下文
- Expression 对应表达式
5、JAR包导入
6、使用Spring方式创建对象
6.1 测试 使用普通类进行创建对象
/*** @author* @Package com.ljx.spring5* @date 2021-09-14 15:26*/
public class User {public void add(){System.out.println("add....");}
}
User user = new User();
6.2 使用Spring方式创建
1.通过配置文件 在配置文件里面创建对象
<?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">
<!-- 配置User创建 --><bean id="user" class="com.ljx.spring5.User"></bean>
</beans>
通过这个bean实现了对象的创建
2、测试对象的编写
package com.ljx.test;import com.ljx.spring5.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author ljx* @Package com.ljx.test* @date 2021-09-14 15:45*/
public class TestSpring5 {@Testpublic void test01(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");//获取配置创建的对象 这个user是对应的bean的IdUser user = applicationContext.getBean("user", User.class);System.out.println(user);}}
测试Spring创建对象
3、IOC容器
3.1、IOC底层原理
1、什么是IOC
控制反转也可以被称为依赖注入,把对象创建和对象之间的调用过程交给Spring进行管理
为什么使用:为了降低耦合度
上面的案例就是IOC的实现
2、IOC底层原理
(1)xml解析、工厂模式、反射
原始创建对象的方式
使用IOC的过程
3、IOC接口
1、IOC思想基于IOC容器,IOC容器底层就是对象工厂
2、Spring提供了两种方式
- BeanFactory: IOC最基本的实现方式 是Spring 内部使用的接口 不提供开发使用
加载配置文件的时候是不会创建对象,只有获取对象才会去创建对象
- ApplicationContext:BeanFactory接口的子接口 一般是由开发人员进行使用的
加载配置文件的时候 就会把对象给创建出来
3、ApplicationContext实现类
- FileSystemXmlApplicationContext 指的是配置文件的带盘符的路径
- ClassPathXmlApplicationContext 指的是配置文件的类路径 也就是src的路径
4、BeanFactory接口实现类
3.2、IOC接口(BeanFactory)
IOC操作Bean管理
1、Bean管理
- Spring创建对象
- Spring属性注入
2、Bean管理的两种操作方式
(1) 基于xml配置文件的方式进行注入
(2)使用注解的方式进行注入
3、基于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">
<!-- 配置User创建 -->
<!-- bean的属性讲解id: 给一个对象起一个表示 根据这个表示可以获取对象class:属性 类的全路径-->
<!-- 没有设置参数的时候默认会执行无参的构建 --><bean id="user" class="com.ljx.spring5.User"></bean>
<!-- 基于XML的方式注入属性1、DI 依赖注入 就是注入属性 是IOC的一种具体实现 前提是在对象已经创建的基础上
-->
</beans>
3.3、IOC的Bean管理(基于XML)
1、在配置文件中先实现对象创建
2、注入属性
通过set方法注入
<?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">
<!-- 配置User创建 -->
<!-- bean的属性讲解id: 给一个对象起一个表示 根据这个表示可以获取对象class:属性 类的全路径-->
<!-- 没有设置参数的时候默认会执行无参的构建 --><bean id="user" class="com.ljx.spring5.User"><property name="userName" value="ljx"/></bean>
<!-- 基于XML的方式注入属性1、DI 依赖注入 就是注入属性 是IOC的一种具体实现 前提是在对象已经创建的基础上
name 是属性名称 value是设置的值
-->
</beans>
前提是对象里面要有Set方法 注入属性就相当于设置对应的Set方法
编写对应的测试类
@Testpublic void test02(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");//获取配置创建的对象 这个user是对应的bean的IdUser user = applicationContext.getBean("user", User.class);String userName = user.getUserName();System.out.println(userName);}
通过构造方法注入
<?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">
<!-- 配置User创建 -->
<!-- bean的属性讲解id: 给一个对象起一个表示 根据这个表示可以获取对象class:属性 类的全路径-->
<!-- 没有设置参数的时候默认会执行无参的构建 --><bean id="user" class="com.ljx.spring5.User">
<!-- <property name="userName" value="ljx"/>--><constructor-arg name="userName" value="ljx"/></bean>
<!-- 基于XML的方式注入属性1、DI 依赖注入 就是注入属性 是IOC的一种具体实现 前提是在对象已经创建的基础上
-->
</beans>
编写对应测试类
@Testpublic void test02(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");//获取配置创建的对象 这个user是对应的bean的IdUser user = applicationContext.getBean("user", User.class);String userName = user.getUserName();System.out.println(userName);}
注入其他类型的属性
<?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"><!-- 配置User创建 --><!-- bean的属性讲解id: 给一个对象起一个表示 根据这个表示可以获取对象class:属性 类的全路径--><!-- 没有设置参数的时候默认会执行无参的构建 --><bean id="user" class="com.ljx.spring5.User"><property name="userName" value="李加喜"/><property name="address"><!-- 为属性设置空值 --><null/></property><!-- 属性值设置了特殊符号 --><!-- <constructor-arg name="userName" value="李加喜"/>--><property name="userId"><value><![CDATA[<<用户Id]>>]]></value></property></bean><!-- 基于XML的方式注入属性1、DI 依赖注入 就是注入属性 是IOC的一种具体实现 前提是在对象已经创建的基础上-->
</beans>
注入属性-外部bean
创建dao和daoImpl
public interface UserDao {/*** 修改*/void update();
}
public class UserDaoImpl implements UserDao {@Overridepublic void update() {System.out.println("service add ....");}
}
创建service里面引入dao
public class UserService {// 创建UserDao类型的属性 生成对应的Set方法private UserDao userDao1;public UserDao getUserDao1() {return userDao1;}public void setUserDao1(UserDao userDao1) {this.userDao1 = userDao1;}public void add(){System.out.println("测试 add...");userDao1.update();}
}
配置文件里面进行引入
<?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"><!-- 创建service和dao对象 --><bean id="userService" class="com.ljx.service.UserService"><!-- name是属性的名字 res是类里面属性的名称 --><property name="userDao1" ref="userDao"/></bean><!----这里引入的是接口的实现类接口是不允许进行实例化的----!><bean id="userDao" class="com.ljx.dao.UserDaoImpl"/>
</beans>
FactoryBean
Spring里面有两种Bean 一种普通的一种工厂Bean(FactoryBean)
- 普通Bean定义的类型就是自己定义的类型
- 工厂Bean 在配置文件里面定义的bean类型可以和返回类型不一样
第一步:创建一个类 让这个类作为工厂bean 实现接口FactoryBean
第二步:实现接口里面的方法 在实现的方法中定义返回的bean类型
package com.ljx.factorybean;import com.ljx.spring5.User;
import org.springframework.beans.factory.FactoryBean;/*** @author* @Package com.ljx.factorybean* @date 2021-09-16 15:08*/
public class MyBean implements FactoryBean<User> {/*** 定义返回的Bean* @return* @throws Exception*/@Overridepublic User getObject() throws Exception {User user = new User();user.setUserName("ljx");return user;}@Overridepublic Class<?> getObjectType() {return null;}
}
配置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="myBean" class="com.ljx.factorybean.MyBean"></bean>
</beans>
编写测试类
@Testpublic void testCollection() {// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");//获取配置创建的对象 这个user是对应的bean的IdUser myBean = applicationContext.getBean("myBean", User.class);System.out.println(myBean);}
bean的作用域
可以设置 bean是单实例还是多实例
Spring默认是单实例的
@Testpublic void testBook(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");//获取配置创建的对象 这个user是对应的bean的IdBook book = applicationContext.getBean("book", Book.class);System.out.println(book);Book book1 = applicationContext.getBean("book", Book.class);System.out.println(book.equals(book1));}
返回结果是true 所以默认是单实例的
设置对象是多实例对象
设置配置文件
<?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="myBean" class="com.ljx.factorybean.MyBean"></bean><bean id="book" class="com.ljx.factorybean.Book" scope="prototype"></bean>
</beans>
prototype:是多实例
singleton:是单实例
编写测试文件
@Testpublic void testBook(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");//获取配置创建的对象 这个user是对应的bean的IdBook book = applicationContext.getBean("book", Book.class);System.out.println(book);Book book1 = applicationContext.getBean("book", Book.class);System.out.println(book.equals(book1));}
是false 说明当前是多实例的
bean的生命周期
1、生命周期
从对象的创建到对象的销毁的过程
2、bean的生命周期
- 通过构造器创建bean的实例(无参构造)
- 为bean的属性设置值和对其他bean进行引用(调用set方法)
- 调用bean的初始化方法
- bean可以进行获取了
- 当容器管理之后 调用bean的销毁方法
package com.ljx.bean;/*** @author 李加喜* @Package com.ljx.bean* @date 2021-09-16 21:48*/
public class Orders {private String name;public String getName() {return name;}public void setName(String name) {System.out.println("第二步 调用set设置值");this.name = name;}public Orders(String name) {this.name = name;}public Orders(){System.out.println("第一步 执行无参数构造 构造创建bean");}// 创建初始化方法public void initMethod(){System.out.println("第三步调用自定义初始化方法");}/*** 执行销毁方法*/public void destroyMethod(){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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="orders" class="com.ljx.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"><property name="name" value="名称"/></bean>
</beans>
测试
@Testpublic void testOrders(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");//获取配置创建的对象 这个user是对应的bean的IdOrders orders = applicationContext.getBean("orders", Orders.class);System.out.println("第四步 获取bean实例对象");System.out.println(orders);// 手动销毁applicationContext.close();}
结果
第一步 执行无参数构造 构造创建bean
第二步 调用set设置值
第三步调用自定义初始化方法
第四步 获取bean实例对象
com.ljx.bean.Orders@64f6106c
第五步 调用销毁方法
3、bean的后置处理器 (总共七步)
- 通过构造器创建bean的实例(无参构造)
- 为bean的属性设置值和对其他bean进行引用(调用set方法)
- 将bean的实例传递给后置处理器(postProcessBeforeInitialization)
- 调用bean的初始化方法
- 将bean的实例传递给后置处理器(postProcessAfterInitialization)
- bean可以进行获取了
- 当容器管理之后 调用bean的销毁方法
创建类 实现接口BeanPostProcessor 创建后置处理器
public class MyBeanPost implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化之前 postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("初始化之后postProcessAfterInitialization");return 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="orders" class="com.ljx.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"><property name="name" value="名称"/></bean><bean id="beanPost" class="com.ljx.bean.MyBeanPost"></bean>
</beans>
这样容器里面的所有Bean都会执行这个方法
基于xml的自动装配
- 根据指定的装配规则(属性名称/属性类型) Spring自动将匹配的属性值进行注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="orders" class="com.ljx.bean.Orders" init-method="initMethod"destroy-method="destroyMethod"><property name="name" value="名称"/></bean><bean id="beanPost" class="com.ljx.bean.MyBeanPost"></bean><!-- byName 根据属性名进行注入 注入的值的bean的id要和类的属性名称一致byType 根据类型进行注入 找到属性的类型在管理的对象里面进行注入 注意 如果出现相同类型的两个bean 就会出去错误 --><bean id="emp" class="com.ljx.bean.Emp" autowire="byName"><!-- <property name="dept" ref="dept"/>--></bean><bean id="dept" class="com.ljx.bean.Dept"></bean>
</beans>
类型
package com.ljx.bean;import java.util.Date;/*** @author 李加喜* @Package com.ljx.bean* @date 2021-09-16 22:59*/
public class Emp {private Dept dept;public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}@Overridepublic String toString() {return "Emp{" +"dept=" + dept +'}';}public void test(){System.out.println(dept);}
}public class Dept {@Overridepublic String toString() {return "Dept{}";}}
package com.ljx.bean;import java.util.Date;/*** @author 李加喜* @Package com.ljx.bean* @date 2021-09-16 22:59*/
public class Emp {private Dept dept;public Dept getDept() {return dept;}public void setDept(Dept dept) {this.dept = dept;}@Overridepublic String toString() {return "Emp{" +"dept=" + dept +'}';}public void test(){System.out.println(dept);}
}public class Dept {@Overridepublic String toString() {return "Dept{}";}}
3.4、IOC的Bean管理()基于注解
1、创建对象提供的注解
@Service
@Component
@Controller
@Reposibility
上面的四个注解都可以用来创建bean实例
2、基于注解实现对象的创建
第一步、引入依赖
spring-aop-5.2.6.RELEASE.jar引入这个jar包
第二步、开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><bean id="dept" class="com.ljx.bean.Dept"></bean><!-- 开启组件扫描 --><context:component-scan base-package="com.ljx"/>
</beans>
第三步 创建类 在类上面添加创建对象的注解
@Component(value="可以是自定义名称 默认是类的首字母小写")
public class Pfm {public void add(){System.out.println("add");}
}
第四步 测试
@Testpublic void pfm(){// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");//获取配置创建的对象 这个user是对应的bean的IdPfm pfm = applicationContext.getBean("pfm", Pfm.class);pfm.add();}
3、组件扫描的细节配置
<!-- 开启组件扫描--><context:component-scan base-package="com.ljx"/>
<!-- use-default-filters="false" 表示不使用默认的filter 自己配置filtercontext:include-filter 设置需要扫描那些配置类 这类的配置只会扫描带有Controller的类
--><context:component-scan base-package="com.ljx" use-default-filters="false"><context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/></context:component-scan>
<!--只扫描包含Component的类--><context:component-scan base-package="com.ljx" ><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/></context:component-scan>
4、基于注解的方式实现属性注入
1、@AutoWired 根据类型进行注入
2、@Qualifier 根据属性名称注入
3、@Resource 可以根据类型也可以根据名称
4、@Value 根据普通类型注入
public interface UserDao {/*** 修改*/void update();
}
@Repository
public class UserDaoImpl implements UserDao {@Overridepublic void update() {System.out.println("service add ....");}
}
@Service
public class UserService {@Autowiredprivate UserDao userDao;public void add(){System.out.println("测试 add...");userDao.update();}
}@Testpublic void setUserService(){ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");//获取配置创建的对象 这个user是对应的bean的IdUserService userService = applicationContext.getBean("userService", UserService.class);userService.add();}
开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 开启组件扫描--><context:component-scan base-package="com.ljx"/>
</beans>
使用@Qualifier
指定名称注入 防止接口有多个实现类
@Repository(value = "userDao1")
public class UserDaoImpl implements UserDao {@Overridepublic void update() {System.out.println("service add ....");}
}
@Service
public class UserService {//@Autowired@Qualifier(value = "userDao1")private UserDao userDao;public void add(){System.out.println("测试 add...");userDao.update();}
}
完全注解开发
1、配置类
@Configuration
@ComponentScan(basePackages = {"com.ljx"})
public class SpringConfig {}
取代了xml注解
2、测试
@Testpublic void config(){AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);UserService userService = context.getBean("userService", UserService.class);userService.add();}
4、AOP
面向切面编程(面向方面编程)
降低耦合度 提高开发
主要就是方便在已有的功能上面添加新的功能 降低代码的耦合度
AOP底层使用的动态代理的方式进行实现的
- 有接口的动态代码 JDK的动态代理
- 没有接口的动态代理 CGLIB动态代理
1、创建Interface接口和接口的实现类
public interface UserDao {public Integer add(int a,int b);public void update(String id);
}
public class UserDaoImpl implements UserDao {@Overridepublic Integer add(int a, int b) {return a+b;}@Overridepublic void update(String id) {System.out.println(id);}
}
public class JDKProxy {public static void main(String[] args) {UserDaoImpl userDao = new UserDaoImpl();// 创建接口实现类的代理对象Class[] interfaces = {UserDao.class};UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,new UserDaoProxy(userDao));Integer add = dao.add(1, 2);System.out.println(add);}
}class UserDaoProxy implements InvocationHandler {// 需要把谁的代理对象要把 对象传递过来private Object object;public UserDaoProxy(Object object){this.object=object;}// 增强逻辑@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 方法之前System.out.println("方法之前执行"+method.getName()+"传递的参数是"+ Arrays.toString(args));// 被增强的方法执行Object res = method.invoke(object, args);System.out.println(res);// 方法之后System.out.println("方法之后执行"+object);return res;}
}
结果
方法之前执行add传递的参数是[1, 2]
3
方法之后执行com.ljx.UserDaoImpl@355da254
3
AOP专业术语
连接点 类里面那些方法可以被增强 方法被称为连接点
切入点 实际被增强的方法
通知(增强) 实际增强的逻辑部分就是通知(前置 后置 环绕 异常 最终)
切面 将通知应用到切入点的过程
2、AOP的JDK动态代理案例
使用newProxyInstance方法,方法有三个参数 (类加载器、增强方法所在的类 这个类所在的接口 支持多个接口、实现InvocationHandle接口 创建代理对象写增强方法)
3、Spring框架基本使用Aspectj实现AOP操作
一般和Spring框架一起使用实现Aop的功能 一般开发中是基于注解的方式进行使用
4、在项目中引入AOP的依赖
5、切入点的表达式
语法结构:execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])
execution(*com.ljx.dao.BookDao.add(…))
execution(*com.ljx.dao.BookDao. * (…)) BookDao下面的左右方法
execution(*com.ljx.dao. * . * (…)) 下面的所有的方法
6、使用注解的方式实现Aop
在增强类里面创建方法 让不同的代表不同的通知类型
- 开启注解扫描
- 使用注解创建User和UserProxy
- 在增强类上面添加注解@Aspect
- 在Spring配置文件中开启生成代理对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
"><context:component-scan base-package="com.ljx"/><!-- 开启Aspect生成代理对象 扫描的类上面有@Aspect就生成代理对象--><aop:aspectj-autoproxy/>
</beans>
设置类
// 被增强类
@Component
public class User {public void add() {System.out.println("add...");}
}
@Component
@Aspect
public class UserProxy {// 在public void before() {System.out.println("before");}
}
配置不同类型的通知
在增强类里面在作为通知类型的注解使用切入点表达式配置
package com.ljx.aopanno;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;// 前置通知 增强类
@Component
@Aspect
public class UserProxy {/*** 相同切入点抽取*/@Pointcut(value = "execution(* com.ljx.aopanno.User.add(..))")public void pointDemo(){}/*** 前置通知*/@Before(value = "pointDemo()")public void before() {System.out.println("before");}/*** 后置通知*/@After(value = "pointDemo()")public void after(){System.out.println("after");}/*** 返回值之后执行*/@AfterReturning(value = "pointDemo()")public void afterReturning(){System.out.println("afterReturning");}/*** 出现异常的时候进行通知*/@AfterThrowing(value = "pointDemo()")public void afterThrowing(){System.out.println("afterThrowing");}/*** 环绕通知*/@Around(value = "pointDemo()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("around前");// 执行增强的方法proceedingJoinPoint.proceed();System.out.println("around后");return new Object();}
}
有多个增强类对同一个方法进行增强 设置优先级
在增强类里面添加注解**@Order**(数字类型的值) 数字类型值越小优先级越高
5、事务操作
事务是数据库操作最基本的单元 逻辑上的一组操作 要么都成功要么都失败
四大特性: ACID
- 原子性 要么都成功呢要么都失败
- 一致性 操作前后数据不变
- 隔离性 多事务操作的时候不会产生影响
- 持久性 表中数据真正发生改变
事务的操作
基本使用的声明式事务(基于AOP的)
事务的传播行为:
- 多事务方法直接进行调用,这个过程中事务是如何进行管理的
- REQUIRED 如果事务在运行 当前方法就在这个事务内运行 否则就启动一个新的事务
- REQUIRED_NEW 当前的方法必须启动新的事务 并在他自己的事务内运行 如果有事务正在运行 应该将他挂起
- SUPPORTS 如果事务在运行 当前的方法就在这个事务内运行 否则可以不运行在事务内
- NOT_SUPPORTS 当前方法不应该在事务中运行 如果有运行的事务 将他挂起
- MANDATORY 当前的方法必须运行在事务内部 如果没有正在运行的事务 就抛出异常
- NEVER 当前的方法不应该运行在事务中 如果有运行的事务 就抛出异常
- NESTED 如果有事务在运行 当前的方法就应该在这个事务的嵌套事内运行 否则就启动一个新的事务
事务隔离级别:
- 多事务之间不会产生影响 不考虑隔离性产生很多问题 比如
- 脏读:多个事务之间一个未提交的事务获取了另一个未提交的事务数据
举例:A将数据从1改为2 不进行读取的时候获取的是2 但是这个时候A进行了回滚 那么B读的数据就是脏数据
- 不可重复读:一个未提交的事务读取了另一个事务修改的数据
A和B都在读取数据 A将数据从100改为200 然后事务进行了提交 但是B在读取数据的时候没有提交数据 导致B一致获取的A提交的数据 导致可能每一次读取的数据都会发生改变
- 幻读 一个未提交的事务获取了另一个提交事务的数据
设置事务隔离级别
- READ UNCOMMITTED(读未提交) 脏读 不可重复读 幻读
- READ COMMINTED(读已提交) 不可重复读 幻读
- READATABLE READ(可重复度) 幻读
- SERIALIZABLE(串行化)
Spring原码讲解相关推荐
- Spring源码讲解之 getBean 方法
getBean 方法是用来进行获取 bean 实例对象的,是我们进行 spring 程序开发从 bean 工厂获取结果的.那这个方法的执行过程是什么样的,下面我们就去揭开它的面纱. 样例代码 Appl ...
- Spring 源码讲解:bean 的创建流程 - 公开课笔记
Spring 官网所有版本下载 官网文档中的链接找起来比较麻烦.我们可以通过下面这个链接: https://repo.spring.io/release/org/springframework/spr ...
- 原码、反码、补码知识详细讲解(此作者是我找到的讲的最细最明白的一个)
本篇文章讲解了计算机的原码, 反码和补码. 并且进行了深入探求了为何要使用反码和补码, 以及更进一步的论证了为何可以用反码, 补码的加法计算原码的减法. 论证部分如有不对的地方请各位牛人帮忙指正! 希 ...
- python基础运算符讲解(原码、补码、反码)
Python 1.Python基础 python是一门弱类型(PHP,python,Javascript,ruby)的语言,变量的类型不固定,当输入值是什么类型时,变量就会变成什么类型. 那什么是强数 ...
- (原码、反码和补码)例子byte的详细讲解
<此处转载> 1.原码.反码和补码定义 1.原码 将最高位作为符号位(以0代表正,1代表负),其余各位代表数值本身的绝对值(以二进制表示). 为了简单起见,我们用1个字节来表示一个整数. ...
- JavaSE笔记(09) —— 进制之间的转换以及原码,反码,补码的讲解
进制的介绍 对于整数,有四种表示方式: 二进制:0,1 ,满 2 进 1.以 0b 或 0B 开头: 十进制:0-9 ,满 10 进 1: 八进制:0-7 ,满 8 进 1. 以数字 0 开头表示: ...
- 数据结构与算法--二进制详解 Python二进制算法详解 史上最详细的二进制讲解 彻底搞懂原码、反码、补码 Python的负数二进制表示形式
阅读目录 原码.反码.补码 机器数 和 真值 原码.反码.补码的基础 Python中负数的处理 负数的补码如何转成十进制 位运算符 和 移位运算符 基本概述 妙用 二进制涉及的算法 原码.反码.补码 ...
- Spring源码深度解析(郝佳)-学习-Spring Boot体系原理
Spring Boot是由Pivotal团队提供的全新框架,其设计目的用来简化新Spring应用初始化搭建以及开发过程,该框架使用了我写的方式进行配置,从而开发人员不再需要定义样板化的配置,通过这 ...
- spring源码扩展点与实战(二)
在上一篇文章<spring源码扩展点>,我们简单的介绍了 spring 的几个常用扩展点,了解了 BeanPostProcessor, BeanFactoryPostProcessor, ...
最新文章
- 豆瓣评分9.4,跪着推荐这本密码学入门第一书
- Java-P: 2_3,类成员具有的控制修饰符
- python和java哪个-Python和Java两门编程语言,学习哪个更好?
- 树莓派进阶之路 (009) - 树莓派ftp脚本(原创)
- VS.NET 2005真是太好用了!
- 斗地主案例的需求分析
- 大数据量生成工具源代码(Delphi)
- graph driver-device mapper-04libdevmapper基本操作
- codeforces 842 D. Vitya and Strange Lesson(01字典树+思维+贪心)
- Orleans 知多少 | 2. 核心概念一览
- php 做更新进度条,PHP exec()后更新Bootstrap进度条
- DHCP服务器--红色箭头
- 用websocket技术开发的web聊天系统
- 表单设置默认值_你还不知道表单怎么设计吗?看这里!
- web前端基础(08html5新标签)
- vscode快捷键:多行同时输入
- matlab表格三维柱状图,excel制作四维数据表格-excel三维柱形图 ,请问如何根据excel表格中的数据......
- 【转】UML的9种图例解析
- 分享一个外泌体数据库
- vue 引入萤石视频
热门文章
- WordPress主题下载Sparkling个人简洁博客主题
- 盟军敢死队开发工具箱
- R语言使用as.Date函数把dataframe中的多个数据列(multiple columns)从字符串转化到日期类型
- python的pptx库_python学习之python-pptx
- Skynet基础入门例子详解(7)
- 解决win + R 键运行窗口中无历史记录
- element表格中的合并行或列
- alfafile中转站免费_中转站全集免费在线观看-手机看中转站HD完整版 - 穷TV_院线大片影视大全...
- qt android 中文乱码,Qt与MSVC中文乱码问题的解决方案
- Linux之配置Yum软件仓库