1.动态代理

动态代理:在程序运行期间由JVM通过反射等机制动态的生成,代理对象和真实对象的关系是在程序运行时期才确定的。

Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理。

通过实现接口的方式 -> JDK动态代理、通过继承类的方式 -> CGLIB动态代理。

JDK动态代理

JDK动态代理主要涉及:java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

动态代理的最小单位是类(所有类中的方法都会被处理) ,如果只想拦截一部分方法可以在invoke方法中对要执行的方法名进行判断

image.png

  1. package com.lymn.test;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. interface LoginService {
  6. public boolean checkUser();
  7. }
  8. class LoginServiceImpl implements LoginService{
  9. @Override
  10. public boolean checkUser() {
  11. System.out.println("LoginServiceImpl checkUser");
  12. return false;
  13. }
  14. }
  15. interface UserService {
  16. public String getUserName();
  17. }
  18. class UserServiceImpl implements UserService{
  19. @Override
  20. public String getUserName() {
  21. System.out.println("UserServiceImpl getUserName");
  22. return null;
  23. }
  24. }
  25. class ProxyHandler implements InvocationHandler{
  26. private Object target;
  27. public void setTarget(Object target) {
  28. this.target = target;
  29. }
  30. @Override
  31. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  32. System.out.println("代理方法执行前...");
  33. Object retObj = method.invoke(target, args);
  34. System.out.println("代理方法执行后...");
  35. return retObj;
  36. }
  37. }
  38. public class ProxyTest {
  39. public static void main(String[] args) {
  40. //创建目标对象
  41. LoginService loninService = new LoginServiceImpl();
  42. UserService userService = new UserServiceImpl();
  43. ProxyHandler proxyHandler = new ProxyHandler();
  44. //创建LoginService代理对象
  45. proxyHandler.setTarget(loninService);
  46. //强转类型:必须转为接口类型,不能转为接口的实现类
  47. LoginService loninProxy = (LoginService) Proxy.newProxyInstance(loninService.getClass().getClassLoader(),loninService.getClass().getInterfaces(), proxyHandler);
  48. loninProxy.checkUser();
  49. //创建UserService代理对象
  50. proxyHandler.setTarget(userService);
  51. UserService userProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(), proxyHandler);
  52. userProxy.getUserName();
  53. }
  54. }
  55. -------------
  56. 代理方法执行前...
  57. LoginServiceImpl checkUser
  58. 代理方法执行后...
  59. 代理方法执行前...
  60. UserServiceImpl getUserName
  61. 代理方法执行后...

CGLIB动态代理

CGLib(Code Generation Library)采用底层字节码技术,为一个类创建一个子类,在子类中采用方法拦截技术拦截所有父类方法的调用并顺势织入横切逻辑。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
package com.lymn.test;

import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

class LoginServiceImpl{
    public void checkUser() {
        System.out.println("LoginServiceImpl  checkUser");
    }
}
class UserServiceImpl {
    public void getUserName() {
        System.out.println("UserServiceImpl getUserName");
    }
}
class CglibProxy implements MethodInterceptor{
    @Override
    public Object intercept(Object proxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        //proxy cglib生成的代理对象  method 被代理对象的方法  params 传入方法的参数  methodProxy 代理的方法
        System.out.println("代理方法执行前...");
        Object retObj = methodProxy.invokeSuper(proxy, params);
        System.out.println("代理方法执行后...");
        return retObj;
    }
    //返回目标对象的代理对象
    public Object newProxy(Object target)
    {
        //通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(target.getClass());
        // 设置enhancer的回调对象
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }
}
public class CglibTest {
    public static void main(String[] args) {
        //创建目标对象
        LoginServiceImpl loninService = new LoginServiceImpl();
        UserServiceImpl userService = new UserServiceImpl();
        CglibProxy proxy = new CglibProxy();
        //创建LoginService代理对象
        LoginServiceImpl loninProxy = (LoginServiceImpl) proxy.newProxy(loninService);
        loninProxy.checkUser();
        //创建UserService代理对象
        UserServiceImpl userProxy = (UserServiceImpl) proxy.newProxy(userService);
        userProxy.getUserName();
    }
}

