Spring5
Spring5课程介绍
1. IOC容器;
2. AOP面向切面编程;
3. 声明事务;
4. 注解的方式启动对我们后期学习SpringBoot有非常大帮助;
5. 整合SpringMVC 和Mybatis;
6. Spring5新特性;
JDK最低版本要求1.8
Spring概念
Spring是一个JavaEE开源的轻量级别的框架,可以解决我们企业开发中遇到的难题,
能够让编码变的更加简单,核心组件IOC容器和Aop面向切面编程。
1. IOC 控制反转:把整个对象创建的过程,统一交给我们SpringIOC容器来实现管理,底层使用反射+工厂模式实现。
2. Aop面向切面编程:对我们功能(方法)前后实现增强,比如打印日志、事务原理、权限管理,底层是基于动态代理模式实现的。
减少到我们的代码的冗余性问题。
Spring优势
1. 方法的解耦,简化开发;
2. Aop技术的支持;
3. 提供声明事务支持
4. Junit单元测试
5. 方便整合其他框架(Mybatis、SpringMVC、SpringBoot、SpringCloud、Redis等)
6. 降低我们的JavaEEapi开发使用的难度(Spring对很多复杂的api接口实现了封装)
Spring与SpringBoot关系
SpringBoot直接采用注解化的方式启动,底层会依赖于Spring/SpringMVC注解方式启动。
总结:SpringBoot底层基于Spring/SpringMVC注解化方式实现包装。
比如:
1.@RestController
2.@ComponentScan("com.mayikt.aop")
3. @Configuration
4. @Component
5. @Scheduled
6. @Value
7. @Bean
SpringIOC底层容器原理
Spring框架快速入门
Spring | Home spring的官网
Spring官方下载依赖jar包地址:
本次课以idea 构建maven项目方式讲解;
javadoc Api文档的介绍
Sources jar的源代码 .java
直接命名为.jar包的格式 就是class文件。
七大核心模块
Test
对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.
Core Container
Spring的核心组件, 包含了Spring框架最基本的支撑.
Beans, 对应spring-beans.jar. Spring进行对象管理时依赖的jar包.
Core, 对应spring-core.jar, Spring核心jar包.
Context, 对应spring-context.jar, Spring容器上下文对象.
SpEL, 对应spring-expression.jar, Spring表达式语言.
AOP
面向切面编程, 对应spring-aop.jar.
Data Access
Spring对数据访问层的封装
JDBC, 对应spring-jdbc.jar. Spring对jdbc的封装, 当需要使用spring连接数据库时使用. spring-jdbc.jar需要依赖spring-tx.jar.
Transactions, 对应spring-tx.jar. 事务管理
ORM, 对应spring-orm.jar. spring整合第三方orm框架需要使用的jar包, 例如Hibernate框架.
Web
Spring对javax下的接口或类做的扩展功能.
spring-web.jar, 对Servlet, filter, Listener等做的增强.
spring-webmvc.jar, 实际上就是SpringMVC框架. 需要依赖spring环境和spring-web.jar.
Spring Core
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
Maven依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring-Beans
这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring Context
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring-Expression
模块提供了一个强大的表达式语言,用于在运行时查询和处理对象图。该语言支持设置和获取属性值;属性赋值,方法调用,访问数组的内容,收集和索引器,逻辑和算术运算,命名变量,并从Spring的IOC容器的名字对象检索,它也支持列表选择和投影以及常见的列表聚合。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring AOP
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
spring-transaction
以前是在这里org.springframework.transaction
为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring ORM
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
Spring Web MVC
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
项目构建
Maven依赖
<dependencies>
<!--
这个jar 文件包含Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。
外部依赖Commons Logging, (Log4J)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--
这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。
外部依赖spring-core,(CGLIB)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
<!--
这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。
外部依赖spring-beans, (spring-aop)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
</dependencies>
创建spring.xml文件
<?xml version="1.0" encoding="UTF-8"?> |
获取Bean对象
// new UserEntity() |
SpringIOC
IOC容器底层实现原理;
1.IOC容器中非常核心的接口 BeanFactory
BeanFactory
Bean对象 Factory工厂
2.IOC容器基本的概念:控制反转
把对象的创建过程与使用统一都交给我们的Spring来进行原理。
不需要开发者自己去new对象
3. IOC容器底层实现技术:反射技术、解析xml、工厂模式
4. IOC作用 降低我们代码的耦合度。
创建对象的方式有那些:
1. 单独new方式---耦合度太高了
每次单独new对象,没有实现统一管理对象,如果后期userDao的名称信息发生变化的情况下,需要改变的引用地方比较多,耦合度太高。
2. 工厂模式---降低我们耦合度
概念:统一的管理和维护我们每个对象创建与使用的过程。
不需要自己new对象。
3. 反射的方式
降低代码的-耦合度
Com.mayikt.dao---数据库访问层;
Com.mayikt.service---业务逻辑层;
业务逻辑层调用到数据库访问层
反射创建对象
SpringIOC容器底层实现原理:
反射+工厂模式+解析xml技术实现
1.使用解析xml技术 解析spring.xml配置文件;
2.获取<bean id=”” class=””/> 类的完整路径地址
3.使用到反射技术初始化对象
4.需要使用工厂模式封装初始化对象
IOC核心的接口
1. IOC的核心思想底层基于反射+工厂模式实现
2. Spring提供IOC容器实现两种方式:
2.1 BeanFactory IOC容器基本的实现,是spring内部自己使用的接口,不提供给开发者使用。
加载配置文件过程的时候,不会创建对象,当我们在获取对象的时候才会获取创建对象。
2.2 ApplicationContext BeanFactory 接口的子接口,提供更多的强大功能,适合于开发者使用。
当我们在加载配置文件的过程中,就会将配置文件中的对象创建。
在做服务器端开发的时候,使用ApplicationContext 比较多,因为所有bean初始化操作在项目启动完成之前都已经初始化了。
ApplicationContext主要实现类
ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件
ConfigurableApplicationContext 是ApplicationContext的子接口,包含一些扩展方法
refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。所以要关闭ApplicationContext需要new此接口的对象调用close()方法
WebApplicationContext 专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作
SpringBean的注入方式
创建对象和set方法注入属性
1. 什么是Bean管理
使用spring创建对象
使用spring注入属性
2. Bean的管理有两种方式
1. 基于XML方式配置
基于XML方式创建对象
<bean id="userEntity" class="com.mayikt.entity.UserEntity"></bean>
在spring的配置文件中,会配置一个bean标签,注入bean的信息 创建bean对象
Id:获取bean对象 唯一bean对象的名称; bean的名称不允许重复
Class属性: 类的完整路径地址(类名称+包名称)
默认底层使用反射技术执行无参数构造函数
2. 基于xml方式注入属性
DI 依赖注入: 对象的属性注入值; (spring实现)
1. 第一种实现方式:基于对象属性set方法实现
<bean id="bookEntity" class="com.mayikt.entity.BookEntity"> 在Bean标签下 在定义一个属性<property>标签 Name:类中的属性名称 Value:需要注入属性值 |
有参构造函数注入属性
实例类
public class OrderEntity { |
Xml配置文件
<bean id="orderEntity" class="com.mayikt.entity.OrderEntity"> |
<constructor-arg name 指定参数列表名称
<constructor-arg index 指定参数列表索引
p名称空间注入
1. Xml头部引入P标签:
<?xml version="1.0" encoding="UTF-8"?> |
2. 使用p标签注入属性:
|
使用p标签为属性注入值:调用set方法注入值
注入空值和特殊符号
注入空值属性
<bean id="bookEntity2" class="com.mayikt.entity.BookEntity"> |
注入特殊符号
转移注入方式
<< 转移为:<<
>>转移为:>>
<bean id="bookEntity3" class="com.mayikt.entity.BookEntity"> |
Cdata注入方式
<![CDATA[<<>>]]>
<bean id="bookEntity4" class="com.mayikt.entity.BookEntity"> |
注入属性外部bean
Com.mayikt.controller---控制层
Com.mayikt.service----业务逻辑层
MemberService ##new MemberDao().
Com.mayikt.dao----数据库访问层
MemberDao----
Com.mayikt.service
调用:memberService
Com.mayikt.dao
MemberDaoImpl
public class MemberDaoImpl implements MemberDao { import com.mayikt.dao.MemberDao; <?xml version="1.0" encoding="UTF-8"?> |
注入内部bean
1. 数据库表一对多或者一对一的关系
2. 部门--n多个员工 一对多
3. 站在员工角度考虑员工属于那个部门
4. 站在部门的角度考虑部门下n多个员工
1. 在数据库中表中有一对一一对多的关系;
2. 一对多关系;部门与员工 一个部门会有多个员工 一个员工属于一个部门;
3. 实体类之间表示一对多的关系;
实体类员工对象
public class EmpEntity { |
部门对象
public class DeptEntity { |
Xml相关配置
|
注入级联赋值
写法1
<bean id="empEntity" class="com.mayikt.entity.EmpEntity"> |
写法2
<bean id="empEntity" class="com.mayikt.entity.EmpEntity"> |
注意:需要在员工实体类新增:deptEntity get方法。
注入集合类型属性
1. 注入数组类型
2. 注入list集合类型
3. 注入Map集合类型属性
4. 注入set集合属性
实体类
|
配置文件
<bean id="stuEntity" class="com.mayikt.entity.StuEntity"> |
集合类型为对象
private List<CourseEntity> courses; public void setCourses(List<CourseEntity> courses) { public class CourseEntity { |
<?xml version="1.0" encoding="UTF-8"?> |
集合注入部分提取公共
1. 需要先引入一个util名称空间
<?xml version="1.0" encoding="UTF-8"?> |
2. 使用util标签 注入
<?xml version="1.0" encoding="UTF-8"?> |
IOC操作Bean的管理
1. Spring中两种类型bean,一种是为普通的bean,另外一种是工厂bean
FactoryBean
2. 普通Bean:在配置文件中定义什么类型与返回的类型需一致;
3. 工厂Bean:在配置文件中定义Bean类型与返回类型可以不一致;
创建一个类,这个类是为工厂Bean,实现FactoryBean接口
import com.mayikt.entity.UserEntity; |
public static void main(String[] args) { |
Spring的工厂Bean
SpringBean的作用域
什么是作用域?
设定bean作用域是为单例还是多例
作用域单例与多例有什么区别呢?
1. 单例的作用域:每次在调用getbean方法获取对象都是为同一个对象;
2. 多例的作用域:每次在调用getbean方法获取对象都是一个
新的对象。
注意:在spring默认的情况下,bean的作用域就是为单例 节约服务器内存。
单例:
在同一个jvm中,该bean对象只会创建一次。
多例:
在同一个jvm中,该bean对象可以被创建多次。
设定对象单例还是多例
在spring的默认的情况下,springbean的作用域为单例。
1.单例就是每次获取bean都是同一个对象;
2.多例就是每次获取bean都是新的一个对象;
单例:在同一个jvm中该bean只能存在一个实例;
多例子:在同一个jvm中该bean存在多个实例;
证明:如果是为单例,则两个对象地址都是一样的,
多例子对象则两个对象地址不一样。
单例配置:
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="singleton"></bean> |
默认就是为单例子;
多例配置:
<bean id="userEntity" class="com.mayikt.entity.UserEntity" scope="prototype"></bean> |
SpringBean的生命周期
简单分为:实例化→属性赋值→初始化→销毁
生命周期概念:
1. 对象的创建与销毁的过程,类似之前学习servlet生命的周期过程。
生命周期的原理:
1. 通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
2. 为bean的属性设置 (使用反射调用set方法)
3. 调用bean的初始化的方法(需要单独在类中配置初始化的方法)
4. 正常使用bean对象
5. Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)
public class MemberEntity { |
<bean id="memberEntity" class="com.mayikt.entity.MemberEntity" init-method="initMethod" destroy-method="destroyMethod"> |
import com.mayikt.entity.MemberEntity; |
Bean的后置处理器 作用提供更多的扩展功能 BeanPostProcessor
相关演示代码
|
<bean id="mayiktBeanPost" class="com.mayikt.bean.MayiktBeanPost"></bean> |
1.通过构造函数创建bean对象(默认执行无参构造函数 底层基于反射实现)
2.为bean的属性设置 (使用反射调用set方法)
3.将bean传递给后置处理器 调用初始化方法之前执行
4.调用bean的初始化的方法(需要单独在类中配置初始化的方法)
5.将bean传递给后置处理器 调用初始化方法之后执行
6.正常使用bean对象
7.Spring容器关闭,调用该类的销毁回调的方法(需要单独在类中配置销毁的方法)
后置处理器底层原理
配置多个BeanPostProcessor
|
import org.springframework.beans.BeansException; |
<!-- 后置处理器--> |
实现Ordered接口 getOrder 值越小越优先加载
SpringBean的自动装配
什么是自动装配呢
根据装配的规则(属性的名称或者属性的类型)
Spring根据装配的规则自动为属性注入值。
1. 什么是自动装配
A. 根据指定装配规则(属性名称或者属性的类型),spring自动
将匹配属性的值注入。
<?xml version="1.0" encoding="UTF-8"?> |
SpringBean的外部属性文件
SpringBean的注解形式
Spring的注解启动方式
Bean的管理操作方式
1. 基于XML方式实现
2. 基于注解方式实现
什么是注解:注解是JDK5中推出的新特性,代码的特殊标记,
格式注解名称“属性名称=属性值,属性名称=属性值”。
我们在后期学习springboot开发基本上都是使用注解,很少在使用
Xml配置的方式。
注解可以使用在类、方法、属性、上面。
使用注解的目的,简化xml的配置方式。
Spring提供的常用注解
1. @Component 将对象注入Spring容器中
2. @Service 注入业务逻辑对象
3. @Controller 控制器类
4. @Repository 注入dao对象
5. 以上该四个注解底层都是基于@Component注解封装的,只是区分用于
在不同的场景下。
注解的使用方式
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(); |
SpringBean的AOP
AOP基本的概念
AOP(Aspect Oriented Programming)是一种面向切面的编程思想。面向切面编程是将程序抽象成各个切面,即解剖对象的内部,将那些影响了多个类的公共行为抽取到一个可重用模块里,减少系统的重复代码,降低模块间的耦合度,增强代码的可操作性和可维护性。
AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、事务处理、增强处理。
简单理解:
Aop面向切面编程,在方法之前和之后实现处理 应用场景在于:日志打印、事务实现、安全、权限控制、自定义注解等。
因为AOP可以解决我们程序上的代码冗余问题
AOP 底层基于 代理设计模式封装
代理设计模式 静态代理与动态代理
动态代理 jdk动态代理与 cglib动态代理
通俗易懂 aop 在我们的目标方法之前和之后 处理的操作
开启事务
目标方法
提交或者回滚事务
提交或者回滚事务
aop 日志打印 事务原理 自定义实现
代理模式实现的原理
代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如上图所示:
抽象主题角色:可以是接口,也可以是抽象类;
委托类角色:真实主题角色,业务逻辑的具体执行者;
代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。
代理模式创建方式
相关测试代码:
package com.mayikt.service;
/**
* @author 余胜军
* @ClassName OrderService
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface OrderService {
/**
* 添加订单数据
*/
String addOrder(String orderName);
}
package com.mayikt.service.impl;
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class OrderServiceImpl implements OrderService {
@Override
public String addOrder(String orderName) {
log.info("<orderName:{}>", orderName);
return "ok";
}
}
静态代理
基于接口实现方式
package com.mayikt.proxy1;
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
/**
* @author 余胜军
* @ClassName OrderServiceProxy
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Slf4j
public class OrderServiceProxy implements OrderService {
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
@Override
public String addOrder(String orderName) {
// 目标方法前后处理操作
log.info("<目标方法之前执行...>");
String result = orderService.addOrder(orderName);
log.info("<目标方法之后执行...>");
return result;
}
}
package com.mayikt.proxy1;
import com.mayikt.service.impl.OrderServiceImpl;
/**
* @author 余胜军
* @ClassName Test01
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class Test01 {
public static void main(String[] args) {
OrderServiceProxy orderServiceProxy = new OrderServiceProxy(new OrderServiceImpl());
String result = orderServiceProxy.addOrder("mayikt");
System.out.println(result);
}
}
基于继承实现方式
package com.mayikt.proxy2;
import com.mayikt.service.impl.OrderServiceImpl;
import lombok.extern.slf4j.Slf4j;
/**
* @author 余胜军
* @ClassName OrderServiceProxy
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Slf4j
public class OrderServiceProxy extends OrderServiceImpl {
@Override
public String addOrder(String orderName) {
// 目标方法前后处理操作
log.info("<目标方法之前执行...>");
String result = super.addOrder(orderName);
log.info("<目标方法之后执行...>");
return result;
}
}
package com.mayikt.proxy2;
/**
* @author 余胜军
* @ClassName Test01
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class Test01 {
public static void main(String[] args) {
OrderServiceProxy orderServiceProxy = new OrderServiceProxy();
String result = orderServiceProxy.addOrder("mayikt");
System.out.println(result);
}
}
动态代理
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成 。
JDK动态代理的一般步骤如下:
1.创建被代理的接口和类;
2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;
3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;
实现原理:利用拦截器机制必须实现InvocationHandler接口中的invoke方法实现对
我们的目标方法增强。
JDK API动态代理用法
package com.mayikt.proxy3;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author 余胜军
* @ClassName JdkInvocationHandler
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Slf4j
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目标对象
*/
private Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("<jdk动态代理目标方法之前>,args:{}", args);
Object result = method.invoke(target, args);
log.info("<jdk动态代理目标方法之后,args:{}", args);
return result;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
package com.mayikt.proxy3;
import com.mayikt.service.OrderService;
import com.mayikt.service.impl.OrderServiceImpl;
/**
* @author 余胜军
* @ClassName Test01
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class Test01 {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService orderService = jdkInvocationHandler.getProxy();
orderService.addOrder("mayikt");
}
}
动态代理与静态代理的区别
动态代理不需要写代理类对象,通过程序自动生成,而静态代理需要我们自己写代理类对象。
JDK动态代理原理分析
1. 获取代理的生成的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.mayikt.service.OrderService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements OrderService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String addOrder(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.mayikt.service.OrderService").getMethod("addOrder", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
public static void main(String[] args) {
$Proxy0 $Proxy0 = new $Proxy0(new JdkInvocationHandler(new OrderServiceImpl()));
$Proxy0.addOrder("mayikt");
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
this);
生成代理类时 会传递我们目标对象实现哪些接口
target.getClass().getInterfaces()
如果我们目标对象没有实现接口 jdk动态代理生成代理类 也没有实现接口
使用jdk动态代理时 注意 让目标对象实现接口, 生成代理类时 实现目标对象的接口
方便可以使用接口调用目标对象的方法
1.执行到我们 代理类中$Proxy0.addOrder()
2.执行到我们MayiktInvocationHandler.invoke
public final String addOrder(String var1) {
try {
return (String) super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
3.执行到我们MayiktInvocationHandler.invoke 在根据java反射机制 传递目标对象 调用目标方法
mybatis mapper接口分析
在mybatis mapper 是一个接口为何可以调用的呢?底层其实就是基于JDK动态代理实现。
相关代码:
package com.mayikt.mybatis.ext;
import org.springframework.stereotype.Indexed;
import java.lang.annotation.*;
/**
* @author 余胜军
* @ClassName MayiktInsert
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface MayiktInsert {
String value();
}
package com.mayikt.mybatis;
import com.mayikt.mybatis.ext.MayiktInsert;
import com.mayikt.utils.MayiktJdbcUtils;
import org.apache.commons.lang.StringUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
/**
* @author 余胜军
* @ClassName MybatisJdkInvocationHandler
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class MybatisJdkInvocationHandler implements InvocationHandler {
private Class mapperClass;
public MybatisJdkInvocationHandler(Class mapperClass) {
this.mapperClass = mapperClass;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 使用java反射技术获取该方法上的注解
MayiktInsert declaredAnnotation = method.getDeclaredAnnotation(MayiktInsert.class);
String insertSql = declaredAnnotation.value();
if (StringUtils.isEmpty(insertSql)) {
return null;
}
// 执行该sql语句
Connection connection = MayiktJdbcUtils.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(insertSql);
int result = preparedStatement.executeUpdate();
return result;
}
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(mapperClass.getClassLoader(), new Class[]{mapperClass}, this);
}
}
package com.mayikt.mybatis;
import com.mayikt.mybatis.ext.MayiktInsert;
/**
* @author 余胜军
* @ClassName UserMapper
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface UserMapper {
@MayiktInsert("INSERT INTO `mayikt`.`mayikt_users` (`id`, `name`, `age`) VALUES (null, 'wangmazi', NULL);")
int addUser();
}
package com.mayikt.mybatis;
/**
* @author 余胜军
* @ClassName Test01
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class Test01 {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
int result = userMapper.addUser();
System.out.println(result);
}
}
package com.mayikt.mybatis;
/**
* @author 余胜军
* @ClassName MapperProxy
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public class MapperProxy {
public static UserMapper getUserMapper(Class mapperClass) {
return new MybatisInvocationHandler(mapperClass).getProxy();
}
public static void main(String[] args) {
// 将jdk动态生成好的 class 存放本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
UserMapper userMapper = MapperProxy.getUserMapper(UserMapper.class);
int i = userMapper.addUser();
System.out.println(i);
}
}
什么是CGLIB 动态代理
Cglib 底层基于asm实现
Cglib 与jdk动态代理到底有哪些区别呢?
jdk动态代理底层基于反射方式调用目标方法 jdk7之前效率是非常低后期优化。
Cglib 底层是反射调用目标方法效率非常低 ,直接采用建立fastclass 索引的方式
调用目标方法。
jdk7之前Cglib动态代理效率是比我们jdk动态代理效率高非常多
jdk7开始jdk动态代理是我们Cglib动态代理效率高。
Cglib动态代理生成的代理类 直接 继承 被代理类(实现继承方式代理)
public class OrderServiceImpl$$EnhancerByCGLIB$$1dd3a71c extends OrderServiceImpl implements Factory {
jdk动态代理生成代理类 实现 被代理实现的接口 (实现接口方式代理)
Cglib底层生成代理类是通过 asm 生成字节码 class
1.Cglib是一个强大的,高性能,高质量的代码生成类库。它可以在运行期扩展JAVA类与实现JAVA接口。其底层实现是通过ASM字节码处理框架来转换字节码并生成新的类。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。
2.运行时动态的生成一个被代理类的子类(通过ASM字节码处理框架实现),子类重写了被代理类中所有非final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。
3.jdk7开始 jdk动态代理效率比cglib要高
spring底层采用 cglib代理类?还是jdk动态代理
判断 被代理类 实现接口 使用jdk动态代理
如果被代理类 没有实现接口则使用cglib代理类
cglib基本用法
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
</dependencies>
实现MethodInterceptor接口的intercept方法后,所有生成的代理方法都调用这个方法。
intercept方法的具体参数有
obj 目标类的实例
1. method 目标方法实例(通过反射获取的目标方法实例)
2. args 目标方法的参数
3. proxy 代理类的实例
该方法的返回值就是目标方法的返回值。
package com.mayikt.service;
/**
* @author 余胜军
* @ClassName OrderService
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
public interface OrderService {
/**
* 添加订单数据
*/
String addOrder(String orderName);
}
package com.mayikt.service.impl;
import com.mayikt.service.OrderService;
import lombok.extern.slf4j.Slf4j;
/**
* @author 余胜军
* @ClassName OrderServiceImpl
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Slf4j
public class OrderServiceImpl implements OrderService {
// @Override
public String addOrder(String orderName) {
log.info("<orderName:{}>", orderName);
// addorder相关的事情
return "ok";
}
}
package com.mayikt.cglib;
import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author 余胜军
* @ClassName MayiktCglibMethodInterceptor
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Slf4j
public class MayiktCglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
log.info("<目标方法之前开始执行....>");
// Object result = method.invoke(obj, args);
Object result = proxy.invokeSuper(obj, args);
log.info("<目标方法之后开始执行....>");
return result;
}
}
package com.mayikt.cglib;
import com.mayikt.service.impl.OrderServiceImpl;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Test01 {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code\\cglib");
MayiktCglibMethodInterceptor mayiktCglibMethodInterceptor = new MayiktCglibMethodInterceptor();
Enhancer enhancer = new Enhancer();
// 设置代理
enhancer.setSuperclass(OrderServiceImpl.class);
// 设置cglib 回调类
enhancer.setCallback(mayiktCglibMethodInterceptor);
// 创建cglib代理类
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
String result = orderServiceImpl.addOrder("mayikt");
System.out.println(result);
}
}
AOP详解
Aop常用术语
1.连接点(Join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。类中的哪些方法可以被增强,这些方法就被称作为连接点。
2.切点(PointCut): 可以插入增强处理的连接点,实际被增强的方法就称作为切入点
3.通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理, 实际增强的业务逻辑,该过程就可以称作为通知 前置、后置、环绕通知
4.切面(Aspect): 切面是通知和切点的结合。 把通知应用到的过程 就是为切面
5.引入(Introduction):允许我们向现有的类添加新的方法或者属性。
6.织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的代理对象
1.连接点 该类中哪些方法需要被增强,这些方法就可以称作连接点
3.切点 实际被增强的方法
2.通知 在方法前后执行代码
前置通知 调用方法之前处理...
后置通知 调用完该方法之后处理
环绕通知 在我们被代理方法前后执行
异常通知
最终通知
4.切面 把通知应用到的过程 就是为切面
Aop环境准备
1.Spring框架一般都是基于AspectJ实现AOP操作
(1)什么是AspectJ
AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法,它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件,AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作.
2.基于AspectJ实现AOP
(1)基于xml配置文件实现
(2)基于注解方式(偏多的)
3.在项目工程目录引入AOP依赖
maven依赖
<!-- aspectj支持 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.bundles</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8_2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.1.RELEASE</version>
</dependency>
切入点表达式
具体那个类中的那个方法来实现增强
需要描述 该类中哪些方法是需要被增强-----切入点规则
execution( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]));
[权限修饰符
1.public.String.com.mayikt.service.MayiktService.addUser(..) --拦截的是
MayiktService类中addUser方法名称 所有参数 返回值String
2.* com.mayikt.service.MayiktService.*(..)拦截我们的
MayiktService类中的所有方法
3.* com.mayikt.service.*.*(..)拦截就是我们 com.mayikt.service.包下
的所有的类所有的方法。
(1)语法接口:
execution( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]));
//举例1:对com.mayikt.service.MayiktService类里面的 add() 进行增强 execution(*com.mayikt.service.MayiktService.add(..)); // * 表示所有, .. 表示参数列表
//举例2:对com.mayikt.service.MayiktService类里面的 所有方法 进行增强 execution(*com.mayikt.service.MayiktService.*(..));
//举例3:对com.mayikt.service.MayiktService所有类里面的 所有方法 进行增强 execution(*com.mayikt.service.MayiktService.*.*(..));
切入点表达式
测试代码
package com.mayikt.service;
import org.springframework.stereotype.Component;
/**
* @author 余胜军
* @ClassName MayiktService
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
public class MayiktService {
public String addMayikt() {
System.out.println("addMayikt...");
return "ok";
}
}
package com.mayikt.proxy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author 余胜军
* @ClassName UserProxy
* @qq 644064779
* @addres www.mayikt.com
* 微信:yushengjun644
*/
@Component
@Aspect// aop 代理
public class UserProxy {
/**
* 前置通知
*/
@Before("execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void before() {
System.out.println("前置通知...");
}
/**
* 后置通知
*/
@After("execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void after() {
System.out.println("后通知...");
}
/**
* 环绕通知
*/
@Around(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知...");
System.out.println("目标方法之前开始执行...");
Object result = proceedingJoinPoint.proceed();
System.out.println("目标方法之后开始执行...");
return result;
}
//@AfterReturning表达后置通知/返回通知,表达方法返回结果之后执行
@AfterReturning(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void afterReturning() {
System.out.println("afterReturning");
}
//@AfterThrowing表达异常通知
@AfterThrowing(value = "execution(* com.mayikt.service.MayiktService.addMayikt(..));")
public void afterThrowing() {
System.out.println("afterThrowing");
}
}
开启springaop
<?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.mayikt"></context:component-scan>
<!--开启 aspectj 生成代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
ClassPathXmlApplicationContext app =
new ClassPathXmlApplicationContext("spring_07.xml");
MayiktService mayiktService = app.getBean("mayiktService", MayiktService.class);
mayiktService.addMayikt();
spring框架种使用 cglib?jdk动态代理?
spring aop 底层基于 代理封装?
如果我们 被代理类 没有实现接口的情况下 则使用 cglib动态代理
如果我们被代理类 有实现接口的情况下 则使用 jdk动态代理
Aop实现统一日志输出
aop实现统一日志输出