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 {
@Test
public void test01(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象 这个user是对应的bean的Id
User 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方法
编写对应的测试类
@Test
public void test02(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象 这个user是对应的bean的Id
User 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>
编写对应测试类
@Test
public void test02(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
BeanFactory applicationContext=new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象 这个user是对应的bean的Id
User 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 {
@Override
public 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
*/
@Override
public User getObject() throws Exception {
User user = new User();
user.setUserName("ljx");
return user;
}
@Override
public 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>
编写测试类
@Test
public void testCollection() {
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
//获取配置创建的对象 这个user是对应的bean的Id
User myBean = applicationContext.getBean("myBean", User.class);
System.out.println(myBean);
}
bean的作用域
可以设置 bean是单实例还是多实例
Spring默认是单实例的
@Test
public void testBook(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
//获取配置创建的对象 这个user是对应的bean的Id
Book 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:是单实例
编写测试文件
@Test
public void testBook(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
BeanFactory applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
//获取配置创建的对象 这个user是对应的bean的Id
Book 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>
测试
@Test
public void testOrders(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");
//获取配置创建的对象 这个user是对应的bean的Id
Orders 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 {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("初始化之前 postProcessBeforeInitialization");
return bean;
}
@Override
public 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;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void test(){
System.out.println(dept);
}
}
public class Dept {
@Override
public 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;
}
@Override
public String toString() {
return "Emp{" +
"dept=" + dept +
'}';
}
public void test(){
System.out.println(dept);
}
}
public class Dept {
@Override
public 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");
}
}
第四步 测试
@Test
public void pfm(){
// 加载Spring的配置文件 在src下直接写名称 不在的话需要写具体路径
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean4.xml");
//获取配置创建的对象 这个user是对应的bean的Id
Pfm pfm = applicationContext.getBean("pfm", Pfm.class);
pfm.add();
}
3、组件扫描的细节配置
<!-- 开启组件扫描-->
<context:component-scan base-package="com.ljx"/>
<!-- use-default-filters="false" 表示不使用默认的filter 自己配置filter
context: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 {
@Override
public void update() {
System.out.println("service add ....");
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add(){
System.out.println("测试 add...");
userDao.update();
}
}
@Test
public void setUserService(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean5.xml");
//获取配置创建的对象 这个user是对应的bean的Id
UserService 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 {
@Override
public 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、测试
@Test
public 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 {
@Override
public Integer add(int a, int b) {
return a+b;
}
@Override
public 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;
}
// 增强逻辑
@Override
public 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/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
http://www.springframework.org/schema/aop
http://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(串行化)