比较

  • 使用JDK动态代理时,目标类必须实现某个接口,如果某个类没有实现接口则不能生成代理对象
  • jdk动态代理是通过反射实现的,性能上可能稍微慢点(反射开销)
  • CGLIB可以生成委托类的子类,并重写父类非final修饰符方法
  • CGLib创建对象时花费时间比JDK动态代理多,但对象在实际运行时比JDK动态代理高
  • 无需频繁创建代理对象,比较适合采用CGLib动态代理,反之则比较适用JDK动态代理
  • 1.6和1.7时,JDK动态代理速度要比CGLib慢;JDK1.8,JDK动态代理速度已经比CGLib动态代理速度快很多了

2.AOP

面向切面编程,可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间耦合度降低,提高程序可重用性,同时提高了开发效率。

不通过修改源代码方式,在主干功能里面添加新功能。

术语

连接点:类里面哪些方法可以被增强,这些方法称为连接点

切入点:实际被真正增强的方法称为切入点

通知(增强):实际增强的逻辑部分称为通知,且分为以下五种类型:

前置通知、后置通知、环绕通知 、异常通知、最终通知

切面:把通知应用到切入点过程

操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作,AspectJ 不是 Spring 组成部分,独立 AOP 框架。

基于 AspectJ 实现 AOP 操作:基于 xml 配置文件实现 、基于注解方式实现

切入点表达式:对哪个类里面的哪个方法进行增强 
execution([权限修饰符] [返回类型] [类全路径] [方法名称]) )

对 com.atguigu.dao.BookDao 类里面的 add 进行增强
    execution(* com.atguigu.dao.BookDao.add(..))
对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
    execution(* com.atguigu.dao.BookDao.* (..))
对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
    execution(* com.atguigu.dao.*.* (..))

注解

//被增强的类
@Component
public class User {
 public void add() {
 System.out.println("add.......");
 }
}
//2、创建增强类(编写增强逻辑)
@Component
@Aspect  //生成代理对象
@Order(1)//在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
public class UserProxy {
   //相同切入点抽取
    @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void pointdemo() {

    }

    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")//相同切入点抽取使用!
    public void before() {
        System.out.println("before.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }

    //最终通知
    @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }

    //异常通知
    @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        System.out.println("环绕之后.........");
    }
}
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

Xml

<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
<!--3、在 spring 配置文件中配置切入点-->
<!--配置 aop 增强-->
<aop:config>
 <!--切入点-->
 <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
 <!--配置切面-->
 <aop:aspect ref="bookProxy">
 <!--增强作用在具体的方法上-->
 <aop:before method="before" pointcut-ref="p"/>
 </aop:aspect>
</aop:config>

3.Spring事务

事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操 作都失败。

事务四个特性(ACID) (1)原子性 (2)一致性 (3)隔离性 (4)持久性

在 Spring 进行事务管理操作 ;两种方式:编程式事务管理、声明式事务管理

声明式事务管理 (1)基于注解方式(推荐使用) (2)基于 xml 配置文件方式

xml

<!--1 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <!--注入数据源-->
 <property name="dataSource" ref="dataSource"></property>
</bean>
<!--2 配置通知-->
<tx:advice id="txadvice">
 <!--配置事务参数-->
 <tx:attributes>
 <!--指定哪种规则的方法上面添加事务-->
 <tx:method name="accountMoney" propagation="REQUIRED"/>
 <!--<tx:method name="account*"/>-->
 </tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
 <!--配置切入点-->
 <aop:pointcut id="pt" expression="execution(*
com.atguigu.spring5.service.UserService.*(..))"/>
 <!--配置切面-->
 <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

事务传播特性

事务传播行为是如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

  • PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
  • PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
  • PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
  • PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
  • PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
  • PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED

隔离性

隔离级别是指若干个并发的事务之间的隔离程度。

  • SOLATION_DEFAULT:使用底层数据库的默认隔离级别,通常值是ISOLATION_READ_COMMITTED
  • ISOLATION_READ_UNCOMMITTED:一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读
  • ISOLATION_READ_COMMITTED:一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读
  • ISOLATION_REPEATABLE_READ:一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读
  • ISOLATION_SERIALIZABLE:该级别可以防止脏读、不可重复读以及幻读

参考

Spring-AOP概念及使用教程
Spring AOP中的JDK和CGLib动态代理哪个效率更高?