3、AOP
AOP(概念)
AOP(底层原理)
AOP(JDK 动态代理)
AOP(CGLIB动态代理)
AOP 的开发中的相关术语
AOP 操作(AspectJ 注解)
AOP 操作(xml 方式)
4、JdbcTemplate
5、事务管理
6、Spring5 新特性
AOP
AOP(概念)
1、什么是 AOP
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
AOP(底层原理)
1、AOP 底层使用动态代理
(1)有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理
创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用 CGLIB 动态代理
创建子类的代理对象,增强类的方法
AOP(JDK 动态代理)
1、使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
(1)调用 newProxyInstance 方法
方法有三个参数: 第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
2、编写 JDK 动态代理代码
(1)创建接口,定义方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
(2)创建接口实现类,实现方法
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
(3)使用 Proxy 类创建接口代理对象
第一种实现方式,实现InvocationHandler接口
public class MyJDKProxy implements InvocationHandler {
private UserDao userDao;
public MyJDKProxy(UserDao userDao) {
this.userDao = userDao;
}
// 编写工具方法:生成代理:
public UserDao getUserDaoImplProxy(){
UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
this);
return userDaoProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验================");
}
return method.invoke(userDao, args);
}
}
第二种实现方式,使用匿名内部类
public class GetUserDaoProxy {
// 编写工具方法:生成代理:
public static UserDao getUserDaoImplProxy(UserDao userDao){
UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("save".equals(method.getName())){
System.out.println("权限校验================");
}
return method.invoke(userDao, args);
}
});
return userDaoProxy;
}
}
(3)编写测试类
@Test
void testJDKProxy() {
System.out.println("增强前------------------");
UserDao userDao = new UserDaoImpl();
userDao.add();
userDao.delete();
userDao.save();
userDao.update();
System.out.println("\n增强后------------------");
UserDao userDaoProxy = UserDaoProxy.getUserDaoImplProxy(userDao);
userDaoProxy.add();
userDaoProxy.delete();
userDaoProxy.save();
userDaoProxy.update();
}
(4)运行结果
AOP(Cglib 动态代理)
1、编写 Cglib动态代理代码
(1)创建CustomerDao类
public class CustomerDao {
public void save(){
System.out.println("保存----------");
}
public void add(){
System.out.println("增加----------");
}
public void delete(){
System.out.println("删除----------");
}
public void update(){
System.out.println("更新----------");
}
}
(2)创建CustomerDao代理类,实现MethodInterceptor接口
public class MyCglibProxy implements MethodInterceptor{
private CustomerDao customerDao;
public MyCglibProxy(CustomerDao customerDao){
this.customerDao = customerDao;
}
// 生成代理的方法:
public CustomerDao createProxy(){
// 创建 Cglib 的核心类:
Enhancer enhancer = new Enhancer();
// 设置父类:
enhancer.setSuperclass(CustomerDao.class);
// 设置回调:
enhancer.setCallback(this);
// 生成代理:
CustomerDao customerDaoProxy = (CustomerDao) enhancer.create();
return customerDaoProxy;
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
methodProxy) throws Throwable {
if("delete".equals(method.getName())){
Object obj = methodProxy.invokeSuper(proxy, args);
System.out.println("日志记录================");
return obj;
}
return methodProxy.invokeSuper(proxy, args);
}
}
(3)编写测试类
@Test
void testCglibProxy() {
System.out.println("增强前------------------");
CustomerDao customerDao = new CustomerDao();
customerDao.add();
customerDao.delete();
customerDao.save();
customerDao.update();
System.out.println("\n增强后------------------");
CustomerDao customerDaoProxy = new CustomerDaoCglibProxy(customerDao).createProxy();
customerDaoProxy.add();
customerDaoProxy.delete();
customerDaoProxy.save();
customerDaoProxy.update();
}
(4)运行结果
AOP 的开发中的相关术语
Joinpoint(连接点): 所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为spring 只支持方法类型的连接点.
Pointcut(切入点): 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field.
Target(目标对象):代理的目标对象
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程.spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入
Proxy(代理): 一个类被 AOP 织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
切入点表达式
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.zsm.dao.BookDao 类里面的 add 进行增强
execution(* com.zsm.dao.BookDao.add(…))
举例 2:对 com.zsm.dao.BookDao 类里面的所有的方法进行增强
execution(* com.zsm.dao.BookDao.* (…))
AOP 操作(AspectJ 注解)
1、创建目标类ProductDao,在类里面定义方法
@Component
public class ProductDao{
public void save() {
System.out.println("ProductDaoImpl 中的save方法执行了。。。");
}
public String delete() {
System.out.println("ProductDaoImpl 中的delete方法执行了。。。");
return "李白";
}
public void update() {
System.out.println("ProductDaoImpl 中的update方法执行了。。。");
}
public void find() {
System.out.println("ProductDaoImpl 中的find方法执行了。。。");
int i = 1/0;
}
}
2、创建切面类,在类里面定义要增强的功能
@Component
@Aspect
public class ProductDaoAspectJ{
// 前置通知
@Before(value = "execution(* com.zsm.aop.dao.ProductDao.save(..))")
public void checkPri() {
System.out.println("权限校验===========>-------前置通知");
}
// 后置通知(返回通知)
@AfterReturning(value = "execution(* com.zsm.aop.dao.ProductDao.delete(..))",returning = "ex")
public void afterReturning(Object ex) {
System.out.println("日志记录===========>-------后置通知++++++++>" + ex);
}
// 环绕通知
@Around(value = "pointcut1()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("日志记录===========>-------环绕通知前");
Object object = joinPoint.proceed();
System.out.println("日志记录===========>-------环绕通知后");
return object;
}
// 异常通知 在被增强方法中出现异常的时候执行
@AfterThrowing(value = "execution(* com.zsm.aop.dao.ProductDao.find(..))", throwing = "ex")
public void name(Throwable ex) {
System.out.println("异常通知=============>" + ex.getMessage());
}
// 最终通知
// @After(value = "execution(* com.zsm.aop.dao.ProductDao.find(..))")
// public void after() {
// System.out.println("最终通知=============>");
// }
// 最终通知
@After(value = "ProductDaoAspectJ.pointcut()")
public void after() {
System.out.println("最终通知=============>");
}
// 相同的切入点抽取
@Pointcut(value = "execution(* com.zsm.aop.dao.ProductDao.find(..))")
private void pointcut() {}
@Pointcut(value = "execution(* com.zsm.aop.dao.ProductDao.update(..))")
private void pointcut1() {}
}
3、编写测试类
@Resource
private ProductDao productDao;
@Test
void testAspectJ() {
productDao.save();
productDao.delete();
productDao.update();
productDao.find();
}
4、运行结果
5、有多个增强类对同一个方法进行增强,设置增强类优先级
(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class ProductDaoAspectJ
AOP 操作(xml 方式)
1、创建目标类OrderDao,在类里面定义方法
public class OrderDao {
public void save() {
System.out.println("保存订单...");
}
public void update() {
System.out.println("修改订单...");
}
public void delete() {
System.out.println("删除订单...");
}
public void find() {
System.out.println("查询订单...");
}
}
2、创建切面类,在类里面定义要增强的功能
public class MyAspectXml {
// 前置通知
public void before(){
System.out.println("前置通知===========");
}
// 后置通知
public void afterReturing(){
System.out.println("后置通知===========");
}
// 环绕通知
public void around(){
System.out.println("环绕通知===========");
}
// 异常通知
public void afterThrowing(){
System.out.println("异常通知===========");
}
// 最终通知
public void after(){
System.out.println("最终通知===========");
}
}
3、application.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置切面类 -->
<bean id="myAspectXml" class="com.zsm.aop.xml.MyAspectXml"></bean>
<!-- 进行 aop 的配置 -->
<aop:config>
<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* com.zsm.aop.dao.*Dao.save(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* com.zsm.aop.dao.*Dao.delete(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* com.zsm.aop.dao.*Dao.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* com.zsm.aop.dao.*Dao.find(..))" id="pointcut4"/>
<!-- 配置切面 -->
<aop:aspect ref="myAspectXml">
<aop:before method="before" pointcut-ref="pointcut1"/>
<aop:after-returning method="afterReturing" pointcut-ref="pointcut2"/>
<aop:around method="around" pointcut-ref="pointcut3"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4"/>
<aop:after method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
</beans>